mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-01 10:13:58 +08:00
platform-drivers-x86 for v5.16-1
Highlights: - AMD-PMC S0ix support fixes and improvements - HP-WMI support for Omen laptops - New nvidia-wmi-ec-backlight driver - New Intel ISH ECLITE driver - WMI core cleanups - Support for various new Melanox platforms - System76 Laptop support improvements - Surface Laptop Studio support and initial Surface Pro 8 support - Various other small fixes and hardware-id additions The following is an automated git shortlog grouped by driver: ABI: - sysfs-platform-intel-pmc: add blank lines to make it valid for ReST - sysfs-platform-dell-privacy-wmi: correct ABI entries ASoC: - Intel: Move soc_intel_is_foo() helpers to a generic header Add Intel ishtp eclite driver: - Add Intel ishtp eclite driver Add driver for ACPI WMAA EC-based backlight control: - Add driver for ACPI WMAA EC-based backlight control Documentation/ABI: - Add new line card attributes for mlxreg-io sysfs interfaces - Add new attributes for mlxreg-io sysfs interfaces HID: - surface-hid: Allow driver matching for target ID 1 devices - surface-hid: Use correct event registry for managing HID events Input: - axp20x-pek - Use new soc_intel_is_cht() helper Remove "WMAA" from identifier names in wmaa-backlight-wmi.c: - Remove "WMAA" from identifier names in wmaa-backlight-wmi.c Rename wmaa-backlight-wmi to nvidia-wmi-ec-backlight: - Rename wmaa-backlight-wmi to nvidia-wmi-ec-backlight Support for EC-connected GPIOs for identify LED/button on Barco P50 board: - Support for EC-connected GPIOs for identify LED/button on Barco P50 board acer-wmi: - use __packed instead of __attribute__((packed)) amd-pmc: - Drop check for valid alarm time - Downgrade dev_info message to dev_dbg - fix compilation without CONFIG_RTC_SYSTOHC_DEVICE - Add special handling for timer based S0i3 wakeup - adjust arguments for `amd_pmc_send_cmd` - Add alternative acpi id for PMC controller - Add a message to print resume time info - Send command to dump data after clearing OS_HINT - Fix compilation when CONFIG_DEBUGFS is disabled - Export Idlemask values based on the APU - Check s0i3 cycle status - Increase the response register timeout asus-wmi: - rename platform_profile_* function symbols barco-p50-gpio: - use KEY_VENDOR for button instead of KEY_RESTART dell: - Make DELL_WMI_PRIVACY depend on DELL_WMI - fix DELL_WMI_PRIVACY dependencies & build error dell-wmi: - Recognise or support new switches docs: - ABI: fix documentation warning in sysfs-driver-mlxreg-io gigabyte-wmi: - add support for B550 AORUS ELITE AX V2 - add support for B550I Aorus Pro AX hp-wmi: - rename platform_profile_* function symbols - add support for omen laptops ideapad-laptop: - Add platform support for Ideapad 5 Pro 16ACH6-82L5 int1092: - Fix non sequential device mode handling intel_int0002_vgpio: - Use the new soc_intel_is_byt()/_cht() helpers intel_scu_ipc: - Update timeout value in comment - Increase virtual timeout to 10s - Fix busy loop expiry time intel_skl_int3472: - Correct null check lg-laptop: - replace snprintf in show functions with sysfs_emit - Correctly handle dmi_get_system_info() returning NULL mlx-platform: - Add support for new system SGN2410 - Add BIOS attributes for CoffeeLake COMEx based systems - Extend FAN and LED configuration to support new MQM97xx systems - Add support for multiply cooling devices - Configure notifier callbacks for modular system - Add initial support for new modular system panasonic-laptop: - Replace snprintf in show functions with sysfs_emit platform: - x86: ideapad-laptop: Use ACPI_COMPANION() directly - lg-laptop: drop unneeded MODULE_ALIAS platform/mellanox: - mlxreg-lc: Add initial support for Nvidia line card devices - mlxreg-io: Extend number of hwmon attributes - mlxreg-hotplug: Extend logic for hotplug devices operations - mlxreg-io: Fix read access of n-bytes size attributes - mlxreg-io: Fix argument base in kstrtou32() call platform/surface: - aggregator_registry: Add initial support for Surface Pro 8 - aggregator_registry: Add support for Surface Laptop Studio - gpe: Add support for Surface Laptop Studio platform/x86/intel: - hid: Add DMI switches allow list - punit_ipc: Drop wrong use of ACPI_PTR() platform_data/mlxreg: - Add new field for secured access - Add new type to support modular systems sony-laptop: - replace snprintf in show functions with sysfs_emit surface: - surface3_power: Drop redundant acpi_bus_get_device() call - surface3-wmi: Use ACPI_COMPANION() directly system76_acpi: - Fix input device error handling - fix Kconfig dependencies - Add attribute group for kb_led_color - Add battery charging thresholds - Replace Fn+F2 function for OLED models - Report temperature and fan speed thinkpad_acpi: - Fix bitwise vs. logical warning - Fix coccinelle warnings - Switch to common use of attributes touchscreen_dmi: - Add info for the Viglen Connect 10 tablet - Update info for the Chuwi Hi10 Plus (CWI527) tablet - Add info for the Chuwi HiBook (CWI514) tablet update email addresses. Change all email addresses for Mark Gross to use markgross@kernel.org.: - update email addresses. Change all email addresses for Mark Gross to use markgross@kernel.org. wmi: - change notification handler type - more detailed error reporting in find_guid() - introduce helper to retrieve event data - introduce helper to determine type - introduce helper to generate method names - introduce helper to convert driver to WMI driver - simplify error handling logic - do not fail if disabling fails - improve debug messages - align arguments of functions - move variables - remove variable - use sizeof(*p) in allocation - use !p to check for NULL - use sysfs_emit() - make GUID block packed - use guid_t and guid_equal() - use bool instead of int - use BIT() macro - remove unnecessary checks - remove stray empty line - remove unnecessary casts - remove unnecessary argument - remove unnecessary variable - remove unnecessary initializations - remove unnecessary initialization - remove commas - fix checkpatch warnings - fix kernel doc -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEEuvA7XScYQRpenhd+kuxHeUQDJ9wFAmGBVW0UHGhkZWdvZWRl QHJlZGhhdC5jb20ACgkQkuxHeUQDJ9xufQgAnheynzaOChdXasbvR//mv+lyGE49 76uRA9HF9SeP430B+MTkZuYhEIiiY7lKjHi7ZY15HPY0r6wrrbJn+zDBpXFo3Scy 6CC/KUNNkwZgy1KoDC0v1SynlkHZgS4F98S1/IKkcBDQH91N0VltLFKuYYiPw2Hp APMmQUxGGxdmBlxyKOZnFK5BicNCzGL9klkU2evQmywICx3ZT3Q9jQ1YIoiw85O+ sH7Owt3jIpWVbhb6TcPupuKw4LP6hqa8z9yYLchGaJQFyr1RXTznmLAB7foKRCJ/ 48jGgjlHF2OkrLiOvT8hFMqpU52VjVUr0fBGyRjWb7dIpt5Fp1M2HLlRXA== =cpVa -----END PGP SIGNATURE----- Merge tag 'platform-drivers-x86-v5.16-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86 Pull x86 platform driver updates from Hans de Goede: "Highlights: - AMD-PMC S0ix support fixes and improvements - HP-WMI support for Omen laptops - New nvidia-wmi-ec-backlight driver - New Intel ISH ECLITE driver - WMI core cleanups - Support for various new Melanox platforms - System76 Laptop support improvements - Surface Laptop Studio support and initial Surface Pro 8 support - Various other small fixes and hardware-id additions" * tag 'platform-drivers-x86-v5.16-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (106 commits) platform/x86: system76_acpi: Fix input device error handling platform/x86: touchscreen_dmi: Add info for the Viglen Connect 10 tablet platform/surface: aggregator_registry: Add initial support for Surface Pro 8 platform/x86: mlx-platform: Add support for new system SGN2410 platform/x86: mlx-platform: Add BIOS attributes for CoffeeLake COMEx based systems platform/x86: mlx-platform: Extend FAN and LED configuration to support new MQM97xx systems platform/x86: asus-wmi: rename platform_profile_* function symbols platform/x86: hp-wmi: rename platform_profile_* function symbols platform/x86: amd-pmc: Drop check for valid alarm time platform/x86: amd-pmc: Downgrade dev_info message to dev_dbg platform/x86: amd-pmc: fix compilation without CONFIG_RTC_SYSTOHC_DEVICE platform/x86: system76_acpi: fix Kconfig dependencies platform/x86: barco-p50-gpio: use KEY_VENDOR for button instead of KEY_RESTART platform/x86: sony-laptop: replace snprintf in show functions with sysfs_emit platform/x86: lg-laptop: replace snprintf in show functions with sysfs_emit docs: ABI: fix documentation warning in sysfs-driver-mlxreg-io platform/x86: wmi: change notification handler type HID: surface-hid: Allow driver matching for target ID 1 devices HID: surface-hid: Use correct event registry for managing HID events platform/surface: aggregator_registry: Add support for Surface Laptop Studio ...
This commit is contained in:
commit
6ab1d4839a
@ -223,3 +223,247 @@ Description: These files show with which CPLD part numbers and minor
|
||||
system.
|
||||
|
||||
The files are read only.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/bios_active_image
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/bios_auth_fail
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/bios_upgrade_fail
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: The files represent BIOS statuses:
|
||||
|
||||
bios_active_image: location of current active BIOS image:
|
||||
0: Top, 1: Bottom.
|
||||
The reported value should correspond to value expected by OS
|
||||
in case of BIOS safe mode is 0. This bit is related to Intel
|
||||
top-swap feature of DualBios on the same flash.
|
||||
|
||||
bios_auth_fail: BIOS upgrade is failed because provided BIOS
|
||||
image is not signed correctly.
|
||||
|
||||
bios_upgrade_fail: BIOS upgrade is failed by some other
|
||||
reason not because authentication. For example due to
|
||||
physical SPI flash problem.
|
||||
|
||||
The files are read only.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc1_enable
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc2_enable
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc3_enable
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc4_enable
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc5_enable
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc6_enable
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc7_enable
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc8_enable
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: These files allow line cards enable state control.
|
||||
Expected behavior:
|
||||
When lc{n}_enable is written 1, related line card is released
|
||||
from the reset state, when 0 - is hold in reset state.
|
||||
|
||||
The files are read/write.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc1_pwr
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc2_pwr
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc3_pwr
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc4_pwr
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc5_pwr
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc6_pwr
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc7_pwr
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc8_pwr
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: These files switching line cards power on and off.
|
||||
Expected behavior:
|
||||
When lc{n}_pwr is written 1, related line card is powered
|
||||
on, when written 0 - powered off.
|
||||
|
||||
The files are read/write.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc1_rst_mask
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc2_rst_mask
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc3_rst_mask
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc4_rst_mask
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc5_rst_mask
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc6_rst_mask
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc7_rst_mask
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lc8_rst_mask
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: These files clear line card reset bit enforced by ASIC, when it
|
||||
sets it due to some abnormal ASIC behavior.
|
||||
Expected behavior:
|
||||
When lc{n}_rst_mask is written 1, related line card reset bit
|
||||
is cleared, when written 0 - no effect.
|
||||
|
||||
The files are write only.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/os_started
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: This file, when written 1, indicates to programmable devices
|
||||
that OS is taking control over it.
|
||||
|
||||
The file is read/write.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pm_mgmt_en
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: This file assigns power management control ownership.
|
||||
When power management control is provided by hardware, hardware
|
||||
will automatically power off one or more line previously
|
||||
powered line cards in case system power budget is getting
|
||||
insufficient. It could be in case when some of power units lost
|
||||
power good state.
|
||||
When pm_mgmt_en is written 1, power management control by
|
||||
software is enabled, 0 - power management control by hardware.
|
||||
Note that for any setting of pm_mgmt_en attribute hardware will
|
||||
not allow to power on any new line card in case system power
|
||||
budget is insufficient.
|
||||
Same in case software will try to power on several line cards
|
||||
at once - hardware will power line cards while system has
|
||||
enough power budget.
|
||||
Default is 0.
|
||||
|
||||
The file is read/write.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/psu3_on
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/psu4_on
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: These files switching power supply units on and off.
|
||||
Expected behavior:
|
||||
When psu3_on or psu4_on is written 1, related unit will be
|
||||
disconnected from the power source, when written 0 - connected.
|
||||
|
||||
The files are write only.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/shutdown_unlock
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: This file allows to unlock ASIC after thermal shutdown event.
|
||||
When system thermal shutdown is enforced by ASIC, ASIC is
|
||||
getting locked and after system boot it will not be available.
|
||||
Software can decide to unlock it by setting this attribute to
|
||||
1 and then perform system power cycle by setting pwr_cycle
|
||||
attribute to 1 (power cycle of main power domain).
|
||||
Before setting shutdown_unlock to 1 it is recommended to
|
||||
validate that system reboot cause is reset_asic_thermal or
|
||||
reset_thermal_spc_or_pciesw.
|
||||
In case shutdown_unlock is not set 1, the only way to release
|
||||
ASIC from locking - is full system power cycle through the
|
||||
external power distribution unit.
|
||||
Default is 1.
|
||||
|
||||
The file is read/write.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/cpld1_pn
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/cpld1_version
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/cpld1_version_min
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: These files show with which CPLD major and minor versions
|
||||
and part number has been burned CPLD device on line card.
|
||||
|
||||
The files are read only.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/fpga1_pn
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/fpga1_version
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/fpga1_version_min
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: These files show with which FPGA major and minor versions
|
||||
and part number has been burned FPGA device on line card.
|
||||
|
||||
The files are read only.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/vpd_wp
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: This file allow to overwrite line card VPD hardware write
|
||||
protection mode. When attribute is set 1 - write protection is
|
||||
disabled, when 0 - enabled.
|
||||
Default is 0.
|
||||
If the system is in locked-down mode writing this file will not
|
||||
be allowed.
|
||||
The purpose if this file is to allow line card VPD burning
|
||||
during production flow.
|
||||
|
||||
The file is read/write.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/reset_aux_pwr_or_ref
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/reset_dc_dc_pwr_fail
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/reset_fpga_not_done
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/reset_from_chassis
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/reset_line_card
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/reset_pwr_off_from_chassis
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: These files show the line reset cause, as following: power
|
||||
auxiliary outage or power refresh, DC-to-DC power failure, FPGA reset
|
||||
failed, line card reset failed, power off from chassis.
|
||||
Value 1 in file means this is reset cause, 0 - otherwise. Only one of
|
||||
the above causes could be 1 at the same time, representing only last
|
||||
reset cause.
|
||||
|
||||
The files are read only.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/cpld_upgrade_en
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/fpga_upgrade_en
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: These files allow CPLD and FPGA burning. Value 1 in file means burning
|
||||
is enabled, 0 - otherwise.
|
||||
If the system is in locked-down mode writing these files will
|
||||
not be allowed.
|
||||
The purpose of these files to allow line card CPLD and FPGA
|
||||
upgrade through the JTAG daisy-chain.
|
||||
|
||||
The files are read/write.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/qsfp_pwr_en
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/pwr_en
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: These files allow to power on/off all QSFP ports and whole line card.
|
||||
The attributes are set 1 for power on, 0 - for power off.
|
||||
|
||||
The files are read/write.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/agb_spi_burn_en
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/fpga_spi_burn_en
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: These files allow gearboxes and FPGA SPI flash burning.
|
||||
The attributes are set 1 to enable burning, 0 - to disable.
|
||||
If the system is in locked-down mode writing these files will
|
||||
not be allowed.
|
||||
The purpose of these files to allow line card Gearboxes and FPGA
|
||||
burning during production flow.
|
||||
|
||||
The file is read/write.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/max_power
|
||||
What: /sys/devices/platform/mlxplat/i2c_mlxcpld.*/i2c-*/i2c-*/i2c-*/*-0032/mlxreg-io.*/hwmon/hwmon*/config
|
||||
Date: October 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Vadim Pasternak <vadimp@nvidia.com>
|
||||
Description: These files provide the maximum powered required for line card
|
||||
feeding and line card configuration Id.
|
||||
|
||||
The files are read only.
|
||||
|
@ -1,55 +1,71 @@
|
||||
What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_supported_type
|
||||
Date: Apr 2021
|
||||
KernelVersion: 5.13
|
||||
Contact: "perry.yuan@dell.com>"
|
||||
Contact: "<perry.yuan@dell.com>"
|
||||
Description:
|
||||
Display which dell hardware level privacy devices are supported
|
||||
“Dell Privacy” is a set of HW, FW, and SW features to enhance
|
||||
Dell’s commitment to platform privacy for MIC, Camera, and
|
||||
ePrivacy screens.
|
||||
The supported hardware privacy devices are:
|
||||
Attributes:
|
||||
Microphone Mute:
|
||||
|
||||
Attributes:
|
||||
Microphone Mute:
|
||||
Identifies the local microphone can be muted by hardware, no applications
|
||||
is available to capture system mic sound
|
||||
|
||||
Camera Shutter:
|
||||
Camera Shutter:
|
||||
Identifies camera shutter controlled by hardware, which is a micromechanical
|
||||
shutter assembly that is built onto the camera module to block capturing images
|
||||
from outside the laptop
|
||||
|
||||
supported:
|
||||
Values:
|
||||
|
||||
supported:
|
||||
The privacy device is supported by this system
|
||||
|
||||
unsupported:
|
||||
unsupported:
|
||||
The privacy device is not supported on this system
|
||||
|
||||
For example to check which privacy devices are supported:
|
||||
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
|
||||
[Microphone Mute] [supported]
|
||||
[Camera Shutter] [supported]
|
||||
[ePrivacy Screen] [unsupported]
|
||||
# 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
|
||||
Date: Apr 2021
|
||||
KernelVersion: 5.13
|
||||
Contact: "perry.yuan@dell.com>"
|
||||
Contact: "<perry.yuan@dell.com>"
|
||||
Description:
|
||||
Allow user space to check current dell privacy device state.
|
||||
Describes the Device State class exposed by BIOS which can be
|
||||
consumed by various applications interested in knowing the Privacy
|
||||
feature capabilities
|
||||
Attributes:
|
||||
muted:
|
||||
Identifies the privacy device is turned off and cannot send stream to OS applications
|
||||
|
||||
unmuted:
|
||||
Identifies the privacy device is turned on ,audio or camera driver can get
|
||||
stream from mic and camera module to OS applications
|
||||
Attributes:
|
||||
Microphone:
|
||||
Identifies the local microphone can be muted by hardware, no applications
|
||||
is available to capture system mic sound
|
||||
|
||||
For example to check all supported current privacy device states:
|
||||
Camera Shutter:
|
||||
Identifies camera shutter controlled by hardware, which is a micromechanical
|
||||
shutter assembly that is built onto the camera module to block capturing images
|
||||
from outside the laptop
|
||||
|
||||
# cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_current_state
|
||||
[Microphone] [unmuted]
|
||||
[Camera Shutter] [unmuted]
|
||||
Values:
|
||||
muted:
|
||||
Identifies the privacy device is turned off
|
||||
and cannot send stream to OS applications
|
||||
|
||||
unmuted:
|
||||
Identifies the privacy device is turned on,
|
||||
audio or camera driver can get stream from mic
|
||||
and camera module to OS applications
|
||||
|
||||
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
|
||||
[Microphone] [unmuted]
|
||||
[Camera Shutter] [unmuted]
|
||||
|
@ -11,8 +11,10 @@ Description:
|
||||
to take effect.
|
||||
|
||||
Display global reset setting bits for PMC.
|
||||
|
||||
* bit 31 - global reset is locked
|
||||
* bit 20 - global reset is set
|
||||
|
||||
Writing bit 20 value to the etr3 will induce
|
||||
a platform "global reset" upon consequent platform reset,
|
||||
in case the register is not locked.
|
||||
|
28
MAINTAINERS
28
MAINTAINERS
@ -3229,6 +3229,12 @@ F: drivers/video/backlight/
|
||||
F: include/linux/backlight.h
|
||||
F: include/linux/pwm_backlight.h
|
||||
|
||||
BARCO P50 GPIO DRIVER
|
||||
M: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
|
||||
M: Peter Korsgaard <peter.korsgaard@barco.com>
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/barco-p50-gpio.c
|
||||
|
||||
BATMAN ADVANCED
|
||||
M: Marek Lindner <mareklindner@neomailbox.ch>
|
||||
M: Simon Wunderlich <sw@simonwunderlich.de>
|
||||
@ -6725,7 +6731,7 @@ S: Supported
|
||||
F: drivers/edac/dmc520_edac.c
|
||||
|
||||
EDAC-E752X
|
||||
M: Mark Gross <mark.gross@intel.com>
|
||||
M: Mark Gross <markgross@kernel.org>
|
||||
L: linux-edac@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/edac/e752x_edac.c
|
||||
@ -9537,6 +9543,12 @@ L: linux-crypto@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/crypto/ixp4xx_crypto.c
|
||||
|
||||
INTEL ISHTP ECLITE DRIVER
|
||||
M: Sumesh K Naduvalath <sumesh.k.naduvalath@intel.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/platform/x86/intel/ishtp_eclite.c
|
||||
|
||||
INTEL IXP4XX QMGR, NPE, ETHERNET and HSS SUPPORT
|
||||
M: Krzysztof Halasa <khalasa@piap.pl>
|
||||
S: Maintained
|
||||
@ -12067,7 +12079,7 @@ F: drivers/net/ethernet/mellanox/mlxfw/
|
||||
|
||||
MELLANOX HARDWARE PLATFORM SUPPORT
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
M: Mark Gross <mgross@linux.intel.com>
|
||||
M: Mark Gross <markgross@kernel.org>
|
||||
M: Vadim Pasternak <vadimp@nvidia.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Supported
|
||||
@ -12525,7 +12537,7 @@ F: drivers/platform/surface/surface_gpe.c
|
||||
|
||||
MICROSOFT SURFACE HARDWARE PLATFORM SUPPORT
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
M: Mark Gross <mgross@linux.intel.com>
|
||||
M: Mark Gross <markgross@kernel.org>
|
||||
M: Maximilian Luz <luzmaximilian@gmail.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
@ -13479,6 +13491,12 @@ S: Maintained
|
||||
F: drivers/video/fbdev/nvidia/
|
||||
F: drivers/video/fbdev/riva/
|
||||
|
||||
NVIDIA WMI EC BACKLIGHT DRIVER
|
||||
M: Daniel Dadap <ddadap@nvidia.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/platform/x86/nvidia-wmi-ec-backlight.c
|
||||
|
||||
NVM EXPRESS DRIVER
|
||||
M: Keith Busch <kbusch@kernel.org>
|
||||
M: Jens Axboe <axboe@fb.com>
|
||||
@ -18561,7 +18579,7 @@ S: Supported
|
||||
F: drivers/net/ethernet/tehuti/*
|
||||
|
||||
TELECOM CLOCK DRIVER FOR MCPL0010
|
||||
M: Mark Gross <mark.gross@intel.com>
|
||||
M: Mark Gross <markgross@kernel.org>
|
||||
S: Supported
|
||||
F: drivers/char/tlclk.c
|
||||
|
||||
@ -20480,7 +20498,7 @@ F: arch/x86/mm/
|
||||
|
||||
X86 PLATFORM DRIVERS
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
M: Mark Gross <mgross@linux.intel.com>
|
||||
M: Mark Gross <markgross@kernel.org>
|
||||
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
|
||||
|
@ -209,7 +209,7 @@ static int surface_hid_probe(struct ssam_device *sdev)
|
||||
|
||||
shid->notif.base.priority = 1;
|
||||
shid->notif.base.fn = ssam_hid_event_fn;
|
||||
shid->notif.event.reg = SSAM_EVENT_REGISTRY_REG;
|
||||
shid->notif.event.reg = SSAM_EVENT_REGISTRY_REG(sdev->uid.target);
|
||||
shid->notif.event.id.target_category = sdev->uid.category;
|
||||
shid->notif.event.id.instance = sdev->uid.instance;
|
||||
shid->notif.event.mask = SSAM_EVENT_MASK_STRICT;
|
||||
@ -230,7 +230,7 @@ static void surface_hid_remove(struct ssam_device *sdev)
|
||||
}
|
||||
|
||||
static const struct ssam_device_id surface_hid_match[] = {
|
||||
{ SSAM_SDEV(HID, 0x02, SSAM_ANY_IID, 0x00) },
|
||||
{ SSAM_SDEV(HID, SSAM_ANY_TID, SSAM_ANY_IID, 0x00) },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ssam, surface_hid_match);
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/axp20x.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_data/x86/soc.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
@ -255,41 +256,24 @@ static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static bool axp20x_pek_should_register_input(struct axp20x_pek *axp20x_pek,
|
||||
struct platform_device *pdev)
|
||||
static bool axp20x_pek_should_register_input(struct axp20x_pek *axp20x_pek)
|
||||
{
|
||||
unsigned long long hrv = 0;
|
||||
acpi_status status;
|
||||
|
||||
if (IS_ENABLED(CONFIG_INPUT_SOC_BUTTON_ARRAY) &&
|
||||
axp20x_pek->axp20x->variant == AXP288_ID) {
|
||||
status = acpi_evaluate_integer(ACPI_HANDLE(pdev->dev.parent),
|
||||
"_HRV", NULL, &hrv);
|
||||
if (ACPI_FAILURE(status))
|
||||
dev_err(&pdev->dev, "Failed to get PMIC hardware revision\n");
|
||||
|
||||
/*
|
||||
* On Cherry Trail platforms (hrv == 3), do not register the
|
||||
* input device if there is an "INTCFD9" or "ACPI0011" gpio
|
||||
* button ACPI device, as that handles the power button too,
|
||||
* and otherwise we end up reporting all presses twice.
|
||||
*/
|
||||
if (hrv == 3 && (acpi_dev_present("INTCFD9", NULL, -1) ||
|
||||
if (soc_intel_is_cht() &&
|
||||
(acpi_dev_present("INTCFD9", NULL, -1) ||
|
||||
acpi_dev_present("ACPI0011", NULL, -1)))
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
static bool axp20x_pek_should_register_input(struct axp20x_pek *axp20x_pek,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int axp20x_pek_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -321,7 +305,7 @@ static int axp20x_pek_probe(struct platform_device *pdev)
|
||||
axp20x_pek->irq_dbf = regmap_irq_get_virq(
|
||||
axp20x_pek->axp20x->regmap_irqc, axp20x_pek->irq_dbf);
|
||||
|
||||
if (axp20x_pek_should_register_input(axp20x_pek, pdev)) {
|
||||
if (axp20x_pek_should_register_input(axp20x_pek)) {
|
||||
error = axp20x_pek_probe_input_device(axp20x_pek, pdev);
|
||||
if (error)
|
||||
return error;
|
||||
|
@ -34,6 +34,18 @@ config MLXREG_IO
|
||||
to system resets operation, system reset causes monitoring and some
|
||||
kinds of mux selection.
|
||||
|
||||
config MLXREG_LC
|
||||
tristate "Mellanox line card platform driver support"
|
||||
depends on REGMAP
|
||||
depends on HWMON
|
||||
depends on I2C
|
||||
help
|
||||
This driver provides support for the Mellanox MSN4800-XX line cards,
|
||||
which are the part of MSN4800 Ethernet modular switch systems
|
||||
providing a high performance switching solution for Enterprise Data
|
||||
Centers (EDC) for building Ethernet based clusters, High-Performance
|
||||
Computing (HPC) and embedded environments.
|
||||
|
||||
config MLXBF_TMFIFO
|
||||
tristate "Mellanox BlueField SoC TmFifo platform driver"
|
||||
depends on ARM64
|
||||
|
@ -8,3 +8,4 @@ obj-$(CONFIG_MLXBF_PMC) += mlxbf-pmc.o
|
||||
obj-$(CONFIG_MLXBF_TMFIFO) += mlxbf-tmfifo.o
|
||||
obj-$(CONFIG_MLXREG_HOTPLUG) += mlxreg-hotplug.o
|
||||
obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o
|
||||
obj-$(CONFIG_MLXREG_LC) += mlxreg-lc.o
|
||||
|
@ -28,7 +28,7 @@
|
||||
/* ASIC good health mask. */
|
||||
#define MLXREG_HOTPLUG_GOOD_HEALTH_MASK 0x02
|
||||
|
||||
#define MLXREG_HOTPLUG_ATTRS_MAX 24
|
||||
#define MLXREG_HOTPLUG_ATTRS_MAX 128
|
||||
#define MLXREG_HOTPLUG_NOT_ASSERT 3
|
||||
|
||||
/**
|
||||
@ -89,9 +89,20 @@ mlxreg_hotplug_udev_event_send(struct kobject *kobj,
|
||||
return kobject_uevent_env(kobj, KOBJ_CHANGE, mlxreg_hotplug_udev_envp);
|
||||
}
|
||||
|
||||
static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data *priv,
|
||||
struct mlxreg_core_data *data)
|
||||
static void
|
||||
mlxreg_hotplug_pdata_export(void *pdata, void *regmap)
|
||||
{
|
||||
struct mlxreg_core_hotplug_platform_data *dev_pdata = pdata;
|
||||
|
||||
/* Export regmap to underlying device. */
|
||||
dev_pdata->regmap = regmap;
|
||||
}
|
||||
|
||||
static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data *priv,
|
||||
struct mlxreg_core_data *data,
|
||||
enum mlxreg_hotplug_kind kind)
|
||||
{
|
||||
struct i2c_board_info *brdinfo = data->hpdev.brdinfo;
|
||||
struct mlxreg_core_hotplug_platform_data *pdata;
|
||||
struct i2c_client *client;
|
||||
|
||||
@ -106,46 +117,88 @@ static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data *priv,
|
||||
return 0;
|
||||
|
||||
pdata = dev_get_platdata(&priv->pdev->dev);
|
||||
data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr +
|
||||
pdata->shift_nr);
|
||||
if (!data->hpdev.adapter) {
|
||||
dev_err(priv->dev, "Failed to get adapter for bus %d\n",
|
||||
data->hpdev.nr + pdata->shift_nr);
|
||||
return -EFAULT;
|
||||
switch (data->hpdev.action) {
|
||||
case MLXREG_HOTPLUG_DEVICE_DEFAULT_ACTION:
|
||||
data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr +
|
||||
pdata->shift_nr);
|
||||
if (!data->hpdev.adapter) {
|
||||
dev_err(priv->dev, "Failed to get adapter for bus %d\n",
|
||||
data->hpdev.nr + pdata->shift_nr);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Export platform data to underlying device. */
|
||||
if (brdinfo->platform_data)
|
||||
mlxreg_hotplug_pdata_export(brdinfo->platform_data, pdata->regmap);
|
||||
|
||||
client = i2c_new_client_device(data->hpdev.adapter,
|
||||
brdinfo);
|
||||
if (IS_ERR(client)) {
|
||||
dev_err(priv->dev, "Failed to create client %s at bus %d at addr 0x%02x\n",
|
||||
brdinfo->type, data->hpdev.nr +
|
||||
pdata->shift_nr, brdinfo->addr);
|
||||
|
||||
i2c_put_adapter(data->hpdev.adapter);
|
||||
data->hpdev.adapter = NULL;
|
||||
return PTR_ERR(client);
|
||||
}
|
||||
|
||||
data->hpdev.client = client;
|
||||
break;
|
||||
case MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION:
|
||||
/* Export platform data to underlying device. */
|
||||
if (data->hpdev.brdinfo && data->hpdev.brdinfo->platform_data)
|
||||
mlxreg_hotplug_pdata_export(data->hpdev.brdinfo->platform_data,
|
||||
pdata->regmap);
|
||||
/* Pass parent hotplug device handle to underlying device. */
|
||||
data->notifier = data->hpdev.notifier;
|
||||
data->hpdev.pdev = platform_device_register_resndata(&priv->pdev->dev,
|
||||
brdinfo->type,
|
||||
data->hpdev.nr,
|
||||
NULL, 0, data,
|
||||
sizeof(*data));
|
||||
if (IS_ERR(data->hpdev.pdev))
|
||||
return PTR_ERR(data->hpdev.pdev);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
client = i2c_new_client_device(data->hpdev.adapter,
|
||||
data->hpdev.brdinfo);
|
||||
if (IS_ERR(client)) {
|
||||
dev_err(priv->dev, "Failed to create client %s at bus %d at addr 0x%02x\n",
|
||||
data->hpdev.brdinfo->type, data->hpdev.nr +
|
||||
pdata->shift_nr, data->hpdev.brdinfo->addr);
|
||||
|
||||
i2c_put_adapter(data->hpdev.adapter);
|
||||
data->hpdev.adapter = NULL;
|
||||
return PTR_ERR(client);
|
||||
}
|
||||
|
||||
data->hpdev.client = client;
|
||||
if (data->hpdev.notifier && data->hpdev.notifier->user_handler)
|
||||
return data->hpdev.notifier->user_handler(data->hpdev.notifier->handle, kind, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mlxreg_hotplug_device_destroy(struct mlxreg_hotplug_priv_data *priv,
|
||||
struct mlxreg_core_data *data)
|
||||
struct mlxreg_core_data *data,
|
||||
enum mlxreg_hotplug_kind kind)
|
||||
{
|
||||
/* Notify user by sending hwmon uevent. */
|
||||
mlxreg_hotplug_udev_event_send(&priv->hwmon->kobj, data, false);
|
||||
if (data->hpdev.notifier && data->hpdev.notifier->user_handler)
|
||||
data->hpdev.notifier->user_handler(data->hpdev.notifier->handle, kind, 0);
|
||||
|
||||
if (data->hpdev.client) {
|
||||
i2c_unregister_device(data->hpdev.client);
|
||||
data->hpdev.client = NULL;
|
||||
}
|
||||
switch (data->hpdev.action) {
|
||||
case MLXREG_HOTPLUG_DEVICE_DEFAULT_ACTION:
|
||||
if (data->hpdev.client) {
|
||||
i2c_unregister_device(data->hpdev.client);
|
||||
data->hpdev.client = NULL;
|
||||
}
|
||||
|
||||
if (data->hpdev.adapter) {
|
||||
i2c_put_adapter(data->hpdev.adapter);
|
||||
data->hpdev.adapter = NULL;
|
||||
if (data->hpdev.adapter) {
|
||||
i2c_put_adapter(data->hpdev.adapter);
|
||||
data->hpdev.adapter = NULL;
|
||||
}
|
||||
break;
|
||||
case MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION:
|
||||
if (data->hpdev.pdev)
|
||||
platform_device_unregister(data->hpdev.pdev);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,14 +370,14 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv,
|
||||
data = item->data + bit;
|
||||
if (regval & BIT(bit)) {
|
||||
if (item->inversed)
|
||||
mlxreg_hotplug_device_destroy(priv, data);
|
||||
mlxreg_hotplug_device_destroy(priv, data, item->kind);
|
||||
else
|
||||
mlxreg_hotplug_device_create(priv, data);
|
||||
mlxreg_hotplug_device_create(priv, data, item->kind);
|
||||
} else {
|
||||
if (item->inversed)
|
||||
mlxreg_hotplug_device_create(priv, data);
|
||||
mlxreg_hotplug_device_create(priv, data, item->kind);
|
||||
else
|
||||
mlxreg_hotplug_device_destroy(priv, data);
|
||||
mlxreg_hotplug_device_destroy(priv, data, item->kind);
|
||||
}
|
||||
}
|
||||
|
||||
@ -381,7 +434,7 @@ mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data *priv,
|
||||
* ASIC is in steady state. Connect associated
|
||||
* device, if configured.
|
||||
*/
|
||||
mlxreg_hotplug_device_create(priv, data);
|
||||
mlxreg_hotplug_device_create(priv, data, item->kind);
|
||||
data->attached = true;
|
||||
}
|
||||
} else {
|
||||
@ -391,7 +444,7 @@ mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data *priv,
|
||||
* in steady state. Disconnect associated
|
||||
* device, if it has been connected.
|
||||
*/
|
||||
mlxreg_hotplug_device_destroy(priv, data);
|
||||
mlxreg_hotplug_device_destroy(priv, data, item->kind);
|
||||
data->attached = false;
|
||||
data->health_cntr = 0;
|
||||
}
|
||||
@ -630,7 +683,7 @@ static void mlxreg_hotplug_unset_irq(struct mlxreg_hotplug_priv_data *priv)
|
||||
/* Remove all the attached devices in group. */
|
||||
count = item->count;
|
||||
for (j = 0; j < count; j++, data++)
|
||||
mlxreg_hotplug_device_destroy(priv, data);
|
||||
mlxreg_hotplug_device_destroy(priv, data, item->kind);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
/* Attribute parameters. */
|
||||
#define MLXREG_IO_ATT_SIZE 10
|
||||
#define MLXREG_IO_ATT_NUM 48
|
||||
#define MLXREG_IO_ATT_NUM 96
|
||||
|
||||
/**
|
||||
* struct mlxreg_io_priv_data - driver's private data:
|
||||
|
906
drivers/platform/mellanox/mlxreg-lc.c
Normal file
906
drivers/platform/mellanox/mlxreg-lc.c
Normal file
@ -0,0 +1,906 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Nvidia line card driver
|
||||
*
|
||||
* Copyright (C) 2020 Nvidia Technologies Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_data/mlxcpld.h>
|
||||
#include <linux/platform_data/mlxreg.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* I2C bus IO offsets */
|
||||
#define MLXREG_LC_REG_CPLD1_VER_OFFSET 0x2500
|
||||
#define MLXREG_LC_REG_FPGA1_VER_OFFSET 0x2501
|
||||
#define MLXREG_LC_REG_CPLD1_PN_OFFSET 0x2504
|
||||
#define MLXREG_LC_REG_FPGA1_PN_OFFSET 0x2506
|
||||
#define MLXREG_LC_REG_RESET_CAUSE_OFFSET 0x251d
|
||||
#define MLXREG_LC_REG_LED1_OFFSET 0x2520
|
||||
#define MLXREG_LC_REG_GP0_OFFSET 0x252e
|
||||
#define MLXREG_LC_REG_FIELD_UPGRADE 0x2534
|
||||
#define MLXREG_LC_CHANNEL_I2C_REG 0x25dc
|
||||
#define MLXREG_LC_REG_CPLD1_MVER_OFFSET 0x25de
|
||||
#define MLXREG_LC_REG_FPGA1_MVER_OFFSET 0x25df
|
||||
#define MLXREG_LC_REG_MAX_POWER_OFFSET 0x25f1
|
||||
#define MLXREG_LC_REG_CONFIG_OFFSET 0x25fb
|
||||
#define MLXREG_LC_REG_MAX 0x3fff
|
||||
|
||||
/**
|
||||
* enum mlxreg_lc_type - line cards types
|
||||
*
|
||||
* @MLXREG_LC_SN4800_C16: 100GbE line card with 16 QSFP28 ports;
|
||||
*/
|
||||
enum mlxreg_lc_type {
|
||||
MLXREG_LC_SN4800_C16 = 0x0000,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum mlxreg_lc_state - line cards state
|
||||
*
|
||||
* @MLXREG_LC_INITIALIZED: line card is initialized;
|
||||
* @MLXREG_LC_POWERED: line card is powered;
|
||||
* @MLXREG_LC_SYNCED: line card is synchronized between hardware and firmware;
|
||||
*/
|
||||
enum mlxreg_lc_state {
|
||||
MLXREG_LC_INITIALIZED = BIT(0),
|
||||
MLXREG_LC_POWERED = BIT(1),
|
||||
MLXREG_LC_SYNCED = BIT(2),
|
||||
};
|
||||
|
||||
#define MLXREG_LC_CONFIGURED (MLXREG_LC_INITIALIZED | MLXREG_LC_POWERED | MLXREG_LC_SYNCED)
|
||||
|
||||
/* mlxreg_lc - device private data
|
||||
* @dev: platform device;
|
||||
* @lock: line card lock;
|
||||
* @par_regmap: parent device regmap handle;
|
||||
* @data: pltaform core data;
|
||||
* @io_data: register access platform data;
|
||||
* @led_data: LED platform data ;
|
||||
* @mux_data: MUX platform data;
|
||||
* @led: LED device;
|
||||
* @io_regs: register access device;
|
||||
* @mux_brdinfo: mux configuration;
|
||||
* @mux: mux devices;
|
||||
* @aux_devs: I2C devices feeding by auxiliary power;
|
||||
* @aux_devs_num: number of I2C devices feeding by auxiliary power;
|
||||
* @main_devs: I2C devices feeding by main power;
|
||||
* @main_devs_num: number of I2C devices feeding by main power;
|
||||
* @state: line card state;
|
||||
*/
|
||||
struct mlxreg_lc {
|
||||
struct device *dev;
|
||||
struct mutex lock; /* line card access lock */
|
||||
void *par_regmap;
|
||||
struct mlxreg_core_data *data;
|
||||
struct mlxreg_core_platform_data *io_data;
|
||||
struct mlxreg_core_platform_data *led_data;
|
||||
struct mlxcpld_mux_plat_data *mux_data;
|
||||
struct platform_device *led;
|
||||
struct platform_device *io_regs;
|
||||
struct i2c_board_info *mux_brdinfo;
|
||||
struct platform_device *mux;
|
||||
struct mlxreg_hotplug_device *aux_devs;
|
||||
int aux_devs_num;
|
||||
struct mlxreg_hotplug_device *main_devs;
|
||||
int main_devs_num;
|
||||
enum mlxreg_lc_state state;
|
||||
};
|
||||
|
||||
static bool mlxreg_lc_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case MLXREG_LC_REG_LED1_OFFSET:
|
||||
case MLXREG_LC_REG_GP0_OFFSET:
|
||||
case MLXREG_LC_REG_FIELD_UPGRADE:
|
||||
case MLXREG_LC_CHANNEL_I2C_REG:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool mlxreg_lc_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case MLXREG_LC_REG_CPLD1_VER_OFFSET:
|
||||
case MLXREG_LC_REG_FPGA1_VER_OFFSET:
|
||||
case MLXREG_LC_REG_CPLD1_PN_OFFSET:
|
||||
case MLXREG_LC_REG_FPGA1_PN_OFFSET:
|
||||
case MLXREG_LC_REG_RESET_CAUSE_OFFSET:
|
||||
case MLXREG_LC_REG_LED1_OFFSET:
|
||||
case MLXREG_LC_REG_GP0_OFFSET:
|
||||
case MLXREG_LC_REG_FIELD_UPGRADE:
|
||||
case MLXREG_LC_CHANNEL_I2C_REG:
|
||||
case MLXREG_LC_REG_CPLD1_MVER_OFFSET:
|
||||
case MLXREG_LC_REG_FPGA1_MVER_OFFSET:
|
||||
case MLXREG_LC_REG_MAX_POWER_OFFSET:
|
||||
case MLXREG_LC_REG_CONFIG_OFFSET:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool mlxreg_lc_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case MLXREG_LC_REG_CPLD1_VER_OFFSET:
|
||||
case MLXREG_LC_REG_FPGA1_VER_OFFSET:
|
||||
case MLXREG_LC_REG_CPLD1_PN_OFFSET:
|
||||
case MLXREG_LC_REG_FPGA1_PN_OFFSET:
|
||||
case MLXREG_LC_REG_RESET_CAUSE_OFFSET:
|
||||
case MLXREG_LC_REG_LED1_OFFSET:
|
||||
case MLXREG_LC_REG_GP0_OFFSET:
|
||||
case MLXREG_LC_REG_FIELD_UPGRADE:
|
||||
case MLXREG_LC_CHANNEL_I2C_REG:
|
||||
case MLXREG_LC_REG_CPLD1_MVER_OFFSET:
|
||||
case MLXREG_LC_REG_FPGA1_MVER_OFFSET:
|
||||
case MLXREG_LC_REG_MAX_POWER_OFFSET:
|
||||
case MLXREG_LC_REG_CONFIG_OFFSET:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct reg_default mlxreg_lc_regmap_default[] = {
|
||||
{ MLXREG_LC_CHANNEL_I2C_REG, 0x00 },
|
||||
};
|
||||
|
||||
/* Configuration for the register map of a device with 2 bytes address space. */
|
||||
static const struct regmap_config mlxreg_lc_regmap_conf = {
|
||||
.reg_bits = 16,
|
||||
.val_bits = 8,
|
||||
.max_register = MLXREG_LC_REG_MAX,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
.writeable_reg = mlxreg_lc_writeable_reg,
|
||||
.readable_reg = mlxreg_lc_readable_reg,
|
||||
.volatile_reg = mlxreg_lc_volatile_reg,
|
||||
.reg_defaults = mlxreg_lc_regmap_default,
|
||||
.num_reg_defaults = ARRAY_SIZE(mlxreg_lc_regmap_default),
|
||||
};
|
||||
|
||||
/* Default channels vector.
|
||||
* It contains only the channels, which physically connected to the devices,
|
||||
* empty channels are skipped.
|
||||
*/
|
||||
static int mlxreg_lc_chan[] = {
|
||||
0x04, 0x05, 0x06, 0x07, 0x08, 0x10, 0x20, 0x21, 0x22, 0x23, 0x40, 0x41,
|
||||
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
|
||||
0x4e, 0x4f
|
||||
};
|
||||
|
||||
/* Defaul mux configuration. */
|
||||
static struct mlxcpld_mux_plat_data mlxreg_lc_mux_data[] = {
|
||||
{
|
||||
.chan_ids = mlxreg_lc_chan,
|
||||
.num_adaps = ARRAY_SIZE(mlxreg_lc_chan),
|
||||
.sel_reg_addr = MLXREG_LC_CHANNEL_I2C_REG,
|
||||
.reg_size = 2,
|
||||
},
|
||||
};
|
||||
|
||||
/* Defaul mux board info. */
|
||||
static struct i2c_board_info mlxreg_lc_mux_brdinfo = {
|
||||
I2C_BOARD_INFO("i2c-mux-mlxcpld", 0x32),
|
||||
};
|
||||
|
||||
/* Line card default auxiliary power static devices. */
|
||||
static struct i2c_board_info mlxreg_lc_aux_pwr_devices[] = {
|
||||
{
|
||||
I2C_BOARD_INFO("24c32", 0x51),
|
||||
},
|
||||
{
|
||||
I2C_BOARD_INFO("24c32", 0x51),
|
||||
},
|
||||
};
|
||||
|
||||
/* Line card default auxiliary power board info. */
|
||||
static struct mlxreg_hotplug_device mlxreg_lc_aux_pwr_brdinfo[] = {
|
||||
{
|
||||
.brdinfo = &mlxreg_lc_aux_pwr_devices[0],
|
||||
.nr = 3,
|
||||
},
|
||||
{
|
||||
.brdinfo = &mlxreg_lc_aux_pwr_devices[1],
|
||||
.nr = 4,
|
||||
},
|
||||
};
|
||||
|
||||
/* Line card default main power static devices. */
|
||||
static struct i2c_board_info mlxreg_lc_main_pwr_devices[] = {
|
||||
{
|
||||
I2C_BOARD_INFO("mp2975", 0x62),
|
||||
},
|
||||
{
|
||||
I2C_BOARD_INFO("mp2975", 0x64),
|
||||
},
|
||||
{
|
||||
I2C_BOARD_INFO("max11603", 0x6d),
|
||||
},
|
||||
{
|
||||
I2C_BOARD_INFO("lm25066", 0x15),
|
||||
},
|
||||
};
|
||||
|
||||
/* Line card default main power board info. */
|
||||
static struct mlxreg_hotplug_device mlxreg_lc_main_pwr_brdinfo[] = {
|
||||
{
|
||||
.brdinfo = &mlxreg_lc_main_pwr_devices[0],
|
||||
.nr = 0,
|
||||
},
|
||||
{
|
||||
.brdinfo = &mlxreg_lc_main_pwr_devices[1],
|
||||
.nr = 0,
|
||||
},
|
||||
{
|
||||
.brdinfo = &mlxreg_lc_main_pwr_devices[2],
|
||||
.nr = 1,
|
||||
},
|
||||
{
|
||||
.brdinfo = &mlxreg_lc_main_pwr_devices[3],
|
||||
.nr = 2,
|
||||
},
|
||||
};
|
||||
|
||||
/* LED default data. */
|
||||
static struct mlxreg_core_data mlxreg_lc_led_data[] = {
|
||||
{
|
||||
.label = "status:green",
|
||||
.reg = MLXREG_LC_REG_LED1_OFFSET,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
{
|
||||
.label = "status:orange",
|
||||
.reg = MLXREG_LC_REG_LED1_OFFSET,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
};
|
||||
|
||||
static struct mlxreg_core_platform_data mlxreg_lc_led = {
|
||||
.identity = "pci",
|
||||
.data = mlxreg_lc_led_data,
|
||||
.counter = ARRAY_SIZE(mlxreg_lc_led_data),
|
||||
};
|
||||
|
||||
/* Default register access data. */
|
||||
static struct mlxreg_core_data mlxreg_lc_io_data[] = {
|
||||
{
|
||||
.label = "cpld1_version",
|
||||
.reg = MLXREG_LC_REG_CPLD1_VER_OFFSET,
|
||||
.bit = GENMASK(7, 0),
|
||||
.mode = 0444,
|
||||
},
|
||||
{
|
||||
.label = "fpga1_version",
|
||||
.reg = MLXREG_LC_REG_FPGA1_VER_OFFSET,
|
||||
.bit = GENMASK(7, 0),
|
||||
.mode = 0444,
|
||||
},
|
||||
{
|
||||
.label = "cpld1_pn",
|
||||
.reg = MLXREG_LC_REG_CPLD1_PN_OFFSET,
|
||||
.bit = GENMASK(15, 0),
|
||||
.mode = 0444,
|
||||
.regnum = 2,
|
||||
},
|
||||
{
|
||||
.label = "fpga1_pn",
|
||||
.reg = MLXREG_LC_REG_FPGA1_PN_OFFSET,
|
||||
.bit = GENMASK(15, 0),
|
||||
.mode = 0444,
|
||||
.regnum = 2,
|
||||
},
|
||||
{
|
||||
.label = "cpld1_version_min",
|
||||
.reg = MLXREG_LC_REG_CPLD1_MVER_OFFSET,
|
||||
.bit = GENMASK(7, 0),
|
||||
.mode = 0444,
|
||||
},
|
||||
{
|
||||
.label = "fpga1_version_min",
|
||||
.reg = MLXREG_LC_REG_FPGA1_MVER_OFFSET,
|
||||
.bit = GENMASK(7, 0),
|
||||
.mode = 0444,
|
||||
},
|
||||
{
|
||||
.label = "reset_fpga_not_done",
|
||||
.reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET,
|
||||
.mask = GENMASK(7, 0) & ~BIT(1),
|
||||
.mode = 0444,
|
||||
},
|
||||
{
|
||||
.label = "reset_aux_pwr_or_ref",
|
||||
.reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET,
|
||||
.mask = GENMASK(7, 0) & ~BIT(2),
|
||||
.mode = 0444,
|
||||
},
|
||||
{
|
||||
.label = "reset_dc_dc_pwr_fail",
|
||||
.reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET,
|
||||
.mask = GENMASK(7, 0) & ~BIT(3),
|
||||
.mode = 0444,
|
||||
},
|
||||
{
|
||||
.label = "reset_from_chassis",
|
||||
.reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET,
|
||||
.mask = GENMASK(7, 0) & ~BIT(4),
|
||||
.mode = 0444,
|
||||
},
|
||||
{
|
||||
.label = "reset_pwr_off_from_chassis",
|
||||
.reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET,
|
||||
.mask = GENMASK(7, 0) & ~BIT(5),
|
||||
.mode = 0444,
|
||||
},
|
||||
{
|
||||
.label = "reset_line_card",
|
||||
.reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET,
|
||||
.mask = GENMASK(7, 0) & ~BIT(6),
|
||||
.mode = 0444,
|
||||
},
|
||||
{
|
||||
.label = "reset_line_card_pwr_en",
|
||||
.reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET,
|
||||
.mask = GENMASK(7, 0) & ~BIT(7),
|
||||
.mode = 0444,
|
||||
},
|
||||
{
|
||||
.label = "cpld_upgrade_en",
|
||||
.reg = MLXREG_LC_REG_FIELD_UPGRADE,
|
||||
.mask = GENMASK(7, 0) & ~BIT(0),
|
||||
.mode = 0644,
|
||||
.secured = 1,
|
||||
},
|
||||
{
|
||||
.label = "fpga_upgrade_en",
|
||||
.reg = MLXREG_LC_REG_FIELD_UPGRADE,
|
||||
.mask = GENMASK(7, 0) & ~BIT(1),
|
||||
.mode = 0644,
|
||||
.secured = 1,
|
||||
},
|
||||
{
|
||||
.label = "qsfp_pwr_en",
|
||||
.reg = MLXREG_LC_REG_GP0_OFFSET,
|
||||
.mask = GENMASK(7, 0) & ~BIT(0),
|
||||
.mode = 0644,
|
||||
},
|
||||
{
|
||||
.label = "vpd_wp",
|
||||
.reg = MLXREG_LC_REG_GP0_OFFSET,
|
||||
.mask = GENMASK(7, 0) & ~BIT(3),
|
||||
.mode = 0644,
|
||||
.secured = 1,
|
||||
},
|
||||
{
|
||||
.label = "agb_spi_burn_en",
|
||||
.reg = MLXREG_LC_REG_GP0_OFFSET,
|
||||
.mask = GENMASK(7, 0) & ~BIT(5),
|
||||
.mode = 0644,
|
||||
.secured = 1,
|
||||
},
|
||||
{
|
||||
.label = "fpga_spi_burn_en",
|
||||
.reg = MLXREG_LC_REG_GP0_OFFSET,
|
||||
.mask = GENMASK(7, 0) & ~BIT(6),
|
||||
.mode = 0644,
|
||||
.secured = 1,
|
||||
},
|
||||
{
|
||||
.label = "max_power",
|
||||
.reg = MLXREG_LC_REG_MAX_POWER_OFFSET,
|
||||
.bit = GENMASK(15, 0),
|
||||
.mode = 0444,
|
||||
.regnum = 2,
|
||||
},
|
||||
{
|
||||
.label = "config",
|
||||
.reg = MLXREG_LC_REG_CONFIG_OFFSET,
|
||||
.bit = GENMASK(15, 0),
|
||||
.mode = 0444,
|
||||
.regnum = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mlxreg_core_platform_data mlxreg_lc_regs_io = {
|
||||
.data = mlxreg_lc_io_data,
|
||||
.counter = ARRAY_SIZE(mlxreg_lc_io_data),
|
||||
};
|
||||
|
||||
static int
|
||||
mlxreg_lc_create_static_devices(struct mlxreg_lc *mlxreg_lc, struct mlxreg_hotplug_device *devs,
|
||||
int size)
|
||||
{
|
||||
struct mlxreg_hotplug_device *dev = devs;
|
||||
int i;
|
||||
|
||||
/* Create static I2C device feeding by auxiliary or main power. */
|
||||
for (i = 0; i < size; i++, dev++) {
|
||||
dev->client = i2c_new_client_device(dev->adapter, dev->brdinfo);
|
||||
if (IS_ERR(dev->client)) {
|
||||
dev_err(mlxreg_lc->dev, "Failed to create client %s at bus %d at addr 0x%02x\n",
|
||||
dev->brdinfo->type, dev->nr, dev->brdinfo->addr);
|
||||
|
||||
dev->adapter = NULL;
|
||||
goto fail_create_static_devices;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_create_static_devices:
|
||||
while (--i >= 0) {
|
||||
dev = devs + i;
|
||||
i2c_unregister_device(dev->client);
|
||||
dev->client = NULL;
|
||||
}
|
||||
return IS_ERR(dev->client);
|
||||
}
|
||||
|
||||
static void
|
||||
mlxreg_lc_destroy_static_devices(struct mlxreg_lc *mlxreg_lc, struct mlxreg_hotplug_device *devs,
|
||||
int size)
|
||||
{
|
||||
struct mlxreg_hotplug_device *dev = devs;
|
||||
int i;
|
||||
|
||||
/* Destroy static I2C device feeding by auxiliary or main power. */
|
||||
for (i = 0; i < size; i++, dev++) {
|
||||
if (dev->client) {
|
||||
i2c_unregister_device(dev->client);
|
||||
dev->client = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int mlxreg_lc_power_on_off(struct mlxreg_lc *mlxreg_lc, u8 action)
|
||||
{
|
||||
u32 regval;
|
||||
int err;
|
||||
|
||||
mutex_lock(&mlxreg_lc->lock);
|
||||
|
||||
err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_pwr, ®val);
|
||||
if (err)
|
||||
goto regmap_read_fail;
|
||||
|
||||
if (action)
|
||||
regval |= BIT(mlxreg_lc->data->slot - 1);
|
||||
else
|
||||
regval &= ~BIT(mlxreg_lc->data->slot - 1);
|
||||
|
||||
err = regmap_write(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_pwr, regval);
|
||||
|
||||
regmap_read_fail:
|
||||
mutex_unlock(&mlxreg_lc->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mlxreg_lc_enable_disable(struct mlxreg_lc *mlxreg_lc, bool action)
|
||||
{
|
||||
u32 regval;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Hardware holds the line card after powering on in the disabled state. Holding line card
|
||||
* in disabled state protects access to the line components, like FPGA and gearboxes.
|
||||
* Line card should be enabled in order to get it in operational state. Line card could be
|
||||
* disabled for moving it to non-operational state. Enabling line card does not affect the
|
||||
* line card which is already has been enabled. Disabling does not affect the disabled line
|
||||
* card.
|
||||
*/
|
||||
mutex_lock(&mlxreg_lc->lock);
|
||||
|
||||
err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_ena, ®val);
|
||||
if (err)
|
||||
goto regmap_read_fail;
|
||||
|
||||
if (action)
|
||||
regval |= BIT(mlxreg_lc->data->slot - 1);
|
||||
else
|
||||
regval &= ~BIT(mlxreg_lc->data->slot - 1);
|
||||
|
||||
err = regmap_write(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_ena, regval);
|
||||
|
||||
regmap_read_fail:
|
||||
mutex_unlock(&mlxreg_lc->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
mlxreg_lc_sn4800_c16_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap,
|
||||
struct mlxreg_core_data *data)
|
||||
{
|
||||
struct device *dev = &data->hpdev.client->dev;
|
||||
|
||||
/* Set line card configuration according to the type. */
|
||||
mlxreg_lc->mux_data = mlxreg_lc_mux_data;
|
||||
mlxreg_lc->io_data = &mlxreg_lc_regs_io;
|
||||
mlxreg_lc->led_data = &mlxreg_lc_led;
|
||||
mlxreg_lc->mux_brdinfo = &mlxreg_lc_mux_brdinfo;
|
||||
|
||||
mlxreg_lc->aux_devs = devm_kmemdup(dev, mlxreg_lc_aux_pwr_brdinfo,
|
||||
sizeof(mlxreg_lc_aux_pwr_brdinfo), GFP_KERNEL);
|
||||
if (!mlxreg_lc->aux_devs)
|
||||
return -ENOMEM;
|
||||
mlxreg_lc->aux_devs_num = ARRAY_SIZE(mlxreg_lc_aux_pwr_brdinfo);
|
||||
mlxreg_lc->main_devs = devm_kmemdup(dev, mlxreg_lc_main_pwr_brdinfo,
|
||||
sizeof(mlxreg_lc_main_pwr_brdinfo), GFP_KERNEL);
|
||||
if (!mlxreg_lc->main_devs)
|
||||
return -ENOMEM;
|
||||
mlxreg_lc->main_devs_num = ARRAY_SIZE(mlxreg_lc_main_pwr_brdinfo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mlxreg_lc_state_update(struct mlxreg_lc *mlxreg_lc, enum mlxreg_lc_state state, u8 action)
|
||||
{
|
||||
mutex_lock(&mlxreg_lc->lock);
|
||||
|
||||
if (action)
|
||||
mlxreg_lc->state |= state;
|
||||
else
|
||||
mlxreg_lc->state &= ~state;
|
||||
|
||||
mutex_unlock(&mlxreg_lc->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback is to be called from mlxreg-hotplug driver to notify about line card about received
|
||||
* event.
|
||||
*/
|
||||
static int mlxreg_lc_event_handler(void *handle, enum mlxreg_hotplug_kind kind, u8 action)
|
||||
{
|
||||
struct mlxreg_lc *mlxreg_lc = handle;
|
||||
int err = 0;
|
||||
|
||||
dev_info(mlxreg_lc->dev, "linecard#%d state %d event kind %d action %d\n",
|
||||
mlxreg_lc->data->slot, mlxreg_lc->state, kind, action);
|
||||
|
||||
if (!(mlxreg_lc->state & MLXREG_LC_INITIALIZED))
|
||||
return 0;
|
||||
|
||||
switch (kind) {
|
||||
case MLXREG_HOTPLUG_LC_SYNCED:
|
||||
/*
|
||||
* Synchronization event - hardware and firmware are synchronized. Power on/off
|
||||
* line card - to allow/disallow main power source.
|
||||
*/
|
||||
mlxreg_lc_state_update(mlxreg_lc, MLXREG_LC_SYNCED, action);
|
||||
/* Power line card if it is not powered yet. */
|
||||
if (!(mlxreg_lc->state & MLXREG_LC_POWERED) && action) {
|
||||
err = mlxreg_lc_power_on_off(mlxreg_lc, 1);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
/* In case line card is configured - enable it. */
|
||||
if (mlxreg_lc->state & MLXREG_LC_CONFIGURED && action)
|
||||
err = mlxreg_lc_enable_disable(mlxreg_lc, 1);
|
||||
break;
|
||||
case MLXREG_HOTPLUG_LC_POWERED:
|
||||
/* Power event - attach or de-attach line card device feeding by the main power. */
|
||||
if (action) {
|
||||
/* Do not create devices, if line card is already powered. */
|
||||
if (mlxreg_lc->state & MLXREG_LC_POWERED) {
|
||||
/* In case line card is configured - enable it. */
|
||||
if (mlxreg_lc->state & MLXREG_LC_CONFIGURED)
|
||||
err = mlxreg_lc_enable_disable(mlxreg_lc, 1);
|
||||
return err;
|
||||
}
|
||||
err = mlxreg_lc_create_static_devices(mlxreg_lc, mlxreg_lc->main_devs,
|
||||
mlxreg_lc->main_devs_num);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* In case line card is already in ready state - enable it. */
|
||||
if (mlxreg_lc->state & MLXREG_LC_CONFIGURED)
|
||||
err = mlxreg_lc_enable_disable(mlxreg_lc, 1);
|
||||
} else {
|
||||
mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->main_devs,
|
||||
mlxreg_lc->main_devs_num);
|
||||
}
|
||||
mlxreg_lc_state_update(mlxreg_lc, MLXREG_LC_POWERED, action);
|
||||
break;
|
||||
case MLXREG_HOTPLUG_LC_READY:
|
||||
/*
|
||||
* Ready event – enable line card by releasing it from reset or disable it by put
|
||||
* to reset state.
|
||||
*/
|
||||
err = mlxreg_lc_enable_disable(mlxreg_lc, !!action);
|
||||
break;
|
||||
case MLXREG_HOTPLUG_LC_THERMAL:
|
||||
/* Thermal shutdown event – power off line card. */
|
||||
if (action)
|
||||
err = mlxreg_lc_power_on_off(mlxreg_lc, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback is to be called from i2c-mux-mlxcpld driver to indicate that all adapter devices has
|
||||
* been created.
|
||||
*/
|
||||
static int mlxreg_lc_completion_notify(void *handle, struct i2c_adapter *parent,
|
||||
struct i2c_adapter *adapters[])
|
||||
{
|
||||
struct mlxreg_hotplug_device *main_dev, *aux_dev;
|
||||
struct mlxreg_lc *mlxreg_lc = handle;
|
||||
u32 regval;
|
||||
int i, err;
|
||||
|
||||
/* Update I2C devices feeding by auxiliary power. */
|
||||
aux_dev = mlxreg_lc->aux_devs;
|
||||
for (i = 0; i < mlxreg_lc->aux_devs_num; i++, aux_dev++) {
|
||||
aux_dev->adapter = adapters[aux_dev->nr];
|
||||
aux_dev->nr = adapters[aux_dev->nr]->nr;
|
||||
}
|
||||
|
||||
err = mlxreg_lc_create_static_devices(mlxreg_lc, mlxreg_lc->aux_devs,
|
||||
mlxreg_lc->aux_devs_num);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Update I2C devices feeding by main power. */
|
||||
main_dev = mlxreg_lc->main_devs;
|
||||
for (i = 0; i < mlxreg_lc->main_devs_num; i++, main_dev++) {
|
||||
main_dev->adapter = adapters[main_dev->nr];
|
||||
main_dev->nr = adapters[main_dev->nr]->nr;
|
||||
}
|
||||
|
||||
/* Verify if line card is powered. */
|
||||
err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_pwr, ®val);
|
||||
if (err)
|
||||
goto mlxreg_lc_regmap_read_power_fail;
|
||||
|
||||
if (regval & mlxreg_lc->data->mask) {
|
||||
err = mlxreg_lc_create_static_devices(mlxreg_lc, mlxreg_lc->main_devs,
|
||||
mlxreg_lc->main_devs_num);
|
||||
if (err)
|
||||
goto mlxreg_lc_create_static_devices_failed;
|
||||
|
||||
mlxreg_lc_state_update(mlxreg_lc, MLXREG_LC_POWERED, 1);
|
||||
}
|
||||
|
||||
/* Verify if line card is synchronized. */
|
||||
err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_sync, ®val);
|
||||
if (err)
|
||||
goto mlxreg_lc_regmap_read_sync_fail;
|
||||
|
||||
/* Power on line card if necessary. */
|
||||
if (regval & mlxreg_lc->data->mask) {
|
||||
mlxreg_lc->state |= MLXREG_LC_SYNCED;
|
||||
mlxreg_lc_state_update(mlxreg_lc, MLXREG_LC_SYNCED, 1);
|
||||
if (mlxreg_lc->state & ~MLXREG_LC_POWERED) {
|
||||
err = mlxreg_lc_power_on_off(mlxreg_lc, 1);
|
||||
if (err)
|
||||
goto mlxreg_lc_regmap_power_on_off_fail;
|
||||
}
|
||||
}
|
||||
|
||||
mlxreg_lc_state_update(mlxreg_lc, MLXREG_LC_INITIALIZED, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
mlxreg_lc_regmap_power_on_off_fail:
|
||||
mlxreg_lc_regmap_read_sync_fail:
|
||||
if (mlxreg_lc->state & MLXREG_LC_POWERED)
|
||||
mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->main_devs,
|
||||
mlxreg_lc->main_devs_num);
|
||||
mlxreg_lc_create_static_devices_failed:
|
||||
mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->aux_devs, mlxreg_lc->aux_devs_num);
|
||||
mlxreg_lc_regmap_read_power_fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
mlxreg_lc_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap,
|
||||
struct mlxreg_core_data *data)
|
||||
{
|
||||
struct device *dev = &data->hpdev.client->dev;
|
||||
int lsb, err;
|
||||
u32 regval;
|
||||
|
||||
/* Validate line card type. */
|
||||
err = regmap_read(regmap, MLXREG_LC_REG_CONFIG_OFFSET, &lsb);
|
||||
err = (!err) ? regmap_read(regmap, MLXREG_LC_REG_CONFIG_OFFSET, ®val) : err;
|
||||
if (err)
|
||||
return err;
|
||||
regval = (regval & GENMASK(7, 0)) << 8 | (lsb & GENMASK(7, 0));
|
||||
switch (regval) {
|
||||
case MLXREG_LC_SN4800_C16:
|
||||
err = mlxreg_lc_sn4800_c16_config_init(mlxreg_lc, regmap, data);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Create mux infrastructure. */
|
||||
mlxreg_lc->mux_data->handle = mlxreg_lc;
|
||||
mlxreg_lc->mux_data->completion_notify = mlxreg_lc_completion_notify;
|
||||
mlxreg_lc->mux_brdinfo->platform_data = mlxreg_lc->mux_data;
|
||||
mlxreg_lc->mux = platform_device_register_resndata(dev, "i2c-mux-mlxcpld", data->hpdev.nr,
|
||||
NULL, 0, mlxreg_lc->mux_data,
|
||||
sizeof(*mlxreg_lc->mux_data));
|
||||
if (IS_ERR(mlxreg_lc->mux))
|
||||
return PTR_ERR(mlxreg_lc->mux);
|
||||
|
||||
/* Register IO access driver. */
|
||||
if (mlxreg_lc->io_data) {
|
||||
mlxreg_lc->io_data->regmap = regmap;
|
||||
mlxreg_lc->io_regs =
|
||||
platform_device_register_resndata(dev, "mlxreg-io", data->hpdev.nr, NULL, 0,
|
||||
mlxreg_lc->io_data, sizeof(*mlxreg_lc->io_data));
|
||||
if (IS_ERR(mlxreg_lc->io_regs)) {
|
||||
err = PTR_ERR(mlxreg_lc->io_regs);
|
||||
goto fail_register_io;
|
||||
}
|
||||
}
|
||||
|
||||
/* Register LED driver. */
|
||||
if (mlxreg_lc->led_data) {
|
||||
mlxreg_lc->led_data->regmap = regmap;
|
||||
mlxreg_lc->led =
|
||||
platform_device_register_resndata(dev, "leds-mlxreg", data->hpdev.nr, NULL, 0,
|
||||
mlxreg_lc->led_data,
|
||||
sizeof(*mlxreg_lc->led_data));
|
||||
if (IS_ERR(mlxreg_lc->led)) {
|
||||
err = PTR_ERR(mlxreg_lc->led);
|
||||
goto fail_register_led;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_register_led:
|
||||
if (mlxreg_lc->io_regs)
|
||||
platform_device_unregister(mlxreg_lc->io_regs);
|
||||
fail_register_io:
|
||||
if (mlxreg_lc->mux)
|
||||
platform_device_unregister(mlxreg_lc->mux);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void mlxreg_lc_config_exit(struct mlxreg_lc *mlxreg_lc)
|
||||
{
|
||||
/* Unregister LED driver. */
|
||||
if (mlxreg_lc->led)
|
||||
platform_device_unregister(mlxreg_lc->led);
|
||||
/* Unregister IO access driver. */
|
||||
if (mlxreg_lc->io_regs)
|
||||
platform_device_unregister(mlxreg_lc->io_regs);
|
||||
/* Remove mux infrastructure. */
|
||||
if (mlxreg_lc->mux)
|
||||
platform_device_unregister(mlxreg_lc->mux);
|
||||
}
|
||||
|
||||
static int mlxreg_lc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mlxreg_core_hotplug_platform_data *par_pdata;
|
||||
struct mlxreg_core_data *data;
|
||||
struct mlxreg_lc *mlxreg_lc;
|
||||
void *regmap;
|
||||
int i, err;
|
||||
|
||||
data = dev_get_platdata(&pdev->dev);
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
mlxreg_lc = devm_kzalloc(&pdev->dev, sizeof(*mlxreg_lc), GFP_KERNEL);
|
||||
if (!mlxreg_lc)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&mlxreg_lc->lock);
|
||||
/* Set event notification callback. */
|
||||
if (data->notifier) {
|
||||
data->notifier->user_handler = mlxreg_lc_event_handler;
|
||||
data->notifier->handle = mlxreg_lc;
|
||||
}
|
||||
data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr);
|
||||
if (!data->hpdev.adapter) {
|
||||
dev_err(&pdev->dev, "Failed to get adapter for bus %d\n",
|
||||
data->hpdev.nr);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Create device at the top of line card I2C tree.*/
|
||||
data->hpdev.client = i2c_new_client_device(data->hpdev.adapter,
|
||||
data->hpdev.brdinfo);
|
||||
if (IS_ERR(data->hpdev.client)) {
|
||||
dev_err(&pdev->dev, "Failed to create client %s at bus %d at addr 0x%02x\n",
|
||||
data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr);
|
||||
|
||||
i2c_put_adapter(data->hpdev.adapter);
|
||||
data->hpdev.adapter = NULL;
|
||||
return PTR_ERR(data->hpdev.client);
|
||||
}
|
||||
|
||||
regmap = devm_regmap_init_i2c(data->hpdev.client,
|
||||
&mlxreg_lc_regmap_conf);
|
||||
if (IS_ERR(regmap)) {
|
||||
err = PTR_ERR(regmap);
|
||||
goto mlxreg_lc_probe_fail;
|
||||
}
|
||||
|
||||
/* Set default registers. */
|
||||
for (i = 0; i < mlxreg_lc_regmap_conf.num_reg_defaults; i++) {
|
||||
err = regmap_write(regmap, mlxreg_lc_regmap_default[i].reg,
|
||||
mlxreg_lc_regmap_default[i].def);
|
||||
if (err)
|
||||
goto mlxreg_lc_probe_fail;
|
||||
}
|
||||
|
||||
/* Sync registers with hardware. */
|
||||
regcache_mark_dirty(regmap);
|
||||
err = regcache_sync(regmap);
|
||||
if (err)
|
||||
goto mlxreg_lc_probe_fail;
|
||||
|
||||
par_pdata = data->hpdev.brdinfo->platform_data;
|
||||
mlxreg_lc->par_regmap = par_pdata->regmap;
|
||||
mlxreg_lc->data = data;
|
||||
mlxreg_lc->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, mlxreg_lc);
|
||||
|
||||
/* Configure line card. */
|
||||
err = mlxreg_lc_config_init(mlxreg_lc, regmap, data);
|
||||
if (err)
|
||||
goto mlxreg_lc_probe_fail;
|
||||
|
||||
return err;
|
||||
|
||||
mlxreg_lc_probe_fail:
|
||||
i2c_put_adapter(data->hpdev.adapter);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mlxreg_lc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mlxreg_core_data *data = dev_get_platdata(&pdev->dev);
|
||||
struct mlxreg_lc *mlxreg_lc = platform_get_drvdata(pdev);
|
||||
|
||||
/* Clear event notification callback. */
|
||||
if (data->notifier) {
|
||||
data->notifier->user_handler = NULL;
|
||||
data->notifier->handle = NULL;
|
||||
}
|
||||
|
||||
/* Destroy static I2C device feeding by main power. */
|
||||
mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->main_devs,
|
||||
mlxreg_lc->main_devs_num);
|
||||
/* Destroy static I2C device feeding by auxiliary power. */
|
||||
mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->aux_devs, mlxreg_lc->aux_devs_num);
|
||||
/* Unregister underlying drivers. */
|
||||
mlxreg_lc_config_exit(mlxreg_lc);
|
||||
if (data->hpdev.client) {
|
||||
i2c_unregister_device(data->hpdev.client);
|
||||
data->hpdev.client = NULL;
|
||||
i2c_put_adapter(data->hpdev.adapter);
|
||||
data->hpdev.adapter = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mlxreg_lc_driver = {
|
||||
.probe = mlxreg_lc_probe,
|
||||
.remove = mlxreg_lc_remove,
|
||||
.driver = {
|
||||
.name = "mlxreg-lc",
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(mlxreg_lc_driver);
|
||||
|
||||
MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Nvidia line card platform driver");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_ALIAS("platform:mlxreg-lc");
|
@ -139,13 +139,12 @@ static acpi_status s3_wmi_attach_spi_device(acpi_handle handle,
|
||||
|
||||
static int s3_wmi_check_platform_device(struct device *dev, void *data)
|
||||
{
|
||||
struct acpi_device *adev, *ts_adev = NULL;
|
||||
acpi_handle handle;
|
||||
struct acpi_device *adev = ACPI_COMPANION(dev);
|
||||
struct acpi_device *ts_adev = NULL;
|
||||
acpi_status status;
|
||||
|
||||
/* ignore non ACPI devices */
|
||||
handle = ACPI_HANDLE(dev);
|
||||
if (!handle || acpi_bus_get_device(handle, &adev))
|
||||
if (!adev)
|
||||
return 0;
|
||||
|
||||
/* check for LID ACPI switch */
|
||||
@ -159,7 +158,7 @@ static int s3_wmi_check_platform_device(struct device *dev, void *data)
|
||||
strlen(SPI_CTL_OBJ_NAME)))
|
||||
return 0;
|
||||
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, adev->handle, 1,
|
||||
s3_wmi_attach_spi_device, NULL,
|
||||
&ts_adev, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
|
@ -159,12 +159,11 @@ mshw0011_notify(struct mshw0011_data *cdata, u8 arg1, u8 arg2,
|
||||
unsigned int *ret_value)
|
||||
{
|
||||
union acpi_object *obj;
|
||||
struct acpi_device *adev;
|
||||
acpi_handle handle;
|
||||
unsigned int i;
|
||||
|
||||
handle = ACPI_HANDLE(&cdata->adp1->dev);
|
||||
if (!handle || acpi_bus_get_device(handle, &adev))
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
obj = acpi_evaluate_dsm_typed(handle, &mshw0011_guid, arg1, arg2, NULL,
|
||||
|
@ -77,6 +77,42 @@ static const struct software_node ssam_node_bas_dtx = {
|
||||
.parent = &ssam_node_root,
|
||||
};
|
||||
|
||||
/* HID keyboard (TID1). */
|
||||
static const struct software_node ssam_node_hid_tid1_keyboard = {
|
||||
.name = "ssam:01:15:01:01:00",
|
||||
.parent = &ssam_node_root,
|
||||
};
|
||||
|
||||
/* HID pen stash (TID1; pen taken / stashed away evens). */
|
||||
static const struct software_node ssam_node_hid_tid1_penstash = {
|
||||
.name = "ssam:01:15:01:02:00",
|
||||
.parent = &ssam_node_root,
|
||||
};
|
||||
|
||||
/* HID touchpad (TID1). */
|
||||
static const struct software_node ssam_node_hid_tid1_touchpad = {
|
||||
.name = "ssam:01:15:01:03:00",
|
||||
.parent = &ssam_node_root,
|
||||
};
|
||||
|
||||
/* HID device instance 6 (TID1, unknown HID device). */
|
||||
static const struct software_node ssam_node_hid_tid1_iid6 = {
|
||||
.name = "ssam:01:15:01:06:00",
|
||||
.parent = &ssam_node_root,
|
||||
};
|
||||
|
||||
/* HID device instance 7 (TID1, unknown HID device). */
|
||||
static const struct software_node ssam_node_hid_tid1_iid7 = {
|
||||
.name = "ssam:01:15:01:07:00",
|
||||
.parent = &ssam_node_root,
|
||||
};
|
||||
|
||||
/* HID system controls (TID1). */
|
||||
static const struct software_node ssam_node_hid_tid1_sysctrl = {
|
||||
.name = "ssam:01:15:01:08:00",
|
||||
.parent = &ssam_node_root,
|
||||
};
|
||||
|
||||
/* HID keyboard. */
|
||||
static const struct software_node ssam_node_hid_main_keyboard = {
|
||||
.name = "ssam:01:15:02:01:00",
|
||||
@ -159,6 +195,21 @@ static const struct software_node *ssam_node_group_sl3[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Devices for Surface Laptop Studio. */
|
||||
static const struct software_node *ssam_node_group_sls[] = {
|
||||
&ssam_node_root,
|
||||
&ssam_node_bat_ac,
|
||||
&ssam_node_bat_main,
|
||||
&ssam_node_tmp_pprof,
|
||||
&ssam_node_hid_tid1_keyboard,
|
||||
&ssam_node_hid_tid1_penstash,
|
||||
&ssam_node_hid_tid1_touchpad,
|
||||
&ssam_node_hid_tid1_iid6,
|
||||
&ssam_node_hid_tid1_iid7,
|
||||
&ssam_node_hid_tid1_sysctrl,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Devices for Surface Laptop Go. */
|
||||
static const struct software_node *ssam_node_group_slg1[] = {
|
||||
&ssam_node_root,
|
||||
@ -177,6 +228,15 @@ static const struct software_node *ssam_node_group_sp7[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct software_node *ssam_node_group_sp8[] = {
|
||||
&ssam_node_root,
|
||||
&ssam_node_bat_ac,
|
||||
&ssam_node_bat_main,
|
||||
&ssam_node_tmp_pprof,
|
||||
/* TODO: Add support for keyboard cover. */
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
/* -- Device registry helper functions. ------------------------------------- */
|
||||
|
||||
@ -483,6 +543,9 @@ static const struct acpi_device_id ssam_platform_hub_match[] = {
|
||||
/* Surface Pro 7+ */
|
||||
{ "MSHW0119", (unsigned long)ssam_node_group_sp7 },
|
||||
|
||||
/* Surface Pro 8 */
|
||||
{ "MSHW0263", (unsigned long)ssam_node_group_sp8 },
|
||||
|
||||
/* Surface Book 2 */
|
||||
{ "MSHW0107", (unsigned long)ssam_node_group_gen5 },
|
||||
|
||||
@ -507,6 +570,9 @@ static const struct acpi_device_id ssam_platform_hub_match[] = {
|
||||
/* Surface Laptop Go 1 */
|
||||
{ "MSHW0118", (unsigned long)ssam_node_group_slg1 },
|
||||
|
||||
/* Surface Laptop Studio */
|
||||
{ "MSHW0123", (unsigned long)ssam_node_group_sls },
|
||||
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ssam_platform_hub_match);
|
||||
|
@ -26,6 +26,11 @@ static const struct property_entry lid_device_props_l17[] = {
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct property_entry lid_device_props_l4B[] = {
|
||||
PROPERTY_ENTRY_U32("gpe", 0x4B),
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct property_entry lid_device_props_l4D[] = {
|
||||
PROPERTY_ENTRY_U32("gpe", 0x4D),
|
||||
{},
|
||||
@ -158,6 +163,14 @@ static const struct dmi_system_id dmi_lid_device_table[] = {
|
||||
},
|
||||
.driver_data = (void *)lid_device_props_l4D,
|
||||
},
|
||||
{
|
||||
.ident = "Surface Laptop Studio",
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop Studio"),
|
||||
},
|
||||
.driver_data = (void *)lid_device_props_l4B,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -91,6 +91,21 @@ config PEAQ_WMI
|
||||
help
|
||||
Say Y here if you want to support WMI-based hotkeys on PEAQ 2-in-1s.
|
||||
|
||||
config NVIDIA_WMI_EC_BACKLIGHT
|
||||
tristate "EC Backlight Driver for Hybrid Graphics Notebook Systems"
|
||||
depends on ACPI_WMI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
This driver provides a sysfs backlight interface for notebook systems
|
||||
which are equipped with NVIDIA hybrid graphics and drive LCD backlight
|
||||
levels through the Embedded Controller (EC).
|
||||
|
||||
Say Y or M here if you want to control the backlight on a notebook
|
||||
system with an EC-driven backlight.
|
||||
|
||||
If you choose to compile this driver as a module the module will be
|
||||
called nvidia-wmi-ec-backlight.
|
||||
|
||||
config XIAOMI_WMI
|
||||
tristate "Xiaomi WMI key driver"
|
||||
depends on ACPI_WMI
|
||||
@ -426,6 +441,7 @@ config HP_WMI
|
||||
depends on RFKILL || RFKILL = n
|
||||
select INPUT_SPARSEKMAP
|
||||
select ACPI_PLATFORM_PROFILE
|
||||
select HWMON
|
||||
help
|
||||
Say Y here if you want to support WMI-based hotkeys on HP laptops and
|
||||
to read data from WMI such as docking or ambient light sensor state.
|
||||
@ -713,6 +729,16 @@ config PCENGINES_APU2
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called pcengines-apuv2.
|
||||
|
||||
config BARCO_P50_GPIO
|
||||
tristate "Barco P50 GPIO driver for identify LED/button"
|
||||
depends on GPIOLIB
|
||||
help
|
||||
This driver provides access to the GPIOs for the identify button
|
||||
and led present on Barco P50 board.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called barco-p50-gpio.
|
||||
|
||||
config SAMSUNG_LAPTOP
|
||||
tristate "Samsung Laptop driver"
|
||||
depends on RFKILL || RFKILL = n
|
||||
@ -905,6 +931,9 @@ config SONYPI_COMPAT
|
||||
config SYSTEM76_ACPI
|
||||
tristate "System76 ACPI Driver"
|
||||
depends on ACPI
|
||||
depends on ACPI_BATTERY
|
||||
depends on HWMON
|
||||
depends on INPUT
|
||||
select NEW_LEDS
|
||||
select LEDS_CLASS
|
||||
select LEDS_TRIGGERS
|
||||
|
@ -11,6 +11,7 @@ obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
|
||||
# WMI drivers
|
||||
obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o
|
||||
obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
|
||||
obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o
|
||||
obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o
|
||||
obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o
|
||||
obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o
|
||||
@ -80,6 +81,9 @@ obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
|
||||
# PC Engines
|
||||
obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o
|
||||
|
||||
# Barco
|
||||
obj-$(CONFIG_BARCO_P50_GPIO) += barco-p50-gpio.o
|
||||
|
||||
# Samsung
|
||||
obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o
|
||||
obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
|
||||
|
@ -138,7 +138,7 @@ struct event_return_value {
|
||||
u16 reserved1;
|
||||
u8 kbd_dock_state;
|
||||
u8 reserved2;
|
||||
} __attribute__((packed));
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* GUID3 Get Device Status device flags
|
||||
@ -172,33 +172,33 @@ struct func_input_params {
|
||||
u8 app_status; /* Acer Device Status. LM, ePM, RF Button... */
|
||||
u8 app_mask; /* Bit mask to app_status */
|
||||
u8 reserved;
|
||||
} __attribute__((packed));
|
||||
} __packed;
|
||||
|
||||
struct func_return_value {
|
||||
u8 error_code; /* Error Code */
|
||||
u8 ec_return_value; /* EC Return Value */
|
||||
u16 reserved;
|
||||
} __attribute__((packed));
|
||||
} __packed;
|
||||
|
||||
struct wmid3_gds_set_input_param { /* Set Device Status input parameter */
|
||||
u8 function_num; /* Function Number */
|
||||
u8 hotkey_number; /* Hotkey Number */
|
||||
u16 devices; /* Set Device */
|
||||
u8 volume_value; /* Volume Value */
|
||||
} __attribute__((packed));
|
||||
} __packed;
|
||||
|
||||
struct wmid3_gds_get_input_param { /* Get Device Status input parameter */
|
||||
u8 function_num; /* Function Number */
|
||||
u8 hotkey_number; /* Hotkey Number */
|
||||
u16 devices; /* Get Device */
|
||||
} __attribute__((packed));
|
||||
} __packed;
|
||||
|
||||
struct wmid3_gds_return_value { /* Get Device Status return value*/
|
||||
u8 error_code; /* Error Code */
|
||||
u8 ec_return_value; /* EC Return Value */
|
||||
u16 devices; /* Current Device Status */
|
||||
u32 reserved;
|
||||
} __attribute__((packed));
|
||||
} __packed;
|
||||
|
||||
struct hotkey_function_type_aa {
|
||||
u8 type;
|
||||
@ -210,7 +210,7 @@ struct hotkey_function_type_aa {
|
||||
u16 display_func_bitmap;
|
||||
u16 others_func_bitmap;
|
||||
u8 commun_fn_key_number;
|
||||
} __attribute__((packed));
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Interface capability flags
|
||||
|
@ -17,9 +17,11 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h>
|
||||
@ -29,6 +31,10 @@
|
||||
#define AMD_PMC_REGISTER_RESPONSE 0x980
|
||||
#define AMD_PMC_REGISTER_ARGUMENT 0x9BC
|
||||
|
||||
/* PMC Scratch Registers */
|
||||
#define AMD_PMC_SCRATCH_REG_CZN 0x94
|
||||
#define AMD_PMC_SCRATCH_REG_YC 0xD14
|
||||
|
||||
/* Base address of SMU for mapping physical address to virtual address */
|
||||
#define AMD_PMC_SMU_INDEX_ADDRESS 0xB8
|
||||
#define AMD_PMC_SMU_INDEX_DATA 0xBC
|
||||
@ -110,6 +116,10 @@ struct amd_pmc_dev {
|
||||
u32 base_addr;
|
||||
u32 cpu_id;
|
||||
u32 active_ips;
|
||||
/* SMU version information */
|
||||
u16 major;
|
||||
u16 minor;
|
||||
u16 rev;
|
||||
struct device *dev;
|
||||
struct mutex lock; /* generic mutex lock */
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
@ -118,7 +128,7 @@ struct amd_pmc_dev {
|
||||
};
|
||||
|
||||
static struct amd_pmc_dev pmc;
|
||||
static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, bool set, u32 *data, u8 msg, bool ret);
|
||||
static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret);
|
||||
|
||||
static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset)
|
||||
{
|
||||
@ -133,7 +143,7 @@ static inline void amd_pmc_reg_write(struct amd_pmc_dev *dev, int reg_offset, u3
|
||||
struct smu_metrics {
|
||||
u32 table_version;
|
||||
u32 hint_count;
|
||||
u32 s0i3_cyclecount;
|
||||
u32 s0i3_last_entry_status;
|
||||
u32 timein_s0i2;
|
||||
u64 timeentering_s0i3_lastcapture;
|
||||
u64 timeentering_s0i3_totaltime;
|
||||
@ -147,6 +157,49 @@ struct smu_metrics {
|
||||
u64 timecondition_notmet_totaltime[SOC_SUBSYSTEM_IP_MAX];
|
||||
} __packed;
|
||||
|
||||
static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev)
|
||||
{
|
||||
int rc;
|
||||
u32 val;
|
||||
|
||||
rc = amd_pmc_send_cmd(dev, 0, &val, SMU_MSG_GETSMUVERSION, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
dev->major = (val >> 16) & GENMASK(15, 0);
|
||||
dev->minor = (val >> 8) & GENMASK(7, 0);
|
||||
dev->rev = (val >> 0) & GENMASK(7, 0);
|
||||
|
||||
dev_dbg(dev->dev, "SMU version is %u.%u.%u\n", dev->major, dev->minor, dev->rev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev,
|
||||
struct seq_file *s)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
switch (pdev->cpu_id) {
|
||||
case AMD_CPU_ID_CZN:
|
||||
val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN);
|
||||
break;
|
||||
case AMD_CPU_ID_YC:
|
||||
val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dev)
|
||||
dev_dbg(pdev->dev, "SMU idlemask s0i3: 0x%x\n", val);
|
||||
|
||||
if (s)
|
||||
seq_printf(s, "SMU idlemask : 0x%x\n", val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int smu_fw_info_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
@ -162,9 +215,12 @@ static int smu_fw_info_show(struct seq_file *s, void *unused)
|
||||
seq_puts(s, "\n=== SMU Statistics ===\n");
|
||||
seq_printf(s, "Table Version: %d\n", table.table_version);
|
||||
seq_printf(s, "Hint Count: %d\n", table.hint_count);
|
||||
seq_printf(s, "S0i3 Cycle Count: %d\n", table.s0i3_cyclecount);
|
||||
seq_printf(s, "Last S0i3 Status: %s\n", table.s0i3_last_entry_status ? "Success" :
|
||||
"Unknown/Fail");
|
||||
seq_printf(s, "Time (in us) to S0i3: %lld\n", table.timeentering_s0i3_lastcapture);
|
||||
seq_printf(s, "Time (in us) in S0i3: %lld\n", table.timein_s0i3_lastcapture);
|
||||
seq_printf(s, "Time (in us) to resume from S0i3: %lld\n",
|
||||
table.timeto_resume_to_os_lastcapture);
|
||||
|
||||
seq_puts(s, "\n=== Active time (in us) ===\n");
|
||||
for (idx = 0 ; idx < SOC_SUBSYSTEM_IP_MAX ; idx++) {
|
||||
@ -201,6 +257,23 @@ static int s0ix_stats_show(struct seq_file *s, void *unused)
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(s0ix_stats);
|
||||
|
||||
static int amd_pmc_idlemask_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct amd_pmc_dev *dev = s->private;
|
||||
int rc;
|
||||
|
||||
if (dev->major > 56 || (dev->major >= 55 && dev->minor >= 37)) {
|
||||
rc = amd_pmc_idlemask_read(dev, NULL, s);
|
||||
if (rc)
|
||||
return rc;
|
||||
} else {
|
||||
seq_puts(s, "Unsupported SMU version for Idlemask\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(amd_pmc_idlemask);
|
||||
|
||||
static void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev)
|
||||
{
|
||||
debugfs_remove_recursive(dev->dbgfs_dir);
|
||||
@ -213,6 +286,8 @@ static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev)
|
||||
&smu_fw_info_fops);
|
||||
debugfs_create_file("s0ix_stats", 0644, dev->dbgfs_dir, dev,
|
||||
&s0ix_stats_fops);
|
||||
debugfs_create_file("amd_pmc_idlemask", 0644, dev->dbgfs_dir, dev,
|
||||
&amd_pmc_idlemask_fops);
|
||||
}
|
||||
#else
|
||||
static inline void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev)
|
||||
@ -264,7 +339,7 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev)
|
||||
dev_dbg(dev->dev, "AMD_PMC_REGISTER_MESSAGE:%x\n", value);
|
||||
}
|
||||
|
||||
static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, bool set, u32 *data, u8 msg, bool ret)
|
||||
static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret)
|
||||
{
|
||||
int rc;
|
||||
u32 val;
|
||||
@ -283,7 +358,7 @@ static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, bool set, u32 *data, u8 msg
|
||||
amd_pmc_reg_write(dev, AMD_PMC_REGISTER_RESPONSE, 0);
|
||||
|
||||
/* Write argument into response register */
|
||||
amd_pmc_reg_write(dev, AMD_PMC_REGISTER_ARGUMENT, set);
|
||||
amd_pmc_reg_write(dev, AMD_PMC_REGISTER_ARGUMENT, arg);
|
||||
|
||||
/* Write message ID to message ID register */
|
||||
amd_pmc_reg_write(dev, AMD_PMC_REGISTER_MESSAGE, msg);
|
||||
@ -339,18 +414,73 @@ static int amd_pmc_get_os_hint(struct amd_pmc_dev *dev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg)
|
||||
{
|
||||
struct rtc_device *rtc_device;
|
||||
time64_t then, now, duration;
|
||||
struct rtc_wkalrm alarm;
|
||||
struct rtc_time tm;
|
||||
int rc;
|
||||
|
||||
if (pdev->major < 64 || (pdev->major == 64 && pdev->minor < 53))
|
||||
return 0;
|
||||
|
||||
rtc_device = rtc_class_open("rtc0");
|
||||
if (!rtc_device)
|
||||
return 0;
|
||||
rc = rtc_read_alarm(rtc_device, &alarm);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!alarm.enabled) {
|
||||
dev_dbg(pdev->dev, "alarm not enabled\n");
|
||||
return 0;
|
||||
}
|
||||
rc = rtc_read_time(rtc_device, &tm);
|
||||
if (rc)
|
||||
return rc;
|
||||
then = rtc_tm_to_time64(&alarm.time);
|
||||
now = rtc_tm_to_time64(&tm);
|
||||
duration = then-now;
|
||||
|
||||
/* in the past */
|
||||
if (then < now)
|
||||
return 0;
|
||||
|
||||
/* will be stored in upper 16 bits of s0i3 hint argument,
|
||||
* so timer wakeup from s0i3 is limited to ~18 hours or less
|
||||
*/
|
||||
if (duration <= 4 || duration > U16_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
*arg |= (duration << 16);
|
||||
rc = rtc_alarm_irq_enable(rtc_device, 0);
|
||||
dev_dbg(pdev->dev, "wakeup timer programmed for %lld seconds\n", duration);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __maybe_unused amd_pmc_suspend(struct device *dev)
|
||||
{
|
||||
struct amd_pmc_dev *pdev = dev_get_drvdata(dev);
|
||||
int rc;
|
||||
u8 msg;
|
||||
u32 arg = 1;
|
||||
|
||||
/* Reset and Start SMU logging - to monitor the s0i3 stats */
|
||||
amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_RESET, 0);
|
||||
amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_START, 0);
|
||||
|
||||
/* Activate CZN specific RTC functionality */
|
||||
if (pdev->cpu_id == AMD_CPU_ID_CZN) {
|
||||
rc = amd_pmc_verify_czn_rtc(pdev, &arg);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Dump the IdleMask before we send hint to SMU */
|
||||
amd_pmc_idlemask_read(pdev, dev, NULL);
|
||||
msg = amd_pmc_get_os_hint(pdev);
|
||||
rc = amd_pmc_send_cmd(pdev, 1, NULL, msg, 0);
|
||||
rc = amd_pmc_send_cmd(pdev, arg, NULL, msg, 0);
|
||||
if (rc)
|
||||
dev_err(pdev->dev, "suspend failed\n");
|
||||
|
||||
@ -363,14 +493,17 @@ static int __maybe_unused amd_pmc_resume(struct device *dev)
|
||||
int rc;
|
||||
u8 msg;
|
||||
|
||||
/* Let SMU know that we are looking for stats */
|
||||
amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, 0);
|
||||
|
||||
msg = amd_pmc_get_os_hint(pdev);
|
||||
rc = amd_pmc_send_cmd(pdev, 0, NULL, msg, 0);
|
||||
if (rc)
|
||||
dev_err(pdev->dev, "resume failed\n");
|
||||
|
||||
/* Let SMU know that we are looking for stats */
|
||||
amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, 0);
|
||||
|
||||
/* Dump the IdleMask to see the blockers */
|
||||
amd_pmc_idlemask_read(pdev, dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -457,6 +590,7 @@ static int amd_pmc_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
dev_err(dev->dev, "SMU debugging info not supported on this platform\n");
|
||||
|
||||
amd_pmc_get_smu_version(dev);
|
||||
platform_set_drvdata(pdev, dev);
|
||||
amd_pmc_dbgfs_register(dev);
|
||||
return 0;
|
||||
|
@ -2169,8 +2169,8 @@ static ssize_t throttle_thermal_policy_store(struct device *dev,
|
||||
static DEVICE_ATTR_RW(throttle_thermal_policy);
|
||||
|
||||
/* Platform profile ***********************************************************/
|
||||
static int platform_profile_get(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option *profile)
|
||||
static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
struct asus_wmi *asus;
|
||||
int tp;
|
||||
@ -2196,8 +2196,8 @@ static int platform_profile_get(struct platform_profile_handler *pprof,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int platform_profile_set(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option profile)
|
||||
static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
struct asus_wmi *asus;
|
||||
int tp;
|
||||
@ -2236,8 +2236,8 @@ static int platform_profile_setup(struct asus_wmi *asus)
|
||||
|
||||
dev_info(dev, "Using throttle_thermal_policy for platform_profile support\n");
|
||||
|
||||
asus->platform_profile_handler.profile_get = platform_profile_get;
|
||||
asus->platform_profile_handler.profile_set = platform_profile_set;
|
||||
asus->platform_profile_handler.profile_get = asus_wmi_platform_profile_get;
|
||||
asus->platform_profile_handler.profile_set = asus_wmi_platform_profile_set;
|
||||
|
||||
set_bit(PLATFORM_PROFILE_QUIET, asus->platform_profile_handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED,
|
||||
|
436
drivers/platform/x86/barco-p50-gpio.c
Normal file
436
drivers/platform/x86/barco-p50-gpio.c
Normal file
@ -0,0 +1,436 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
/*
|
||||
* Support for EC-connected GPIOs for identify
|
||||
* LED/button on Barco P50 board
|
||||
*
|
||||
* Copyright (C) 2021 Barco NV
|
||||
* Author: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
|
||||
#define DRIVER_NAME "barco-p50-gpio"
|
||||
|
||||
/* GPIO lines */
|
||||
#define P50_GPIO_LINE_LED 0
|
||||
#define P50_GPIO_LINE_BTN 1
|
||||
|
||||
/* GPIO IO Ports */
|
||||
#define P50_GPIO_IO_PORT_BASE 0x299
|
||||
|
||||
#define P50_PORT_DATA 0x00
|
||||
#define P50_PORT_CMD 0x01
|
||||
|
||||
#define P50_STATUS_OBF 0x01 /* EC output buffer full */
|
||||
#define P50_STATUS_IBF 0x02 /* EC input buffer full */
|
||||
|
||||
#define P50_CMD_READ 0xa0
|
||||
#define P50_CMD_WRITE 0x50
|
||||
|
||||
/* EC mailbox registers */
|
||||
#define P50_MBOX_REG_CMD 0x00
|
||||
#define P50_MBOX_REG_STATUS 0x01
|
||||
#define P50_MBOX_REG_PARAM 0x02
|
||||
#define P50_MBOX_REG_DATA 0x03
|
||||
|
||||
#define P50_MBOX_CMD_READ_GPIO 0x11
|
||||
#define P50_MBOX_CMD_WRITE_GPIO 0x12
|
||||
#define P50_MBOX_CMD_CLEAR 0xff
|
||||
|
||||
#define P50_MBOX_STATUS_SUCCESS 0x01
|
||||
|
||||
#define P50_MBOX_PARAM_LED 0x12
|
||||
#define P50_MBOX_PARAM_BTN 0x13
|
||||
|
||||
|
||||
struct p50_gpio {
|
||||
struct gpio_chip gc;
|
||||
struct mutex lock;
|
||||
unsigned long base;
|
||||
struct platform_device *leds_pdev;
|
||||
struct platform_device *keys_pdev;
|
||||
};
|
||||
|
||||
static struct platform_device *gpio_pdev;
|
||||
|
||||
static int gpio_params[] = {
|
||||
[P50_GPIO_LINE_LED] = P50_MBOX_PARAM_LED,
|
||||
[P50_GPIO_LINE_BTN] = P50_MBOX_PARAM_BTN,
|
||||
};
|
||||
|
||||
static const char * const gpio_names[] = {
|
||||
[P50_GPIO_LINE_LED] = "identify-led",
|
||||
[P50_GPIO_LINE_BTN] = "identify-button",
|
||||
};
|
||||
|
||||
|
||||
static struct gpiod_lookup_table p50_gpio_led_table = {
|
||||
.dev_id = "leds-gpio",
|
||||
.table = {
|
||||
GPIO_LOOKUP_IDX(DRIVER_NAME, P50_GPIO_LINE_LED, NULL, 0, GPIO_ACTIVE_HIGH),
|
||||
{}
|
||||
}
|
||||
};
|
||||
|
||||
/* GPIO LEDs */
|
||||
static struct gpio_led leds[] = {
|
||||
{ .name = "identify" }
|
||||
};
|
||||
|
||||
static struct gpio_led_platform_data leds_pdata = {
|
||||
.num_leds = ARRAY_SIZE(leds),
|
||||
.leds = leds,
|
||||
};
|
||||
|
||||
/* GPIO keyboard */
|
||||
static struct gpio_keys_button buttons[] = {
|
||||
{
|
||||
.code = KEY_VENDOR,
|
||||
.gpio = P50_GPIO_LINE_BTN,
|
||||
.active_low = 1,
|
||||
.type = EV_KEY,
|
||||
.value = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpio_keys_platform_data keys_pdata = {
|
||||
.buttons = buttons,
|
||||
.nbuttons = ARRAY_SIZE(buttons),
|
||||
.poll_interval = 100,
|
||||
.rep = 0,
|
||||
.name = "identify",
|
||||
};
|
||||
|
||||
|
||||
/* low level access routines */
|
||||
|
||||
static int p50_wait_ec(struct p50_gpio *p50, int mask, int expected)
|
||||
{
|
||||
int i, val;
|
||||
|
||||
for (i = 0; i < 100; i++) {
|
||||
val = inb(p50->base + P50_PORT_CMD) & mask;
|
||||
if (val == expected)
|
||||
return 0;
|
||||
usleep_range(500, 2000);
|
||||
}
|
||||
|
||||
dev_err(p50->gc.parent, "Timed out waiting for EC (0x%x)\n", val);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
|
||||
static int p50_read_mbox_reg(struct p50_gpio *p50, int reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* clear output buffer flag, prevent unfinished commands */
|
||||
inb(p50->base + P50_PORT_DATA);
|
||||
|
||||
/* cmd/address */
|
||||
outb(P50_CMD_READ | reg, p50->base + P50_PORT_CMD);
|
||||
|
||||
ret = p50_wait_ec(p50, P50_STATUS_OBF, P50_STATUS_OBF);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return inb(p50->base + P50_PORT_DATA);
|
||||
}
|
||||
|
||||
static int p50_write_mbox_reg(struct p50_gpio *p50, int reg, int val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* cmd/address */
|
||||
outb(P50_CMD_WRITE | reg, p50->base + P50_PORT_CMD);
|
||||
|
||||
ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* data */
|
||||
outb(val, p50->base + P50_PORT_DATA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* mbox routines */
|
||||
|
||||
static int p50_wait_mbox_idle(struct p50_gpio *p50)
|
||||
{
|
||||
int i, val;
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
val = p50_read_mbox_reg(p50, P50_MBOX_REG_CMD);
|
||||
/* cmd is 0 when idle */
|
||||
if (val <= 0)
|
||||
return val;
|
||||
|
||||
usleep_range(500, 2000);
|
||||
}
|
||||
|
||||
dev_err(p50->gc.parent, "Timed out waiting for EC mbox idle (CMD: 0x%x)\n", val);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int p50_send_mbox_cmd(struct p50_gpio *p50, int cmd, int param, int data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = p50_wait_mbox_idle(p50);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = p50_write_mbox_reg(p50, P50_MBOX_REG_DATA, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = p50_write_mbox_reg(p50, P50_MBOX_REG_PARAM, param);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, cmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = p50_wait_mbox_idle(p50);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = p50_read_mbox_reg(p50, P50_MBOX_REG_STATUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret == P50_MBOX_STATUS_SUCCESS)
|
||||
return 0;
|
||||
|
||||
dev_err(p50->gc.parent, "Mbox command failed (CMD=0x%x STAT=0x%x PARAM=0x%x DATA=0x%x)\n",
|
||||
cmd, ret, param, data);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
||||
/* gpio routines */
|
||||
|
||||
static int p50_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
switch (offset) {
|
||||
case P50_GPIO_LINE_BTN:
|
||||
return GPIO_LINE_DIRECTION_IN;
|
||||
|
||||
case P50_GPIO_LINE_LED:
|
||||
return GPIO_LINE_DIRECTION_OUT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int p50_gpio_get(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct p50_gpio *p50 = gpiochip_get_data(gc);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&p50->lock);
|
||||
|
||||
ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_READ_GPIO, gpio_params[offset], 0);
|
||||
if (ret == 0)
|
||||
ret = p50_read_mbox_reg(p50, P50_MBOX_REG_DATA);
|
||||
|
||||
mutex_unlock(&p50->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void p50_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
|
||||
{
|
||||
struct p50_gpio *p50 = gpiochip_get_data(gc);
|
||||
|
||||
mutex_lock(&p50->lock);
|
||||
|
||||
p50_send_mbox_cmd(p50, P50_MBOX_CMD_WRITE_GPIO, gpio_params[offset], value);
|
||||
|
||||
mutex_unlock(&p50->lock);
|
||||
}
|
||||
|
||||
static int p50_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct p50_gpio *p50;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Cannot get I/O ports\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!devm_request_region(&pdev->dev, res->start, resource_size(res), pdev->name)) {
|
||||
dev_err(&pdev->dev, "Unable to reserve I/O region\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
p50 = devm_kzalloc(&pdev->dev, sizeof(*p50), GFP_KERNEL);
|
||||
if (!p50)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, p50);
|
||||
mutex_init(&p50->lock);
|
||||
p50->base = res->start;
|
||||
p50->gc.owner = THIS_MODULE;
|
||||
p50->gc.parent = &pdev->dev;
|
||||
p50->gc.label = dev_name(&pdev->dev);
|
||||
p50->gc.ngpio = ARRAY_SIZE(gpio_names);
|
||||
p50->gc.names = gpio_names;
|
||||
p50->gc.can_sleep = true;
|
||||
p50->gc.base = -1;
|
||||
p50->gc.get_direction = p50_gpio_get_direction;
|
||||
p50->gc.get = p50_gpio_get;
|
||||
p50->gc.set = p50_gpio_set;
|
||||
|
||||
|
||||
/* reset mbox */
|
||||
ret = p50_wait_mbox_idle(p50);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, P50_MBOX_CMD_CLEAR);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = p50_wait_mbox_idle(p50);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
||||
ret = devm_gpiochip_add_data(&pdev->dev, &p50->gc, p50);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Could not register gpiochip: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpiod_add_lookup_table(&p50_gpio_led_table);
|
||||
|
||||
p50->leds_pdev = platform_device_register_data(&pdev->dev,
|
||||
"leds-gpio", PLATFORM_DEVID_NONE, &leds_pdata, sizeof(leds_pdata));
|
||||
|
||||
if (IS_ERR(p50->leds_pdev)) {
|
||||
ret = PTR_ERR(p50->leds_pdev);
|
||||
dev_err(&pdev->dev, "Could not register leds-gpio: %d\n", ret);
|
||||
goto err_leds;
|
||||
}
|
||||
|
||||
/* gpio-keys-polled uses old-style gpio interface, pass the right identifier */
|
||||
buttons[0].gpio += p50->gc.base;
|
||||
|
||||
p50->keys_pdev =
|
||||
platform_device_register_data(&pdev->dev, "gpio-keys-polled",
|
||||
PLATFORM_DEVID_NONE,
|
||||
&keys_pdata, sizeof(keys_pdata));
|
||||
|
||||
if (IS_ERR(p50->keys_pdev)) {
|
||||
ret = PTR_ERR(p50->keys_pdev);
|
||||
dev_err(&pdev->dev, "Could not register gpio-keys-polled: %d\n", ret);
|
||||
goto err_keys;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_keys:
|
||||
platform_device_unregister(p50->leds_pdev);
|
||||
err_leds:
|
||||
gpiod_remove_lookup_table(&p50_gpio_led_table);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int p50_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct p50_gpio *p50 = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_unregister(p50->keys_pdev);
|
||||
platform_device_unregister(p50->leds_pdev);
|
||||
|
||||
gpiod_remove_lookup_table(&p50_gpio_led_table);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver p50_gpio_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
.probe = p50_gpio_probe,
|
||||
.remove = p50_gpio_remove,
|
||||
};
|
||||
|
||||
/* Board setup */
|
||||
static const struct dmi_system_id dmi_ids[] __initconst = {
|
||||
{
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Barco"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "P50")
|
||||
},
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(dmi, dmi_ids);
|
||||
|
||||
static int __init p50_module_init(void)
|
||||
{
|
||||
struct resource res = DEFINE_RES_IO(P50_GPIO_IO_PORT_BASE, P50_PORT_CMD + 1);
|
||||
|
||||
if (!dmi_first_match(dmi_ids))
|
||||
return -ENODEV;
|
||||
|
||||
platform_driver_register(&p50_gpio_driver);
|
||||
|
||||
gpio_pdev = platform_device_register_simple(DRIVER_NAME, PLATFORM_DEVID_NONE, &res, 1);
|
||||
if (IS_ERR(gpio_pdev)) {
|
||||
pr_err("failed registering %s: %ld\n", DRIVER_NAME, PTR_ERR(gpio_pdev));
|
||||
platform_driver_unregister(&p50_gpio_driver);
|
||||
return PTR_ERR(gpio_pdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit p50_module_exit(void)
|
||||
{
|
||||
platform_device_unregister(gpio_pdev);
|
||||
platform_driver_unregister(&p50_gpio_driver);
|
||||
}
|
||||
|
||||
module_init(p50_module_init);
|
||||
module_exit(p50_module_exit);
|
||||
|
||||
MODULE_AUTHOR("Santosh Kumar Yadav, Barco NV <santoshkumar.yadav@barco.com>");
|
||||
MODULE_DESCRIPTION("Barco P50 identify GPIOs driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -40,6 +40,7 @@ static bool wmi_requires_smbios_request;
|
||||
|
||||
struct dell_wmi_priv {
|
||||
struct input_dev *input_dev;
|
||||
struct input_dev *tabletswitch_dev;
|
||||
u32 interface_version;
|
||||
};
|
||||
|
||||
@ -309,6 +310,9 @@ static const struct key_entry dell_wmi_keymap_type_0010[] = {
|
||||
* Keymap for WMI events of type 0x0011
|
||||
*/
|
||||
static const struct key_entry dell_wmi_keymap_type_0011[] = {
|
||||
/* Reflex keyboard switch on 2n1 devices */
|
||||
{ KE_IGNORE, 0xe070, { KEY_RESERVED } },
|
||||
|
||||
/* Battery unplugged */
|
||||
{ KE_IGNORE, 0xfff0, { KEY_RESERVED } },
|
||||
|
||||
@ -340,21 +344,55 @@ static const struct key_entry dell_wmi_keymap_type_0011[] = {
|
||||
* They are events with extended data
|
||||
*/
|
||||
static const struct key_entry dell_wmi_keymap_type_0012[] = {
|
||||
/* Ultra-performance mode switch request */
|
||||
{ KE_IGNORE, 0x000d, { KEY_RESERVED } },
|
||||
|
||||
/* Fn-lock button pressed */
|
||||
{ KE_IGNORE, 0xe035, { KEY_RESERVED } },
|
||||
};
|
||||
|
||||
static void dell_wmi_process_key(struct wmi_device *wdev, int type, int code)
|
||||
static void dell_wmi_switch_event(struct input_dev **subdev,
|
||||
const char *devname,
|
||||
int switchid,
|
||||
int value)
|
||||
{
|
||||
if (!*subdev) {
|
||||
struct input_dev *dev = input_allocate_device();
|
||||
|
||||
if (!dev) {
|
||||
pr_warn("could not allocate device for %s\n", devname);
|
||||
return;
|
||||
}
|
||||
__set_bit(EV_SW, (dev)->evbit);
|
||||
__set_bit(switchid, (dev)->swbit);
|
||||
|
||||
(dev)->name = devname;
|
||||
(dev)->id.bustype = BUS_HOST;
|
||||
if (input_register_device(dev)) {
|
||||
input_free_device(dev);
|
||||
pr_warn("could not register device for %s\n", devname);
|
||||
return;
|
||||
}
|
||||
*subdev = dev;
|
||||
}
|
||||
|
||||
input_report_switch(*subdev, switchid, value);
|
||||
input_sync(*subdev);
|
||||
}
|
||||
|
||||
static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, u16 *buffer, int remaining)
|
||||
{
|
||||
struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
const struct key_entry *key;
|
||||
int used = 0;
|
||||
int value = 1;
|
||||
|
||||
key = sparse_keymap_entry_from_scancode(priv->input_dev,
|
||||
(type << 16) | code);
|
||||
if (!key) {
|
||||
pr_info("Unknown key with type 0x%04x and code 0x%04x pressed\n",
|
||||
type, code);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_debug("Key with type 0x%04x and code 0x%04x pressed\n", type, code);
|
||||
@ -363,16 +401,27 @@ static void dell_wmi_process_key(struct wmi_device *wdev, int type, int code)
|
||||
if ((key->keycode == KEY_BRIGHTNESSUP ||
|
||||
key->keycode == KEY_BRIGHTNESSDOWN) &&
|
||||
acpi_video_handles_brightness_key_presses())
|
||||
return;
|
||||
return 0;
|
||||
|
||||
if (type == 0x0000 && code == 0xe025 && !wmi_requires_smbios_request)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
if (key->keycode == KEY_KBDILLUMTOGGLE)
|
||||
if (key->keycode == KEY_KBDILLUMTOGGLE) {
|
||||
dell_laptop_call_notifier(
|
||||
DELL_LAPTOP_KBD_BACKLIGHT_BRIGHTNESS_CHANGED, NULL);
|
||||
} else if (type == 0x0011 && code == 0xe070 && remaining > 0) {
|
||||
dell_wmi_switch_event(&priv->tabletswitch_dev,
|
||||
"Dell tablet mode switch",
|
||||
SW_TABLET_MODE, !buffer[0]);
|
||||
return 1;
|
||||
} else if (type == 0x0012 && code == 0x000d && remaining > 0) {
|
||||
value = (buffer[2] == 2);
|
||||
used = 1;
|
||||
}
|
||||
|
||||
sparse_keymap_report_entry(priv->input_dev, key, 1, true);
|
||||
sparse_keymap_report_entry(priv->input_dev, key, value, true);
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
static void dell_wmi_notify(struct wmi_device *wdev,
|
||||
@ -430,21 +479,26 @@ static void dell_wmi_notify(struct wmi_device *wdev,
|
||||
case 0x0000: /* One key pressed or event occurred */
|
||||
if (len > 2)
|
||||
dell_wmi_process_key(wdev, buffer_entry[1],
|
||||
buffer_entry[2]);
|
||||
buffer_entry[2],
|
||||
buffer_entry + 3,
|
||||
len - 3);
|
||||
/* Extended data is currently ignored */
|
||||
break;
|
||||
case 0x0010: /* Sequence of keys pressed */
|
||||
case 0x0011: /* Sequence of events occurred */
|
||||
for (i = 2; i < len; ++i)
|
||||
dell_wmi_process_key(wdev, buffer_entry[1],
|
||||
buffer_entry[i]);
|
||||
i += dell_wmi_process_key(wdev, buffer_entry[1],
|
||||
buffer_entry[i],
|
||||
buffer_entry + i,
|
||||
len - i - 1);
|
||||
break;
|
||||
case 0x0012:
|
||||
if ((len > 4) && dell_privacy_process_event(buffer_entry[1], buffer_entry[3],
|
||||
buffer_entry[4]))
|
||||
/* dell_privacy_process_event has handled the event */;
|
||||
else if (len > 2)
|
||||
dell_wmi_process_key(wdev, buffer_entry[1], buffer_entry[2]);
|
||||
dell_wmi_process_key(wdev, buffer_entry[1], buffer_entry[2],
|
||||
buffer_entry + 3, len - 3);
|
||||
break;
|
||||
default: /* Unknown event */
|
||||
pr_info("Unknown WMI event type 0x%x\n",
|
||||
@ -661,6 +715,8 @@ static void dell_wmi_input_destroy(struct wmi_device *wdev)
|
||||
struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
|
||||
input_unregister_device(priv->input_dev);
|
||||
if (priv->tabletswitch_dev)
|
||||
input_unregister_device(priv->tabletswitch_dev);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -22,9 +22,11 @@
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_profile.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/dmi.h>
|
||||
|
||||
MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
|
||||
MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
|
||||
@ -39,6 +41,25 @@ MODULE_PARM_DESC(enable_tablet_mode_sw, "Enable SW_TABLET_MODE reporting (-1=aut
|
||||
|
||||
#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
|
||||
#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
|
||||
#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95
|
||||
|
||||
/* DMI board names of devices that should use the omen specific path for
|
||||
* thermal profiles.
|
||||
* This was obtained by taking a look in the windows omen command center
|
||||
* app and parsing a json file that they use to figure out what capabilities
|
||||
* the device should have.
|
||||
* A device is considered an omen if the DisplayName in that list contains
|
||||
* "OMEN", and it can use the thermal profile stuff if the "Feature" array
|
||||
* contains "PerformanceControl".
|
||||
*/
|
||||
static const char * const omen_thermal_profile_boards[] = {
|
||||
"84DA", "84DB", "84DC", "8574", "8575", "860A", "87B5", "8572", "8573",
|
||||
"8600", "8601", "8602", "8605", "8606", "8607", "8746", "8747", "8749",
|
||||
"874A", "8603", "8604", "8748", "886B", "886C", "878A", "878B", "878C",
|
||||
"88C8", "88CB", "8786", "8787", "8788", "88D1", "88D2", "88F4", "88FD",
|
||||
"88F5", "88F6", "88F7", "88FE", "88FF", "8900", "8901", "8902", "8912",
|
||||
"8917", "8918", "8949", "894A", "89EB"
|
||||
};
|
||||
|
||||
enum hp_wmi_radio {
|
||||
HPWMI_WIFI = 0x0,
|
||||
@ -89,10 +110,18 @@ enum hp_wmi_commandtype {
|
||||
HPWMI_THERMAL_PROFILE_QUERY = 0x4c,
|
||||
};
|
||||
|
||||
enum hp_wmi_gm_commandtype {
|
||||
HPWMI_FAN_SPEED_GET_QUERY = 0x11,
|
||||
HPWMI_SET_PERFORMANCE_MODE = 0x1A,
|
||||
HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26,
|
||||
HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27,
|
||||
};
|
||||
|
||||
enum hp_wmi_command {
|
||||
HPWMI_READ = 0x01,
|
||||
HPWMI_WRITE = 0x02,
|
||||
HPWMI_ODM = 0x03,
|
||||
HPWMI_GM = 0x20008,
|
||||
};
|
||||
|
||||
enum hp_wmi_hardware_mask {
|
||||
@ -120,6 +149,12 @@ enum hp_wireless2_bits {
|
||||
HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD,
|
||||
};
|
||||
|
||||
enum hp_thermal_profile_omen {
|
||||
HP_OMEN_THERMAL_PROFILE_DEFAULT = 0x00,
|
||||
HP_OMEN_THERMAL_PROFILE_PERFORMANCE = 0x01,
|
||||
HP_OMEN_THERMAL_PROFILE_COOL = 0x02,
|
||||
};
|
||||
|
||||
enum hp_thermal_profile {
|
||||
HP_THERMAL_PROFILE_PERFORMANCE = 0x00,
|
||||
HP_THERMAL_PROFILE_DEFAULT = 0x01,
|
||||
@ -279,6 +314,24 @@ out_free:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hp_wmi_get_fan_speed(int fan)
|
||||
{
|
||||
u8 fsh, fsl;
|
||||
char fan_data[4] = { fan, 0, 0, 0 };
|
||||
|
||||
int ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_GET_QUERY, HPWMI_GM,
|
||||
&fan_data, sizeof(fan_data),
|
||||
sizeof(fan_data));
|
||||
|
||||
if (ret != 0)
|
||||
return -EINVAL;
|
||||
|
||||
fsh = fan_data[2];
|
||||
fsl = fan_data[3];
|
||||
|
||||
return (fsh << 8) | fsl;
|
||||
}
|
||||
|
||||
static int hp_wmi_read_int(int query)
|
||||
{
|
||||
int val = 0, ret;
|
||||
@ -302,6 +355,73 @@ static int hp_wmi_hw_state(int mask)
|
||||
return !!(state & mask);
|
||||
}
|
||||
|
||||
static int omen_thermal_profile_set(int mode)
|
||||
{
|
||||
char buffer[2] = {0, mode};
|
||||
int ret;
|
||||
|
||||
if (mode < 0 || mode > 2)
|
||||
return -EINVAL;
|
||||
|
||||
ret = hp_wmi_perform_query(HPWMI_SET_PERFORMANCE_MODE, HPWMI_GM,
|
||||
&buffer, sizeof(buffer), sizeof(buffer));
|
||||
|
||||
if (ret)
|
||||
return ret < 0 ? ret : -EINVAL;
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static bool is_omen_thermal_profile(void)
|
||||
{
|
||||
const char *board_name = dmi_get_system_info(DMI_BOARD_NAME);
|
||||
|
||||
if (!board_name)
|
||||
return false;
|
||||
|
||||
return match_string(omen_thermal_profile_boards,
|
||||
ARRAY_SIZE(omen_thermal_profile_boards),
|
||||
board_name) >= 0;
|
||||
}
|
||||
|
||||
static int omen_thermal_profile_get(void)
|
||||
{
|
||||
u8 data;
|
||||
|
||||
int ret = ec_read(HP_OMEN_EC_THERMAL_PROFILE_OFFSET, &data);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int hp_wmi_fan_speed_max_set(int enabled)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_SET_QUERY, HPWMI_GM,
|
||||
&enabled, sizeof(enabled), sizeof(enabled));
|
||||
|
||||
if (ret)
|
||||
return ret < 0 ? ret : -EINVAL;
|
||||
|
||||
return enabled;
|
||||
}
|
||||
|
||||
static int hp_wmi_fan_speed_max_get(void)
|
||||
{
|
||||
int val = 0, ret;
|
||||
|
||||
ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_GET_QUERY, HPWMI_GM,
|
||||
&val, sizeof(val), sizeof(val));
|
||||
|
||||
if (ret)
|
||||
return ret < 0 ? ret : -EINVAL;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int __init hp_wmi_bios_2008_later(void)
|
||||
{
|
||||
int state = 0;
|
||||
@ -878,6 +998,58 @@ fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int platform_profile_omen_get(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
int tp;
|
||||
|
||||
tp = omen_thermal_profile_get();
|
||||
if (tp < 0)
|
||||
return tp;
|
||||
|
||||
switch (tp) {
|
||||
case HP_OMEN_THERMAL_PROFILE_PERFORMANCE:
|
||||
*profile = PLATFORM_PROFILE_PERFORMANCE;
|
||||
break;
|
||||
case HP_OMEN_THERMAL_PROFILE_DEFAULT:
|
||||
*profile = PLATFORM_PROFILE_BALANCED;
|
||||
break;
|
||||
case HP_OMEN_THERMAL_PROFILE_COOL:
|
||||
*profile = PLATFORM_PROFILE_COOL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int platform_profile_omen_set(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
int err, tp;
|
||||
|
||||
switch (profile) {
|
||||
case PLATFORM_PROFILE_PERFORMANCE:
|
||||
tp = HP_OMEN_THERMAL_PROFILE_PERFORMANCE;
|
||||
break;
|
||||
case PLATFORM_PROFILE_BALANCED:
|
||||
tp = HP_OMEN_THERMAL_PROFILE_DEFAULT;
|
||||
break;
|
||||
case PLATFORM_PROFILE_COOL:
|
||||
tp = HP_OMEN_THERMAL_PROFILE_COOL;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
err = omen_thermal_profile_set(tp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_profile_get(void)
|
||||
{
|
||||
return hp_wmi_read_int(HPWMI_THERMAL_PROFILE_QUERY);
|
||||
@ -889,8 +1061,8 @@ static int thermal_profile_set(int thermal_profile)
|
||||
sizeof(thermal_profile), 0);
|
||||
}
|
||||
|
||||
static int platform_profile_get(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option *profile)
|
||||
static int hp_wmi_platform_profile_get(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
int tp;
|
||||
|
||||
@ -915,8 +1087,8 @@ static int platform_profile_get(struct platform_profile_handler *pprof,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int platform_profile_set(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option profile)
|
||||
static int hp_wmi_platform_profile_set(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
int err, tp;
|
||||
|
||||
@ -945,20 +1117,39 @@ static int thermal_profile_setup(void)
|
||||
{
|
||||
int err, tp;
|
||||
|
||||
tp = thermal_profile_get();
|
||||
if (tp < 0)
|
||||
return tp;
|
||||
if (is_omen_thermal_profile()) {
|
||||
tp = omen_thermal_profile_get();
|
||||
if (tp < 0)
|
||||
return tp;
|
||||
|
||||
/*
|
||||
* call thermal profile write command to ensure that the firmware correctly
|
||||
* sets the OEM variables for the DPTF
|
||||
*/
|
||||
err = thermal_profile_set(tp);
|
||||
if (err)
|
||||
return err;
|
||||
/*
|
||||
* call thermal profile write command to ensure that the
|
||||
* firmware correctly sets the OEM variables
|
||||
*/
|
||||
|
||||
platform_profile_handler.profile_get = platform_profile_get,
|
||||
platform_profile_handler.profile_set = platform_profile_set,
|
||||
err = omen_thermal_profile_set(tp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
platform_profile_handler.profile_get = platform_profile_omen_get;
|
||||
platform_profile_handler.profile_set = platform_profile_omen_set;
|
||||
} else {
|
||||
tp = thermal_profile_get();
|
||||
|
||||
if (tp < 0)
|
||||
return tp;
|
||||
|
||||
/*
|
||||
* call thermal profile write command to ensure that the
|
||||
* firmware correctly sets the OEM variables for the DPTF
|
||||
*/
|
||||
err = thermal_profile_set(tp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
platform_profile_handler.profile_get = hp_wmi_platform_profile_get;
|
||||
platform_profile_handler.profile_set = hp_wmi_platform_profile_set;
|
||||
}
|
||||
|
||||
set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, platform_profile_handler.choices);
|
||||
@ -973,8 +1164,11 @@ static int thermal_profile_setup(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hp_wmi_hwmon_init(void);
|
||||
|
||||
static int __init hp_wmi_bios_setup(struct platform_device *device)
|
||||
{
|
||||
int err;
|
||||
/* clear detected rfkill devices */
|
||||
wifi_rfkill = NULL;
|
||||
bluetooth_rfkill = NULL;
|
||||
@ -984,6 +1178,11 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
|
||||
if (hp_wmi_rfkill_setup(device))
|
||||
hp_wmi_rfkill2_setup(device);
|
||||
|
||||
err = hp_wmi_hwmon_init();
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
thermal_profile_setup();
|
||||
|
||||
return 0;
|
||||
@ -1068,6 +1267,112 @@ static struct platform_driver hp_wmi_driver = {
|
||||
.remove = __exit_p(hp_wmi_bios_remove),
|
||||
};
|
||||
|
||||
static umode_t hp_wmi_hwmon_is_visible(const void *data,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
switch (type) {
|
||||
case hwmon_pwm:
|
||||
return 0644;
|
||||
case hwmon_fan:
|
||||
if (hp_wmi_get_fan_speed(channel) >= 0)
|
||||
return 0444;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_fan:
|
||||
ret = hp_wmi_get_fan_speed(channel);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return 0;
|
||||
case hwmon_pwm:
|
||||
switch (hp_wmi_fan_speed_max_get()) {
|
||||
case 0:
|
||||
/* 0 is automatic fan, which is 2 for hwmon */
|
||||
*val = 2;
|
||||
return 0;
|
||||
case 1:
|
||||
/* 1 is max fan, which is 0
|
||||
* (no fan speed control) for hwmon
|
||||
*/
|
||||
*val = 0;
|
||||
return 0;
|
||||
default:
|
||||
/* shouldn't happen */
|
||||
return -ENODATA;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long val)
|
||||
{
|
||||
switch (type) {
|
||||
case hwmon_pwm:
|
||||
switch (val) {
|
||||
case 0:
|
||||
/* 0 is no fan speed control (max), which is 1 for us */
|
||||
return hp_wmi_fan_speed_max_set(1);
|
||||
case 2:
|
||||
/* 2 is automatic speed control, which is 0 for us */
|
||||
return hp_wmi_fan_speed_max_set(0);
|
||||
default:
|
||||
/* we don't support manual fan speed control */
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct hwmon_channel_info *info[] = {
|
||||
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT),
|
||||
HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_ops ops = {
|
||||
.is_visible = hp_wmi_hwmon_is_visible,
|
||||
.read = hp_wmi_hwmon_read,
|
||||
.write = hp_wmi_hwmon_write,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info chip_info = {
|
||||
.ops = &ops,
|
||||
.info = info,
|
||||
};
|
||||
|
||||
static int hp_wmi_hwmon_init(void)
|
||||
{
|
||||
struct device *dev = &hp_wmi_platform_dev->dev;
|
||||
struct device *hwmon;
|
||||
|
||||
hwmon = devm_hwmon_device_register_with_info(dev, "hp", &hp_wmi_driver,
|
||||
&chip_info, NULL);
|
||||
|
||||
if (IS_ERR(hwmon)) {
|
||||
dev_err(dev, "Could not register hp hwmon device\n");
|
||||
return PTR_ERR(hwmon);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init hp_wmi_init(void)
|
||||
{
|
||||
int event_capable = wmi_has_guid(HPWMI_EVENT_GUID);
|
||||
|
@ -868,6 +868,18 @@ static void dytc_profile_refresh(struct ideapad_private *priv)
|
||||
}
|
||||
}
|
||||
|
||||
static const struct dmi_system_id ideapad_dytc_v4_allow_table[] = {
|
||||
{
|
||||
/* Ideapad 5 Pro 16ACH6 */
|
||||
.ident = "LENOVO 82L5",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "82L5")
|
||||
}
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static int ideapad_dytc_profile_init(struct ideapad_private *priv)
|
||||
{
|
||||
int err, dytc_version;
|
||||
@ -882,12 +894,21 @@ static int ideapad_dytc_profile_init(struct ideapad_private *priv)
|
||||
return err;
|
||||
|
||||
/* Check DYTC is enabled and supports mode setting */
|
||||
if (!test_bit(DYTC_QUERY_ENABLE_BIT, &output))
|
||||
if (!test_bit(DYTC_QUERY_ENABLE_BIT, &output)) {
|
||||
dev_info(&priv->platform_device->dev, "DYTC_QUERY_ENABLE_BIT returned false\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF;
|
||||
if (dytc_version < 5)
|
||||
return -ENODEV;
|
||||
|
||||
if (dytc_version < 5) {
|
||||
if (dytc_version < 4 || !dmi_check_system(ideapad_dytc_v4_allow_table)) {
|
||||
dev_info(&priv->platform_device->dev,
|
||||
"DYTC_VERSION is less than 4 or is not allowed: %d\n",
|
||||
dytc_version);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
priv->dytc = kzalloc(sizeof(*priv->dytc), GFP_KERNEL);
|
||||
if (!priv->dytc)
|
||||
@ -1534,17 +1555,13 @@ static void ideapad_check_features(struct ideapad_private *priv)
|
||||
|
||||
static int ideapad_acpi_add(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
|
||||
struct ideapad_private *priv;
|
||||
struct acpi_device *adev;
|
||||
acpi_status status;
|
||||
unsigned long cfg;
|
||||
int err, i;
|
||||
|
||||
err = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev);
|
||||
if (err)
|
||||
return -ENODEV;
|
||||
|
||||
if (eval_int(adev->handle, "_CFG", &cfg))
|
||||
if (!adev || eval_int(adev->handle, "_CFG", &cfg))
|
||||
return -ENODEV;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
|
@ -102,6 +102,22 @@ config INTEL_CHTDC_TI_PWRBTN
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called intel_chtdc_ti_pwrbtn.
|
||||
|
||||
config INTEL_ISHTP_ECLITE
|
||||
tristate "Intel ISHTP eclite controller Driver"
|
||||
depends on INTEL_ISH_HID
|
||||
depends on ACPI
|
||||
help
|
||||
This driver is for accessing the PSE (Programmable Service Engine) -
|
||||
an Embedded Controller like IP - using ISHTP (Integrated Sensor Hub
|
||||
Transport Protocol) to get battery, thermal and UCSI (USB Type-C
|
||||
Connector System Software Interface) related data from the platform.
|
||||
Users who don't want to use discrete Embedded Controller on Intel's
|
||||
Elkhartlake platform can leverage this integrated solution of
|
||||
ECLite which is part of PSE subsystem.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called intel_ishtp_eclite.
|
||||
|
||||
config INTEL_MRFLD_PWRBTN
|
||||
tristate "Intel Merrifield Basin Cove power button driver"
|
||||
depends on INTEL_SOC_PMIC_MRFLD
|
||||
|
@ -21,6 +21,7 @@ intel-vbtn-y := vbtn.o
|
||||
obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o
|
||||
|
||||
# Intel miscellaneous drivers
|
||||
obj-$(CONFIG_INTEL_ISHTP_ECLITE) += ishtp_eclite.o
|
||||
intel_int0002_vgpio-y := int0002_vgpio.o
|
||||
obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o
|
||||
intel_oaktrail-y := oaktrail.o
|
||||
|
@ -34,13 +34,11 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_data/x86/soc.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
|
||||
#define DRV_NAME "INT0002 Virtual GPIO"
|
||||
|
||||
/* For some reason the virtual GPIO pin tied to the GPE is numbered pin 2 */
|
||||
@ -151,12 +149,6 @@ static struct irq_chip int0002_irqchip = {
|
||||
.irq_set_wake = int0002_irq_set_wake,
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id int0002_cpu_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT, NULL),
|
||||
{}
|
||||
};
|
||||
|
||||
static void int0002_init_irq_valid_mask(struct gpio_chip *chip,
|
||||
unsigned long *valid_mask,
|
||||
unsigned int ngpios)
|
||||
@ -167,15 +159,13 @@ static void int0002_init_irq_valid_mask(struct gpio_chip *chip,
|
||||
static int int0002_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct x86_cpu_id *cpu_id;
|
||||
struct int0002_data *int0002;
|
||||
struct gpio_irq_chip *girq;
|
||||
struct gpio_chip *chip;
|
||||
int irq, ret;
|
||||
|
||||
/* Menlow has a different INT0002 device? <sigh> */
|
||||
cpu_id = x86_match_cpu(int0002_cpu_ids);
|
||||
if (!cpu_id)
|
||||
if (!soc_intel_is_byt() && !soc_intel_is_cht())
|
||||
return -ENODEV;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
|
701
drivers/platform/x86/intel/ishtp_eclite.c
Normal file
701
drivers/platform/x86/intel/ishtp_eclite.c
Normal file
@ -0,0 +1,701 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Intel ECLite opregion driver for talking to ECLite firmware running on
|
||||
* Intel Integrated Sensor Hub (ISH) using ISH Transport Protocol (ISHTP)
|
||||
*
|
||||
* Copyright (c) 2021, Intel Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/intel-ish-client-if.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define ECLITE_DATA_OPREGION_ID 0x9E
|
||||
#define ECLITE_CMD_OPREGION_ID 0x9F
|
||||
|
||||
#define ECL_MSG_DATA 0x1
|
||||
#define ECL_MSG_EVENT 0x2
|
||||
|
||||
#define ECL_ISH_READ 0x1
|
||||
#define ECL_ISH_WRITE 0x2
|
||||
#define ECL_ISH_HEADER_VERSION 0
|
||||
|
||||
#define ECL_CL_RX_RING_SIZE 16
|
||||
#define ECL_CL_TX_RING_SIZE 8
|
||||
|
||||
#define ECL_DATA_OPR_BUFLEN 384
|
||||
#define ECL_EVENTS_NOTIFY 333
|
||||
|
||||
#define cmd_opr_offsetof(element) offsetof(struct opregion_cmd, element)
|
||||
#define cl_data_to_dev(opr_dev) ishtp_device((opr_dev)->cl_device)
|
||||
|
||||
#ifndef BITS_TO_BYTES
|
||||
#define BITS_TO_BYTES(x) ((x) / 8)
|
||||
#endif
|
||||
|
||||
struct opregion_cmd {
|
||||
unsigned int command;
|
||||
unsigned int offset;
|
||||
unsigned int length;
|
||||
unsigned int event_id;
|
||||
};
|
||||
|
||||
struct opregion_data {
|
||||
char data[ECL_DATA_OPR_BUFLEN];
|
||||
};
|
||||
|
||||
struct opregion_context {
|
||||
struct opregion_cmd cmd_area;
|
||||
struct opregion_data data_area;
|
||||
};
|
||||
|
||||
struct ecl_message_header {
|
||||
unsigned int version:2;
|
||||
unsigned int data_type:2;
|
||||
unsigned int request_type:2;
|
||||
unsigned int offset:9;
|
||||
unsigned int data_len:9;
|
||||
unsigned int event:8;
|
||||
};
|
||||
|
||||
struct ecl_message {
|
||||
struct ecl_message_header header;
|
||||
char payload[ECL_DATA_OPR_BUFLEN];
|
||||
};
|
||||
|
||||
struct ishtp_opregion_dev {
|
||||
struct opregion_context opr_context;
|
||||
struct ishtp_cl *ecl_ishtp_cl;
|
||||
struct ishtp_cl_device *cl_device;
|
||||
struct ishtp_fw_client *fw_client;
|
||||
struct ishtp_cl_rb *rb;
|
||||
struct acpi_device *adev;
|
||||
unsigned int dsm_event_id;
|
||||
unsigned int ish_link_ready;
|
||||
unsigned int ish_read_done;
|
||||
unsigned int acpi_init_done;
|
||||
wait_queue_head_t read_wait;
|
||||
struct work_struct event_work;
|
||||
struct work_struct reset_work;
|
||||
/* lock for opregion context */
|
||||
struct mutex lock;
|
||||
|
||||
};
|
||||
|
||||
/* eclite ishtp client UUID: 6a19cc4b-d760-4de3-b14d-f25ebd0fbcd9 */
|
||||
static const guid_t ecl_ishtp_guid =
|
||||
GUID_INIT(0x6a19cc4b, 0xd760, 0x4de3,
|
||||
0xb1, 0x4d, 0xf2, 0x5e, 0xbd, 0xf, 0xbc, 0xd9);
|
||||
|
||||
/* ACPI DSM UUID: 91d936a7-1f01-49c6-a6b4-72f00ad8d8a5 */
|
||||
static const guid_t ecl_acpi_guid =
|
||||
GUID_INIT(0x91d936a7, 0x1f01, 0x49c6, 0xa6,
|
||||
0xb4, 0x72, 0xf0, 0x0a, 0xd8, 0xd8, 0xa5);
|
||||
|
||||
/**
|
||||
* ecl_ish_cl_read() - Read data from eclite FW
|
||||
*
|
||||
* @opr_dev: pointer to opregion device
|
||||
*
|
||||
* This function issues a read request to eclite FW and waits until it
|
||||
* receives a response. When response is received the read data is copied to
|
||||
* opregion buffer.
|
||||
*/
|
||||
static int ecl_ish_cl_read(struct ishtp_opregion_dev *opr_dev)
|
||||
{
|
||||
struct ecl_message_header header;
|
||||
int len, rv;
|
||||
|
||||
if (!opr_dev->ish_link_ready)
|
||||
return -EIO;
|
||||
|
||||
if ((opr_dev->opr_context.cmd_area.offset +
|
||||
opr_dev->opr_context.cmd_area.length) > ECL_DATA_OPR_BUFLEN) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
header.version = ECL_ISH_HEADER_VERSION;
|
||||
header.data_type = ECL_MSG_DATA;
|
||||
header.request_type = ECL_ISH_READ;
|
||||
header.offset = opr_dev->opr_context.cmd_area.offset;
|
||||
header.data_len = opr_dev->opr_context.cmd_area.length;
|
||||
header.event = opr_dev->opr_context.cmd_area.event_id;
|
||||
len = sizeof(header);
|
||||
|
||||
opr_dev->ish_read_done = false;
|
||||
rv = ishtp_cl_send(opr_dev->ecl_ishtp_cl, (uint8_t *)&header, len);
|
||||
if (rv) {
|
||||
dev_err(cl_data_to_dev(opr_dev), "ish-read : send failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dev_dbg(cl_data_to_dev(opr_dev),
|
||||
"[ish_rd] Req: off : %x, len : %x\n",
|
||||
header.offset,
|
||||
header.data_len);
|
||||
|
||||
rv = wait_event_interruptible_timeout(opr_dev->read_wait,
|
||||
opr_dev->ish_read_done,
|
||||
2 * HZ);
|
||||
if (!rv) {
|
||||
dev_err(cl_data_to_dev(opr_dev),
|
||||
"[ish_rd] No response from firmware\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecl_ish_cl_write() - This function writes data to eclite FW.
|
||||
*
|
||||
* @opr_dev: pointer to opregion device
|
||||
*
|
||||
* This function writes data to eclite FW.
|
||||
*/
|
||||
static int ecl_ish_cl_write(struct ishtp_opregion_dev *opr_dev)
|
||||
{
|
||||
struct ecl_message message;
|
||||
int len;
|
||||
|
||||
if (!opr_dev->ish_link_ready)
|
||||
return -EIO;
|
||||
|
||||
if ((opr_dev->opr_context.cmd_area.offset +
|
||||
opr_dev->opr_context.cmd_area.length) > ECL_DATA_OPR_BUFLEN) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
message.header.version = ECL_ISH_HEADER_VERSION;
|
||||
message.header.data_type = ECL_MSG_DATA;
|
||||
message.header.request_type = ECL_ISH_WRITE;
|
||||
message.header.offset = opr_dev->opr_context.cmd_area.offset;
|
||||
message.header.data_len = opr_dev->opr_context.cmd_area.length;
|
||||
message.header.event = opr_dev->opr_context.cmd_area.event_id;
|
||||
len = sizeof(struct ecl_message_header) + message.header.data_len;
|
||||
|
||||
memcpy(message.payload,
|
||||
opr_dev->opr_context.data_area.data + message.header.offset,
|
||||
message.header.data_len);
|
||||
|
||||
dev_dbg(cl_data_to_dev(opr_dev),
|
||||
"[ish_wr] off : %x, len : %x\n",
|
||||
message.header.offset,
|
||||
message.header.data_len);
|
||||
|
||||
return ishtp_cl_send(opr_dev->ecl_ishtp_cl, (uint8_t *)&message, len);
|
||||
}
|
||||
|
||||
static acpi_status
|
||||
ecl_opregion_cmd_handler(u32 function, acpi_physical_address address,
|
||||
u32 bits, u64 *value64,
|
||||
void *handler_context, void *region_context)
|
||||
{
|
||||
struct ishtp_opregion_dev *opr_dev;
|
||||
struct opregion_cmd *cmd;
|
||||
acpi_status status = AE_OK;
|
||||
|
||||
if (!region_context || !value64)
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
if (function == ACPI_READ)
|
||||
return AE_ERROR;
|
||||
|
||||
opr_dev = (struct ishtp_opregion_dev *)region_context;
|
||||
|
||||
mutex_lock(&opr_dev->lock);
|
||||
|
||||
cmd = &opr_dev->opr_context.cmd_area;
|
||||
|
||||
switch (address) {
|
||||
case cmd_opr_offsetof(command):
|
||||
cmd->command = (u32)*value64;
|
||||
|
||||
if (cmd->command == ECL_ISH_READ)
|
||||
status = ecl_ish_cl_read(opr_dev);
|
||||
else if (cmd->command == ECL_ISH_WRITE)
|
||||
status = ecl_ish_cl_write(opr_dev);
|
||||
else
|
||||
status = AE_ERROR;
|
||||
break;
|
||||
case cmd_opr_offsetof(offset):
|
||||
cmd->offset = (u32)*value64;
|
||||
break;
|
||||
case cmd_opr_offsetof(length):
|
||||
cmd->length = (u32)*value64;
|
||||
break;
|
||||
case cmd_opr_offsetof(event_id):
|
||||
cmd->event_id = (u32)*value64;
|
||||
break;
|
||||
default:
|
||||
status = AE_ERROR;
|
||||
}
|
||||
|
||||
mutex_unlock(&opr_dev->lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static acpi_status
|
||||
ecl_opregion_data_handler(u32 function, acpi_physical_address address,
|
||||
u32 bits, u64 *value64,
|
||||
void *handler_context, void *region_context)
|
||||
{
|
||||
struct ishtp_opregion_dev *opr_dev;
|
||||
unsigned int bytes = BITS_TO_BYTES(bits);
|
||||
void *data_addr;
|
||||
|
||||
if (!region_context || !value64)
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
if (address + bytes > ECL_DATA_OPR_BUFLEN)
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
opr_dev = (struct ishtp_opregion_dev *)region_context;
|
||||
|
||||
mutex_lock(&opr_dev->lock);
|
||||
|
||||
data_addr = &opr_dev->opr_context.data_area.data[address];
|
||||
|
||||
if (function == ACPI_READ) {
|
||||
memcpy(value64, data_addr, bytes);
|
||||
} else if (function == ACPI_WRITE) {
|
||||
memcpy(data_addr, value64, bytes);
|
||||
} else {
|
||||
mutex_unlock(&opr_dev->lock);
|
||||
return AE_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
mutex_unlock(&opr_dev->lock);
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static int acpi_find_eclite_device(struct ishtp_opregion_dev *opr_dev)
|
||||
{
|
||||
struct acpi_device *adev;
|
||||
|
||||
/* Find ECLite device and save reference */
|
||||
adev = acpi_dev_get_first_match_dev("INTC1035", NULL, -1);
|
||||
if (!adev) {
|
||||
dev_err(cl_data_to_dev(opr_dev), "eclite ACPI device not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
opr_dev->adev = adev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acpi_opregion_init(struct ishtp_opregion_dev *opr_dev)
|
||||
{
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_install_address_space_handler(opr_dev->adev->handle,
|
||||
ECLITE_CMD_OPREGION_ID,
|
||||
ecl_opregion_cmd_handler,
|
||||
NULL, opr_dev);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_err(cl_data_to_dev(opr_dev),
|
||||
"cmd space handler install failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
status = acpi_install_address_space_handler(opr_dev->adev->handle,
|
||||
ECLITE_DATA_OPREGION_ID,
|
||||
ecl_opregion_data_handler,
|
||||
NULL, opr_dev);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_err(cl_data_to_dev(opr_dev),
|
||||
"data space handler install failed\n");
|
||||
|
||||
acpi_remove_address_space_handler(opr_dev->adev->handle,
|
||||
ECLITE_CMD_OPREGION_ID,
|
||||
ecl_opregion_cmd_handler);
|
||||
return -ENODEV;
|
||||
}
|
||||
opr_dev->acpi_init_done = true;
|
||||
|
||||
dev_dbg(cl_data_to_dev(opr_dev), "Opregion handlers are installed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acpi_opregion_deinit(struct ishtp_opregion_dev *opr_dev)
|
||||
{
|
||||
acpi_remove_address_space_handler(opr_dev->adev->handle,
|
||||
ECLITE_CMD_OPREGION_ID,
|
||||
ecl_opregion_cmd_handler);
|
||||
|
||||
acpi_remove_address_space_handler(opr_dev->adev->handle,
|
||||
ECLITE_DATA_OPREGION_ID,
|
||||
ecl_opregion_data_handler);
|
||||
opr_dev->acpi_init_done = false;
|
||||
}
|
||||
|
||||
static void ecl_acpi_invoke_dsm(struct work_struct *work)
|
||||
{
|
||||
struct ishtp_opregion_dev *opr_dev;
|
||||
union acpi_object *obj;
|
||||
|
||||
opr_dev = container_of(work, struct ishtp_opregion_dev, event_work);
|
||||
if (!opr_dev->acpi_init_done)
|
||||
return;
|
||||
|
||||
obj = acpi_evaluate_dsm(opr_dev->adev->handle, &ecl_acpi_guid, 0,
|
||||
opr_dev->dsm_event_id, NULL);
|
||||
if (!obj) {
|
||||
dev_warn(cl_data_to_dev(opr_dev), "_DSM fn call failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(cl_data_to_dev(opr_dev), "Exec DSM function code: %d success\n",
|
||||
opr_dev->dsm_event_id);
|
||||
|
||||
ACPI_FREE(obj);
|
||||
}
|
||||
|
||||
static void ecl_ish_process_rx_data(struct ishtp_opregion_dev *opr_dev)
|
||||
{
|
||||
struct ecl_message *message =
|
||||
(struct ecl_message *)opr_dev->rb->buffer.data;
|
||||
|
||||
dev_dbg(cl_data_to_dev(opr_dev),
|
||||
"[ish_rd] Resp: off : %x, len : %x\n",
|
||||
message->header.offset,
|
||||
message->header.data_len);
|
||||
|
||||
if ((message->header.offset + message->header.data_len) >
|
||||
ECL_DATA_OPR_BUFLEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(opr_dev->opr_context.data_area.data + message->header.offset,
|
||||
message->payload, message->header.data_len);
|
||||
|
||||
opr_dev->ish_read_done = true;
|
||||
wake_up_interruptible(&opr_dev->read_wait);
|
||||
}
|
||||
|
||||
static void ecl_ish_process_rx_event(struct ishtp_opregion_dev *opr_dev)
|
||||
{
|
||||
struct ecl_message_header *header =
|
||||
(struct ecl_message_header *)opr_dev->rb->buffer.data;
|
||||
|
||||
dev_dbg(cl_data_to_dev(opr_dev),
|
||||
"[ish_ev] Evt received: %8x\n", header->event);
|
||||
|
||||
opr_dev->dsm_event_id = header->event;
|
||||
schedule_work(&opr_dev->event_work);
|
||||
}
|
||||
|
||||
static int ecl_ish_cl_enable_events(struct ishtp_opregion_dev *opr_dev,
|
||||
bool config_enable)
|
||||
{
|
||||
struct ecl_message message;
|
||||
int len;
|
||||
|
||||
message.header.version = ECL_ISH_HEADER_VERSION;
|
||||
message.header.data_type = ECL_MSG_DATA;
|
||||
message.header.request_type = ECL_ISH_WRITE;
|
||||
message.header.offset = ECL_EVENTS_NOTIFY;
|
||||
message.header.data_len = 1;
|
||||
message.payload[0] = config_enable;
|
||||
|
||||
len = sizeof(struct ecl_message_header) + message.header.data_len;
|
||||
|
||||
return ishtp_cl_send(opr_dev->ecl_ishtp_cl, (uint8_t *)&message, len);
|
||||
}
|
||||
|
||||
static void ecl_ishtp_cl_event_cb(struct ishtp_cl_device *cl_device)
|
||||
{
|
||||
struct ishtp_cl *ecl_ishtp_cl = ishtp_get_drvdata(cl_device);
|
||||
struct ishtp_opregion_dev *opr_dev;
|
||||
struct ecl_message_header *header;
|
||||
struct ishtp_cl_rb *rb;
|
||||
|
||||
opr_dev = ishtp_get_client_data(ecl_ishtp_cl);
|
||||
while ((rb = ishtp_cl_rx_get_rb(opr_dev->ecl_ishtp_cl)) != NULL) {
|
||||
opr_dev->rb = rb;
|
||||
header = (struct ecl_message_header *)rb->buffer.data;
|
||||
|
||||
if (header->data_type == ECL_MSG_DATA)
|
||||
ecl_ish_process_rx_data(opr_dev);
|
||||
else if (header->data_type == ECL_MSG_EVENT)
|
||||
ecl_ish_process_rx_event(opr_dev);
|
||||
else
|
||||
/* Got an event with wrong data_type, ignore it */
|
||||
dev_err(cl_data_to_dev(opr_dev),
|
||||
"[ish_cb] Received wrong data_type\n");
|
||||
|
||||
ishtp_cl_io_rb_recycle(rb);
|
||||
}
|
||||
}
|
||||
|
||||
static int ecl_ishtp_cl_init(struct ishtp_cl *ecl_ishtp_cl)
|
||||
{
|
||||
struct ishtp_opregion_dev *opr_dev =
|
||||
ishtp_get_client_data(ecl_ishtp_cl);
|
||||
struct ishtp_fw_client *fw_client;
|
||||
struct ishtp_device *dev;
|
||||
int rv;
|
||||
|
||||
rv = ishtp_cl_link(ecl_ishtp_cl);
|
||||
if (rv) {
|
||||
dev_err(cl_data_to_dev(opr_dev), "ishtp_cl_link failed\n");
|
||||
return rv;
|
||||
}
|
||||
|
||||
dev = ishtp_get_ishtp_device(ecl_ishtp_cl);
|
||||
|
||||
/* Connect to FW client */
|
||||
ishtp_set_tx_ring_size(ecl_ishtp_cl, ECL_CL_TX_RING_SIZE);
|
||||
ishtp_set_rx_ring_size(ecl_ishtp_cl, ECL_CL_RX_RING_SIZE);
|
||||
|
||||
fw_client = ishtp_fw_cl_get_client(dev, &ecl_ishtp_guid);
|
||||
if (!fw_client) {
|
||||
dev_err(cl_data_to_dev(opr_dev), "fw client not found\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ishtp_cl_set_fw_client_id(ecl_ishtp_cl,
|
||||
ishtp_get_fw_client_id(fw_client));
|
||||
|
||||
ishtp_set_connection_state(ecl_ishtp_cl, ISHTP_CL_CONNECTING);
|
||||
|
||||
rv = ishtp_cl_connect(ecl_ishtp_cl);
|
||||
if (rv) {
|
||||
dev_err(cl_data_to_dev(opr_dev), "client connect failed\n");
|
||||
|
||||
ishtp_cl_unlink(ecl_ishtp_cl);
|
||||
return rv;
|
||||
}
|
||||
|
||||
dev_dbg(cl_data_to_dev(opr_dev), "Host connected to fw client\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ecl_ishtp_cl_deinit(struct ishtp_cl *ecl_ishtp_cl)
|
||||
{
|
||||
ishtp_cl_unlink(ecl_ishtp_cl);
|
||||
ishtp_cl_flush_queues(ecl_ishtp_cl);
|
||||
ishtp_cl_free(ecl_ishtp_cl);
|
||||
}
|
||||
|
||||
static void ecl_ishtp_cl_reset_handler(struct work_struct *work)
|
||||
{
|
||||
struct ishtp_opregion_dev *opr_dev;
|
||||
struct ishtp_cl_device *cl_device;
|
||||
struct ishtp_cl *ecl_ishtp_cl;
|
||||
int rv;
|
||||
int retry;
|
||||
|
||||
opr_dev = container_of(work, struct ishtp_opregion_dev, reset_work);
|
||||
|
||||
opr_dev->ish_link_ready = false;
|
||||
|
||||
cl_device = opr_dev->cl_device;
|
||||
ecl_ishtp_cl = opr_dev->ecl_ishtp_cl;
|
||||
|
||||
ecl_ishtp_cl_deinit(ecl_ishtp_cl);
|
||||
|
||||
ecl_ishtp_cl = ishtp_cl_allocate(cl_device);
|
||||
if (!ecl_ishtp_cl)
|
||||
return;
|
||||
|
||||
ishtp_set_drvdata(cl_device, ecl_ishtp_cl);
|
||||
ishtp_set_client_data(ecl_ishtp_cl, opr_dev);
|
||||
|
||||
opr_dev->ecl_ishtp_cl = ecl_ishtp_cl;
|
||||
|
||||
for (retry = 0; retry < 3; ++retry) {
|
||||
rv = ecl_ishtp_cl_init(ecl_ishtp_cl);
|
||||
if (!rv)
|
||||
break;
|
||||
}
|
||||
if (rv) {
|
||||
ishtp_cl_free(ecl_ishtp_cl);
|
||||
opr_dev->ecl_ishtp_cl = NULL;
|
||||
dev_err(cl_data_to_dev(opr_dev),
|
||||
"[ish_rst] Reset failed. Link not ready.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ishtp_register_event_cb(cl_device, ecl_ishtp_cl_event_cb);
|
||||
dev_info(cl_data_to_dev(opr_dev),
|
||||
"[ish_rst] Reset Success. Link ready.\n");
|
||||
|
||||
opr_dev->ish_link_ready = true;
|
||||
|
||||
if (opr_dev->acpi_init_done)
|
||||
return;
|
||||
|
||||
rv = acpi_opregion_init(opr_dev);
|
||||
if (rv) {
|
||||
dev_err(cl_data_to_dev(opr_dev),
|
||||
"ACPI opregion init failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int ecl_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
|
||||
{
|
||||
struct ishtp_cl *ecl_ishtp_cl;
|
||||
struct ishtp_opregion_dev *opr_dev;
|
||||
int rv;
|
||||
|
||||
opr_dev = devm_kzalloc(ishtp_device(cl_device), sizeof(*opr_dev),
|
||||
GFP_KERNEL);
|
||||
if (!opr_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ecl_ishtp_cl = ishtp_cl_allocate(cl_device);
|
||||
if (!ecl_ishtp_cl)
|
||||
return -ENOMEM;
|
||||
|
||||
ishtp_set_drvdata(cl_device, ecl_ishtp_cl);
|
||||
ishtp_set_client_data(ecl_ishtp_cl, opr_dev);
|
||||
opr_dev->ecl_ishtp_cl = ecl_ishtp_cl;
|
||||
opr_dev->cl_device = cl_device;
|
||||
|
||||
init_waitqueue_head(&opr_dev->read_wait);
|
||||
INIT_WORK(&opr_dev->event_work, ecl_acpi_invoke_dsm);
|
||||
INIT_WORK(&opr_dev->reset_work, ecl_ishtp_cl_reset_handler);
|
||||
|
||||
/* Initialize ish client device */
|
||||
rv = ecl_ishtp_cl_init(ecl_ishtp_cl);
|
||||
if (rv) {
|
||||
dev_err(cl_data_to_dev(opr_dev), "Client init failed\n");
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
dev_dbg(cl_data_to_dev(opr_dev), "eclite-ishtp client initialised\n");
|
||||
|
||||
opr_dev->ish_link_ready = true;
|
||||
mutex_init(&opr_dev->lock);
|
||||
|
||||
rv = acpi_find_eclite_device(opr_dev);
|
||||
if (rv) {
|
||||
dev_err(cl_data_to_dev(opr_dev), "ECLite ACPI ID not found\n");
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
/* Register a handler for eclite fw events */
|
||||
ishtp_register_event_cb(cl_device, ecl_ishtp_cl_event_cb);
|
||||
|
||||
/* Now init opregion handlers */
|
||||
rv = acpi_opregion_init(opr_dev);
|
||||
if (rv) {
|
||||
dev_err(cl_data_to_dev(opr_dev), "ACPI opregion init failed\n");
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
/* Reprobe devices depending on ECLite - battery, fan, etc. */
|
||||
acpi_dev_clear_dependencies(opr_dev->adev);
|
||||
|
||||
return 0;
|
||||
err_exit:
|
||||
ishtp_set_connection_state(ecl_ishtp_cl, ISHTP_CL_DISCONNECTING);
|
||||
ishtp_cl_disconnect(ecl_ishtp_cl);
|
||||
ecl_ishtp_cl_deinit(ecl_ishtp_cl);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void ecl_ishtp_cl_remove(struct ishtp_cl_device *cl_device)
|
||||
{
|
||||
struct ishtp_cl *ecl_ishtp_cl = ishtp_get_drvdata(cl_device);
|
||||
struct ishtp_opregion_dev *opr_dev =
|
||||
ishtp_get_client_data(ecl_ishtp_cl);
|
||||
|
||||
if (opr_dev->acpi_init_done)
|
||||
acpi_opregion_deinit(opr_dev);
|
||||
|
||||
acpi_dev_put(opr_dev->adev);
|
||||
|
||||
ishtp_set_connection_state(ecl_ishtp_cl, ISHTP_CL_DISCONNECTING);
|
||||
ishtp_cl_disconnect(ecl_ishtp_cl);
|
||||
ecl_ishtp_cl_deinit(ecl_ishtp_cl);
|
||||
|
||||
cancel_work_sync(&opr_dev->reset_work);
|
||||
cancel_work_sync(&opr_dev->event_work);
|
||||
}
|
||||
|
||||
static int ecl_ishtp_cl_reset(struct ishtp_cl_device *cl_device)
|
||||
{
|
||||
struct ishtp_cl *ecl_ishtp_cl = ishtp_get_drvdata(cl_device);
|
||||
struct ishtp_opregion_dev *opr_dev =
|
||||
ishtp_get_client_data(ecl_ishtp_cl);
|
||||
|
||||
schedule_work(&opr_dev->reset_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ecl_ishtp_cl_suspend(struct device *device)
|
||||
{
|
||||
struct ishtp_cl_device *cl_device = ishtp_dev_to_cl_device(device);
|
||||
struct ishtp_cl *ecl_ishtp_cl = ishtp_get_drvdata(cl_device);
|
||||
struct ishtp_opregion_dev *opr_dev =
|
||||
ishtp_get_client_data(ecl_ishtp_cl);
|
||||
|
||||
if (acpi_target_system_state() == ACPI_STATE_S0)
|
||||
return 0;
|
||||
|
||||
acpi_opregion_deinit(opr_dev);
|
||||
ecl_ish_cl_enable_events(opr_dev, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ecl_ishtp_cl_resume(struct device *device)
|
||||
{
|
||||
/* A reset is expected to call after an Sx. At this point
|
||||
* we are not sure if the link is up or not to restore anything,
|
||||
* so do nothing in resume path
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops ecl_ishtp_pm_ops = {
|
||||
.suspend = ecl_ishtp_cl_suspend,
|
||||
.resume = ecl_ishtp_cl_resume,
|
||||
};
|
||||
|
||||
static struct ishtp_cl_driver ecl_ishtp_cl_driver = {
|
||||
.name = "ishtp-eclite",
|
||||
.guid = &ecl_ishtp_guid,
|
||||
.probe = ecl_ishtp_cl_probe,
|
||||
.remove = ecl_ishtp_cl_remove,
|
||||
.reset = ecl_ishtp_cl_reset,
|
||||
.driver.pm = &ecl_ishtp_pm_ops,
|
||||
};
|
||||
|
||||
static int __init ecl_ishtp_init(void)
|
||||
{
|
||||
return ishtp_cl_driver_register(&ecl_ishtp_cl_driver, THIS_MODULE);
|
||||
}
|
||||
|
||||
static void __exit ecl_ishtp_exit(void)
|
||||
{
|
||||
return ishtp_cl_driver_unregister(&ecl_ishtp_cl_driver);
|
||||
}
|
||||
|
||||
late_initcall(ecl_ishtp_init);
|
||||
module_exit(ecl_ishtp_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ISH ISHTP eclite client opregion driver");
|
||||
MODULE_AUTHOR("K Naduvalath, Sumesh <sumesh.k.naduvalath@intel.com>");
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("ishtp:*");
|
@ -60,7 +60,6 @@ MODULE_ALIAS("wmi:" WMI_EVENT_GUID2);
|
||||
MODULE_ALIAS("wmi:" WMI_EVENT_GUID3);
|
||||
MODULE_ALIAS("wmi:" WMI_METHOD_WMAB);
|
||||
MODULE_ALIAS("wmi:" WMI_METHOD_WMBB);
|
||||
MODULE_ALIAS("acpi*:LGEX0815:*");
|
||||
|
||||
static struct platform_device *pf_device;
|
||||
static struct input_dev *wmi_input_dev;
|
||||
@ -331,7 +330,7 @@ static ssize_t fan_mode_show(struct device *dev,
|
||||
status = r->integer.value & 0x01;
|
||||
kfree(r);
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n", status);
|
||||
return sysfs_emit(buffer, "%d\n", status);
|
||||
}
|
||||
|
||||
static ssize_t usb_charge_store(struct device *dev,
|
||||
@ -373,7 +372,7 @@ static ssize_t usb_charge_show(struct device *dev,
|
||||
|
||||
kfree(r);
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n", status);
|
||||
return sysfs_emit(buffer, "%d\n", status);
|
||||
}
|
||||
|
||||
static ssize_t reader_mode_store(struct device *dev,
|
||||
@ -415,7 +414,7 @@ static ssize_t reader_mode_show(struct device *dev,
|
||||
|
||||
kfree(r);
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n", status);
|
||||
return sysfs_emit(buffer, "%d\n", status);
|
||||
}
|
||||
|
||||
static ssize_t fn_lock_store(struct device *dev,
|
||||
@ -456,7 +455,7 @@ static ssize_t fn_lock_show(struct device *dev,
|
||||
status = !!r->buffer.pointer[0];
|
||||
kfree(r);
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n", status);
|
||||
return sysfs_emit(buffer, "%d\n", status);
|
||||
}
|
||||
|
||||
static ssize_t battery_care_limit_store(struct device *dev,
|
||||
@ -521,7 +520,7 @@ static ssize_t battery_care_limit_show(struct device *dev,
|
||||
if (status != 80 && status != 100)
|
||||
status = 0;
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n", status);
|
||||
return sysfs_emit(buffer, "%d\n", status);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(fan_mode);
|
||||
|
File diff suppressed because it is too large
Load Diff
213
drivers/platform/x86/nvidia-wmi-ec-backlight.c
Normal file
213
drivers/platform/x86/nvidia-wmi-ec-backlight.c
Normal file
@ -0,0 +1,213 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wmi.h>
|
||||
|
||||
/**
|
||||
* enum wmi_brightness_method - WMI method IDs
|
||||
* @WMI_BRIGHTNESS_METHOD_LEVEL: Get/Set EC brightness level status
|
||||
* @WMI_BRIGHTNESS_METHOD_SOURCE: Get/Set EC Brightness Source
|
||||
*/
|
||||
enum wmi_brightness_method {
|
||||
WMI_BRIGHTNESS_METHOD_LEVEL = 1,
|
||||
WMI_BRIGHTNESS_METHOD_SOURCE = 2,
|
||||
WMI_BRIGHTNESS_METHOD_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* enum wmi_brightness_mode - Operation mode for WMI-wrapped method
|
||||
* @WMI_BRIGHTNESS_MODE_GET: Get the current brightness level/source.
|
||||
* @WMI_BRIGHTNESS_MODE_SET: Set the brightness level.
|
||||
* @WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL: Get the maximum brightness level. This
|
||||
* is only valid when the WMI method is
|
||||
* %WMI_BRIGHTNESS_METHOD_LEVEL.
|
||||
*/
|
||||
enum wmi_brightness_mode {
|
||||
WMI_BRIGHTNESS_MODE_GET = 0,
|
||||
WMI_BRIGHTNESS_MODE_SET = 1,
|
||||
WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL = 2,
|
||||
WMI_BRIGHTNESS_MODE_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* enum wmi_brightness_source - Backlight brightness control source selection
|
||||
* @WMI_BRIGHTNESS_SOURCE_GPU: Backlight brightness is controlled by the GPU.
|
||||
* @WMI_BRIGHTNESS_SOURCE_EC: Backlight brightness is controlled by the
|
||||
* system's Embedded Controller (EC).
|
||||
* @WMI_BRIGHTNESS_SOURCE_AUX: Backlight brightness is controlled over the
|
||||
* DisplayPort AUX channel.
|
||||
*/
|
||||
enum wmi_brightness_source {
|
||||
WMI_BRIGHTNESS_SOURCE_GPU = 1,
|
||||
WMI_BRIGHTNESS_SOURCE_EC = 2,
|
||||
WMI_BRIGHTNESS_SOURCE_AUX = 3,
|
||||
WMI_BRIGHTNESS_SOURCE_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* struct wmi_brightness_args - arguments for the WMI-wrapped ACPI method
|
||||
* @mode: Pass in an &enum wmi_brightness_mode value to select between
|
||||
* getting or setting a value.
|
||||
* @val: In parameter for value to set when using %WMI_BRIGHTNESS_MODE_SET
|
||||
* mode. Not used in conjunction with %WMI_BRIGHTNESS_MODE_GET or
|
||||
* %WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL mode.
|
||||
* @ret: Out parameter returning retrieved value when operating in
|
||||
* %WMI_BRIGHTNESS_MODE_GET or %WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL
|
||||
* mode. Not used in %WMI_BRIGHTNESS_MODE_SET mode.
|
||||
* @ignored: Padding; not used. The ACPI method expects a 24 byte params struct.
|
||||
*
|
||||
* This is the parameters structure for the WmiBrightnessNotify ACPI method as
|
||||
* wrapped by WMI. The value passed in to @val or returned by @ret will be a
|
||||
* brightness value when the WMI method ID is %WMI_BRIGHTNESS_METHOD_LEVEL, or
|
||||
* an &enum wmi_brightness_source value with %WMI_BRIGHTNESS_METHOD_SOURCE.
|
||||
*/
|
||||
struct wmi_brightness_args {
|
||||
u32 mode;
|
||||
u32 val;
|
||||
u32 ret;
|
||||
u32 ignored[3];
|
||||
};
|
||||
|
||||
/**
|
||||
* wmi_brightness_notify() - helper function for calling WMI-wrapped ACPI method
|
||||
* @w: Pointer to the struct wmi_device identified by %WMI_BRIGHTNESS_GUID
|
||||
* @id: The WMI method ID to call (e.g. %WMI_BRIGHTNESS_METHOD_LEVEL or
|
||||
* %WMI_BRIGHTNESS_METHOD_SOURCE)
|
||||
* @mode: The operation to perform on the method (e.g. %WMI_BRIGHTNESS_MODE_SET
|
||||
* or %WMI_BRIGHTNESS_MODE_GET)
|
||||
* @val: Pointer to a value passed in by the caller when @mode is
|
||||
* %WMI_BRIGHTNESS_MODE_SET, or a value passed out to caller when @mode
|
||||
* is %WMI_BRIGHTNESS_MODE_GET or %WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL.
|
||||
*
|
||||
* Returns 0 on success, or a negative error number on failure.
|
||||
*/
|
||||
static int wmi_brightness_notify(struct wmi_device *w, enum wmi_brightness_method id, enum wmi_brightness_mode mode, u32 *val)
|
||||
{
|
||||
struct wmi_brightness_args args = {
|
||||
.mode = mode,
|
||||
.val = 0,
|
||||
.ret = 0,
|
||||
};
|
||||
struct acpi_buffer buf = { (acpi_size)sizeof(args), &args };
|
||||
acpi_status status;
|
||||
|
||||
if (id < WMI_BRIGHTNESS_METHOD_LEVEL ||
|
||||
id >= WMI_BRIGHTNESS_METHOD_MAX ||
|
||||
mode < WMI_BRIGHTNESS_MODE_GET || mode >= WMI_BRIGHTNESS_MODE_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (mode == WMI_BRIGHTNESS_MODE_SET)
|
||||
args.val = *val;
|
||||
|
||||
status = wmidev_evaluate_method(w, 0, id, &buf, &buf);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_err(&w->dev, "EC backlight control failed: %s\n",
|
||||
acpi_format_exception(status));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (mode != WMI_BRIGHTNESS_MODE_SET)
|
||||
*val = args.ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvidia_wmi_ec_backlight_update_status(struct backlight_device *bd)
|
||||
{
|
||||
struct wmi_device *wdev = bl_get_data(bd);
|
||||
|
||||
return wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL,
|
||||
WMI_BRIGHTNESS_MODE_SET,
|
||||
&bd->props.brightness);
|
||||
}
|
||||
|
||||
static int nvidia_wmi_ec_backlight_get_brightness(struct backlight_device *bd)
|
||||
{
|
||||
struct wmi_device *wdev = bl_get_data(bd);
|
||||
u32 level;
|
||||
int ret;
|
||||
|
||||
ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL,
|
||||
WMI_BRIGHTNESS_MODE_GET, &level);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
static const struct backlight_ops nvidia_wmi_ec_backlight_ops = {
|
||||
.update_status = nvidia_wmi_ec_backlight_update_status,
|
||||
.get_brightness = nvidia_wmi_ec_backlight_get_brightness,
|
||||
};
|
||||
|
||||
static int nvidia_wmi_ec_backlight_probe(struct wmi_device *wdev, const void *ctx)
|
||||
{
|
||||
struct backlight_properties props = {};
|
||||
struct backlight_device *bdev;
|
||||
u32 source;
|
||||
int ret;
|
||||
|
||||
ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_SOURCE,
|
||||
WMI_BRIGHTNESS_MODE_GET, &source);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* This driver is only to be used when brightness control is handled
|
||||
* by the EC; otherwise, the GPU driver(s) should control brightness.
|
||||
*/
|
||||
if (source != WMI_BRIGHTNESS_SOURCE_EC)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Identify this backlight device as a firmware device so that it can
|
||||
* be prioritized over any exposed GPU-driven raw device(s).
|
||||
*/
|
||||
props.type = BACKLIGHT_FIRMWARE;
|
||||
|
||||
ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL,
|
||||
WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL,
|
||||
&props.max_brightness);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL,
|
||||
WMI_BRIGHTNESS_MODE_GET, &props.brightness);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bdev = devm_backlight_device_register(&wdev->dev,
|
||||
"nvidia_wmi_ec_backlight",
|
||||
&wdev->dev, wdev,
|
||||
&nvidia_wmi_ec_backlight_ops,
|
||||
&props);
|
||||
return PTR_ERR_OR_ZERO(bdev);
|
||||
}
|
||||
|
||||
#define WMI_BRIGHTNESS_GUID "603E9613-EF25-4338-A3D0-C46177516DB7"
|
||||
|
||||
static const struct wmi_device_id nvidia_wmi_ec_backlight_id_table[] = {
|
||||
{ .guid_string = WMI_BRIGHTNESS_GUID },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(wmi, nvidia_wmi_ec_backlight_id_table);
|
||||
|
||||
static struct wmi_driver nvidia_wmi_ec_backlight_driver = {
|
||||
.driver = {
|
||||
.name = "nvidia-wmi-ec-backlight",
|
||||
},
|
||||
.probe = nvidia_wmi_ec_backlight_probe,
|
||||
.id_table = nvidia_wmi_ec_backlight_id_table,
|
||||
};
|
||||
module_wmi_driver(nvidia_wmi_ec_backlight_driver);
|
||||
|
||||
MODULE_AUTHOR("Daniel Dadap <ddadap@nvidia.com>");
|
||||
MODULE_DESCRIPTION("NVIDIA WMI EC Backlight driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -470,7 +470,7 @@ static ssize_t numbatt_show(struct device *dev, struct device_attribute *attr,
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
|
||||
return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
|
||||
}
|
||||
|
||||
static ssize_t lcdtype_show(struct device *dev, struct device_attribute *attr,
|
||||
@ -482,7 +482,7 @@ static ssize_t lcdtype_show(struct device *dev, struct device_attribute *attr,
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
|
||||
return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
|
||||
}
|
||||
|
||||
static ssize_t mute_show(struct device *dev, struct device_attribute *attr,
|
||||
@ -494,7 +494,7 @@ static ssize_t mute_show(struct device *dev, struct device_attribute *attr,
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]);
|
||||
return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_MUTE]);
|
||||
}
|
||||
|
||||
static ssize_t mute_store(struct device *dev, struct device_attribute *attr,
|
||||
@ -524,7 +524,7 @@ static ssize_t sticky_key_show(struct device *dev, struct device_attribute *attr
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sticky_key);
|
||||
return sysfs_emit(buf, "%u\n", pcc->sticky_key);
|
||||
}
|
||||
|
||||
static ssize_t sticky_key_store(struct device *dev, struct device_attribute *attr,
|
||||
@ -566,7 +566,7 @@ static ssize_t eco_mode_show(struct device *dev, struct device_attribute *attr,
|
||||
result = -EIO;
|
||||
break;
|
||||
}
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", result);
|
||||
return sysfs_emit(buf, "%u\n", result);
|
||||
}
|
||||
|
||||
static ssize_t eco_mode_store(struct device *dev, struct device_attribute *attr,
|
||||
@ -625,7 +625,7 @@ static ssize_t ac_brightness_show(struct device *dev, struct device_attribute *a
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_AC_CUR_BRIGHT]);
|
||||
return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_AC_CUR_BRIGHT]);
|
||||
}
|
||||
|
||||
static ssize_t ac_brightness_store(struct device *dev, struct device_attribute *attr,
|
||||
@ -655,7 +655,7 @@ static ssize_t dc_brightness_show(struct device *dev, struct device_attribute *a
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_DC_CUR_BRIGHT]);
|
||||
return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_DC_CUR_BRIGHT]);
|
||||
}
|
||||
|
||||
static ssize_t dc_brightness_store(struct device *dev, struct device_attribute *attr,
|
||||
@ -685,7 +685,7 @@ static ssize_t current_brightness_show(struct device *dev, struct device_attribu
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_CUR_BRIGHT]);
|
||||
return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_CUR_BRIGHT]);
|
||||
}
|
||||
|
||||
static ssize_t current_brightness_store(struct device *dev, struct device_attribute *attr,
|
||||
@ -710,7 +710,7 @@ static ssize_t current_brightness_store(struct device *dev, struct device_attrib
|
||||
static ssize_t cdpower_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", get_optd_power_state());
|
||||
return sysfs_emit(buf, "%d\n", get_optd_power_state());
|
||||
}
|
||||
|
||||
static ssize_t cdpower_store(struct device *dev, struct device_attribute *attr,
|
||||
|
@ -964,7 +964,7 @@ static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *a
|
||||
if (item->validate)
|
||||
value = item->validate(SNC_VALIDATE_OUT, value);
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n", value);
|
||||
return sysfs_emit(buffer, "%d\n", value);
|
||||
}
|
||||
|
||||
static ssize_t sony_nc_sysfs_store(struct device *dev,
|
||||
@ -1811,9 +1811,7 @@ static ssize_t sony_nc_kbd_backlight_mode_store(struct device *dev,
|
||||
static ssize_t sony_nc_kbd_backlight_mode_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buffer)
|
||||
{
|
||||
ssize_t count = 0;
|
||||
count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_ctl->mode);
|
||||
return count;
|
||||
return sysfs_emit(buffer, "%d\n", kbdbl_ctl->mode);
|
||||
}
|
||||
|
||||
static int __sony_nc_kbd_backlight_timeout_set(u8 value)
|
||||
@ -1855,9 +1853,7 @@ static ssize_t sony_nc_kbd_backlight_timeout_store(struct device *dev,
|
||||
static ssize_t sony_nc_kbd_backlight_timeout_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buffer)
|
||||
{
|
||||
ssize_t count = 0;
|
||||
count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_ctl->timeout);
|
||||
return count;
|
||||
return sysfs_emit(buffer, "%d\n", kbdbl_ctl->timeout);
|
||||
}
|
||||
|
||||
static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
|
||||
@ -2051,21 +2047,18 @@ static ssize_t sony_nc_battery_care_limit_show(struct device *dev,
|
||||
break;
|
||||
}
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n", status);
|
||||
return sysfs_emit(buffer, "%d\n", status);
|
||||
}
|
||||
|
||||
static ssize_t sony_nc_battery_care_health_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buffer)
|
||||
{
|
||||
ssize_t count = 0;
|
||||
unsigned int health;
|
||||
|
||||
if (sony_call_snc_handle(bcare_ctl->handle, 0x0200, &health))
|
||||
return -EIO;
|
||||
|
||||
count = snprintf(buffer, PAGE_SIZE, "%d\n", health & 0xff);
|
||||
|
||||
return count;
|
||||
return sysfs_emit(buffer, "%d\n", health & 0xff);
|
||||
}
|
||||
|
||||
static int sony_nc_battery_care_setup(struct platform_device *pd,
|
||||
@ -2215,15 +2208,12 @@ static ssize_t sony_nc_thermal_mode_store(struct device *dev,
|
||||
static ssize_t sony_nc_thermal_mode_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buffer)
|
||||
{
|
||||
ssize_t count = 0;
|
||||
int mode = sony_nc_thermal_mode_get();
|
||||
|
||||
if (mode < 0)
|
||||
return mode;
|
||||
|
||||
count = snprintf(buffer, PAGE_SIZE, "%s\n", snc_thermal_profiles[mode]);
|
||||
|
||||
return count;
|
||||
return sysfs_emit(buffer, "%s\n", snc_thermal_profiles[mode]);
|
||||
}
|
||||
|
||||
static int sony_nc_thermal_setup(struct platform_device *pd)
|
||||
@ -2361,7 +2351,7 @@ static ssize_t sony_nc_lid_resume_show(struct device *dev,
|
||||
|
||||
while (pos < LID_RESUME_MAX) {
|
||||
if (&lid_ctl->attrs[pos].attr == &attr->attr)
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n",
|
||||
return sysfs_emit(buffer, "%d\n",
|
||||
(lid_ctl->status >> pos) & 0x01);
|
||||
pos++;
|
||||
}
|
||||
@ -2493,7 +2483,7 @@ static ssize_t sony_nc_gfx_switch_status_show(struct device *dev,
|
||||
if (pos < 0)
|
||||
return pos;
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%s\n",
|
||||
return sysfs_emit(buffer, "%s\n",
|
||||
pos == SPEED ? "speed" :
|
||||
pos == STAMINA ? "stamina" :
|
||||
pos == AUTO ? "auto" : "unknown");
|
||||
@ -2568,7 +2558,7 @@ static ssize_t sony_nc_highspeed_charging_show(struct device *dev,
|
||||
if (sony_call_snc_handle(0x0131, 0x0100, &result))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01);
|
||||
return sysfs_emit(buffer, "%d\n", result & 0x01);
|
||||
}
|
||||
|
||||
static int sony_nc_highspeed_charging_setup(struct platform_device *pd)
|
||||
@ -2642,7 +2632,7 @@ static ssize_t sony_nc_lowbatt_show(struct device *dev,
|
||||
if (sony_call_snc_handle(0x0121, 0x0200, &result))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n", result & 1);
|
||||
return sysfs_emit(buffer, "%d\n", result & 1);
|
||||
}
|
||||
|
||||
static int sony_nc_lowbatt_setup(struct platform_device *pd)
|
||||
@ -2708,7 +2698,7 @@ static ssize_t sony_nc_hsfan_show(struct device *dev,
|
||||
if (sony_call_snc_handle(0x0149, 0x0100, &result))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01);
|
||||
return sysfs_emit(buffer, "%d\n", result & 0x01);
|
||||
}
|
||||
|
||||
static ssize_t sony_nc_fanspeed_show(struct device *dev,
|
||||
@ -2719,7 +2709,7 @@ static ssize_t sony_nc_fanspeed_show(struct device *dev,
|
||||
if (sony_call_snc_handle(0x0149, 0x0300, &result))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0xff);
|
||||
return sysfs_emit(buffer, "%d\n", result & 0xff);
|
||||
}
|
||||
|
||||
static int sony_nc_fanspeed_setup(struct platform_device *pd)
|
||||
@ -2815,7 +2805,7 @@ static ssize_t sony_nc_usb_charge_show(struct device *dev,
|
||||
if (sony_call_snc_handle(0x0155, 0x0000, &result))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01);
|
||||
return sysfs_emit(buffer, "%d\n", result & 0x01);
|
||||
}
|
||||
|
||||
static int sony_nc_usb_charge_setup(struct platform_device *pd)
|
||||
@ -2870,7 +2860,7 @@ static ssize_t sony_nc_panelid_show(struct device *dev,
|
||||
if (sony_call_snc_handle(0x011D, 0x0000, &result))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n", result);
|
||||
return sysfs_emit(buffer, "%d\n", result);
|
||||
}
|
||||
|
||||
static int sony_nc_panelid_setup(struct platform_device *pd)
|
||||
@ -2998,7 +2988,7 @@ static ssize_t sony_nc_touchpad_show(struct device *dev,
|
||||
if (sony_call_snc_handle(tp_ctl->handle, 0x000, &result))
|
||||
return -EINVAL;
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n", !(result & 0x01));
|
||||
return sysfs_emit(buffer, "%d\n", !(result & 0x01));
|
||||
}
|
||||
|
||||
static int sony_nc_touchpad_setup(struct platform_device *pd,
|
||||
@ -3915,7 +3905,7 @@ static ssize_t sony_pic_wwanpower_show(struct device *dev,
|
||||
{
|
||||
ssize_t count;
|
||||
mutex_lock(&spic_dev.lock);
|
||||
count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.wwan_power);
|
||||
count = sysfs_emit(buffer, "%d\n", spic_dev.wwan_power);
|
||||
mutex_unlock(&spic_dev.lock);
|
||||
return count;
|
||||
}
|
||||
@ -3954,7 +3944,7 @@ static ssize_t sony_pic_bluetoothpower_show(struct device *dev,
|
||||
{
|
||||
ssize_t count = 0;
|
||||
mutex_lock(&spic_dev.lock);
|
||||
count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.bluetooth_power);
|
||||
count = sysfs_emit(buffer, "%d\n", spic_dev.bluetooth_power);
|
||||
mutex_unlock(&spic_dev.lock);
|
||||
return count;
|
||||
}
|
||||
@ -3996,7 +3986,7 @@ static ssize_t sony_pic_fanspeed_show(struct device *dev,
|
||||
if (sony_pic_get_fanspeed(&value))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%d\n", value);
|
||||
return sysfs_emit(buffer, "%d\n", value);
|
||||
}
|
||||
|
||||
#define SPIC_ATTR(_name, _mode) \
|
||||
|
@ -10,13 +10,20 @@
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <acpi/battery.h>
|
||||
|
||||
struct system76_data {
|
||||
struct acpi_device *acpi_dev;
|
||||
struct led_classdev ap_led;
|
||||
@ -24,6 +31,10 @@ struct system76_data {
|
||||
enum led_brightness kb_brightness;
|
||||
enum led_brightness kb_toggle_brightness;
|
||||
int kb_color;
|
||||
struct device *therm;
|
||||
union acpi_object *nfan;
|
||||
union acpi_object *ntmp;
|
||||
struct input_dev *input;
|
||||
};
|
||||
|
||||
static const struct acpi_device_id device_ids[] = {
|
||||
@ -63,9 +74,57 @@ static int system76_get(struct system76_data *data, char *method)
|
||||
handle = acpi_device_handle(data->acpi_dev);
|
||||
status = acpi_evaluate_integer(handle, method, NULL, &ret);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return (int)ret;
|
||||
else
|
||||
return -1;
|
||||
return ret;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
// Get a System76 ACPI device value by name with index
|
||||
static int system76_get_index(struct system76_data *data, char *method, int index)
|
||||
{
|
||||
union acpi_object obj;
|
||||
struct acpi_object_list obj_list;
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
unsigned long long ret = 0;
|
||||
|
||||
obj.type = ACPI_TYPE_INTEGER;
|
||||
obj.integer.value = index;
|
||||
obj_list.count = 1;
|
||||
obj_list.pointer = &obj;
|
||||
|
||||
handle = acpi_device_handle(data->acpi_dev);
|
||||
status = acpi_evaluate_integer(handle, method, &obj_list, &ret);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return ret;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
// Get a System76 ACPI device object by name
|
||||
static int system76_get_object(struct system76_data *data, char *method, union acpi_object **obj)
|
||||
{
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
|
||||
handle = acpi_device_handle(data->acpi_dev);
|
||||
status = acpi_evaluate_object(handle, method, NULL, &buf);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
*obj = buf.pointer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
// Get a name from a System76 ACPI device object
|
||||
static char *system76_name(union acpi_object *obj, int index)
|
||||
{
|
||||
if (obj && obj->type == ACPI_TYPE_PACKAGE && index <= obj->package.count) {
|
||||
if (obj->package.elements[index].type == ACPI_TYPE_STRING)
|
||||
return obj->package.elements[index].string.pointer;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Set a System76 ACPI device value by name
|
||||
@ -88,6 +147,154 @@ static int system76_set(struct system76_data *data, char *method, int value)
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define BATTERY_THRESHOLD_INVALID 0xFF
|
||||
|
||||
enum {
|
||||
THRESHOLD_START,
|
||||
THRESHOLD_END,
|
||||
};
|
||||
|
||||
static ssize_t battery_get_threshold(int which, char *buf)
|
||||
{
|
||||
struct acpi_object_list input;
|
||||
union acpi_object param;
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
unsigned long long ret = BATTERY_THRESHOLD_INVALID;
|
||||
|
||||
handle = ec_get_handle();
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
input.count = 1;
|
||||
input.pointer = ¶m;
|
||||
// Start/stop selection
|
||||
param.type = ACPI_TYPE_INTEGER;
|
||||
param.integer.value = which;
|
||||
|
||||
status = acpi_evaluate_integer(handle, "GBCT", &input, &ret);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
if (ret == BATTERY_THRESHOLD_INVALID)
|
||||
return -EINVAL;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", (int)ret);
|
||||
}
|
||||
|
||||
static ssize_t battery_set_threshold(int which, const char *buf, size_t count)
|
||||
{
|
||||
struct acpi_object_list input;
|
||||
union acpi_object params[2];
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
unsigned int value;
|
||||
int ret;
|
||||
|
||||
handle = ec_get_handle();
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
ret = kstrtouint(buf, 10, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (value > 100)
|
||||
return -EINVAL;
|
||||
|
||||
input.count = 2;
|
||||
input.pointer = params;
|
||||
// Start/stop selection
|
||||
params[0].type = ACPI_TYPE_INTEGER;
|
||||
params[0].integer.value = which;
|
||||
// Threshold value
|
||||
params[1].type = ACPI_TYPE_INTEGER;
|
||||
params[1].integer.value = value;
|
||||
|
||||
status = acpi_evaluate_object(handle, "SBCT", &input, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t charge_control_start_threshold_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return battery_get_threshold(THRESHOLD_START, buf);
|
||||
}
|
||||
|
||||
static ssize_t charge_control_start_threshold_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
return battery_set_threshold(THRESHOLD_START, buf, count);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(charge_control_start_threshold);
|
||||
|
||||
static ssize_t charge_control_end_threshold_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return battery_get_threshold(THRESHOLD_END, buf);
|
||||
}
|
||||
|
||||
static ssize_t charge_control_end_threshold_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
return battery_set_threshold(THRESHOLD_END, buf, count);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(charge_control_end_threshold);
|
||||
|
||||
static struct attribute *system76_battery_attrs[] = {
|
||||
&dev_attr_charge_control_start_threshold.attr,
|
||||
&dev_attr_charge_control_end_threshold.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
ATTRIBUTE_GROUPS(system76_battery);
|
||||
|
||||
static int system76_battery_add(struct power_supply *battery)
|
||||
{
|
||||
// System76 EC only supports 1 battery
|
||||
if (strcmp(battery->desc->name, "BAT0") != 0)
|
||||
return -ENODEV;
|
||||
|
||||
if (device_add_groups(&battery->dev, system76_battery_groups))
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int system76_battery_remove(struct power_supply *battery)
|
||||
{
|
||||
device_remove_groups(&battery->dev, system76_battery_groups);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct acpi_battery_hook system76_battery_hook = {
|
||||
.add_battery = system76_battery_add,
|
||||
.remove_battery = system76_battery_remove,
|
||||
.name = "System76 Battery Extension",
|
||||
};
|
||||
|
||||
static void system76_battery_init(void)
|
||||
{
|
||||
acpi_handle handle;
|
||||
|
||||
handle = ec_get_handle();
|
||||
if (handle && acpi_has_method(handle, "GBCT"))
|
||||
battery_hook_register(&system76_battery_hook);
|
||||
}
|
||||
|
||||
static void system76_battery_exit(void)
|
||||
{
|
||||
acpi_handle handle;
|
||||
|
||||
handle = ec_get_handle();
|
||||
if (handle && acpi_has_method(handle, "GBCT"))
|
||||
battery_hook_unregister(&system76_battery_hook);
|
||||
}
|
||||
|
||||
// Get the airplane mode LED brightness
|
||||
static enum led_brightness ap_led_get(struct led_classdev *led)
|
||||
{
|
||||
@ -141,7 +348,7 @@ static ssize_t kb_led_color_show(
|
||||
|
||||
led = (struct led_classdev *)dev->driver_data;
|
||||
data = container_of(led, struct system76_data, kb_led);
|
||||
return sprintf(buf, "%06X\n", data->kb_color);
|
||||
return sysfs_emit(buf, "%06X\n", data->kb_color);
|
||||
}
|
||||
|
||||
// Set the keyboard LED color
|
||||
@ -169,7 +376,7 @@ static ssize_t kb_led_color_store(
|
||||
return size;
|
||||
}
|
||||
|
||||
static const struct device_attribute kb_led_color_dev_attr = {
|
||||
static struct device_attribute dev_attr_kb_led_color = {
|
||||
.attr = {
|
||||
.name = "color",
|
||||
.mode = 0644,
|
||||
@ -178,6 +385,13 @@ static const struct device_attribute kb_led_color_dev_attr = {
|
||||
.store = kb_led_color_store,
|
||||
};
|
||||
|
||||
static struct attribute *system76_kb_led_color_attrs[] = {
|
||||
&dev_attr_kb_led_color.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
ATTRIBUTE_GROUPS(system76_kb_led_color);
|
||||
|
||||
// Notify that the keyboard LED was changed by hardware
|
||||
static void kb_led_notify(struct system76_data *data)
|
||||
{
|
||||
@ -270,6 +484,155 @@ static void kb_led_hotkey_color(struct system76_data *data)
|
||||
kb_led_notify(data);
|
||||
}
|
||||
|
||||
static umode_t thermal_is_visible(const void *drvdata, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
const struct system76_data *data = drvdata;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_fan:
|
||||
case hwmon_pwm:
|
||||
if (system76_name(data->nfan, channel))
|
||||
return 0444;
|
||||
break;
|
||||
|
||||
case hwmon_temp:
|
||||
if (system76_name(data->ntmp, channel))
|
||||
return 0444;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long *val)
|
||||
{
|
||||
struct system76_data *data = dev_get_drvdata(dev);
|
||||
int raw;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_fan:
|
||||
if (attr == hwmon_fan_input) {
|
||||
raw = system76_get_index(data, "GFAN", channel);
|
||||
if (raw < 0)
|
||||
return raw;
|
||||
*val = (raw >> 8) & 0xFFFF;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case hwmon_pwm:
|
||||
if (attr == hwmon_pwm_input) {
|
||||
raw = system76_get_index(data, "GFAN", channel);
|
||||
if (raw < 0)
|
||||
return raw;
|
||||
*val = raw & 0xFF;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case hwmon_temp:
|
||||
if (attr == hwmon_temp_input) {
|
||||
raw = system76_get_index(data, "GTMP", channel);
|
||||
if (raw < 0)
|
||||
return raw;
|
||||
*val = raw * 1000;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int thermal_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, const char **str)
|
||||
{
|
||||
struct system76_data *data = dev_get_drvdata(dev);
|
||||
|
||||
switch (type) {
|
||||
case hwmon_fan:
|
||||
if (attr == hwmon_fan_label) {
|
||||
*str = system76_name(data->nfan, channel);
|
||||
if (*str)
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case hwmon_temp:
|
||||
if (attr == hwmon_temp_label) {
|
||||
*str = system76_name(data->ntmp, channel);
|
||||
if (*str)
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static const struct hwmon_ops thermal_ops = {
|
||||
.is_visible = thermal_is_visible,
|
||||
.read = thermal_read,
|
||||
.read_string = thermal_read_string,
|
||||
};
|
||||
|
||||
// Allocate up to 8 fans and temperatures
|
||||
static const struct hwmon_channel_info *thermal_channel_info[] = {
|
||||
HWMON_CHANNEL_INFO(fan,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL),
|
||||
HWMON_CHANNEL_INFO(pwm,
|
||||
HWMON_PWM_INPUT,
|
||||
HWMON_PWM_INPUT,
|
||||
HWMON_PWM_INPUT,
|
||||
HWMON_PWM_INPUT,
|
||||
HWMON_PWM_INPUT,
|
||||
HWMON_PWM_INPUT,
|
||||
HWMON_PWM_INPUT,
|
||||
HWMON_PWM_INPUT),
|
||||
HWMON_CHANNEL_INFO(temp,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info thermal_chip_info = {
|
||||
.ops = &thermal_ops,
|
||||
.info = thermal_channel_info,
|
||||
};
|
||||
|
||||
static void input_key(struct system76_data *data, unsigned int code)
|
||||
{
|
||||
input_report_key(data->input, code, 1);
|
||||
input_sync(data->input);
|
||||
|
||||
input_report_key(data->input, code, 0);
|
||||
input_sync(data->input);
|
||||
}
|
||||
|
||||
// Handle ACPI notification
|
||||
static void system76_notify(struct acpi_device *acpi_dev, u32 event)
|
||||
{
|
||||
@ -292,6 +655,9 @@ static void system76_notify(struct acpi_device *acpi_dev, u32 event)
|
||||
case 0x84:
|
||||
kb_led_hotkey_color(data);
|
||||
break;
|
||||
case 0x85:
|
||||
input_key(data, KEY_SCREENLOCK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -326,6 +692,7 @@ static int system76_add(struct acpi_device *acpi_dev)
|
||||
data->kb_led.brightness_set_blocking = kb_led_set;
|
||||
if (acpi_has_method(acpi_device_handle(data->acpi_dev), "SKBC")) {
|
||||
data->kb_led.max_brightness = 255;
|
||||
data->kb_led.groups = system76_kb_led_color_groups;
|
||||
data->kb_toggle_brightness = 72;
|
||||
data->kb_color = 0xffffff;
|
||||
system76_set(data, "SKBC", data->kb_color);
|
||||
@ -337,16 +704,42 @@ static int system76_add(struct acpi_device *acpi_dev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (data->kb_color >= 0) {
|
||||
err = device_create_file(
|
||||
data->kb_led.dev,
|
||||
&kb_led_color_dev_attr
|
||||
);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
data->input = devm_input_allocate_device(&acpi_dev->dev);
|
||||
if (!data->input)
|
||||
return -ENOMEM;
|
||||
|
||||
data->input->name = "System76 ACPI Hotkeys";
|
||||
data->input->phys = "system76_acpi/input0";
|
||||
data->input->id.bustype = BUS_HOST;
|
||||
data->input->dev.parent = &acpi_dev->dev;
|
||||
input_set_capability(data->input, EV_KEY, KEY_SCREENLOCK);
|
||||
|
||||
err = input_register_device(data->input);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
err = system76_get_object(data, "NFAN", &data->nfan);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
err = system76_get_object(data, "NTMP", &data->ntmp);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
data->therm = devm_hwmon_device_register_with_info(&acpi_dev->dev,
|
||||
"system76_acpi", data, &thermal_chip_info, NULL);
|
||||
err = PTR_ERR_OR_ZERO(data->therm);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
system76_battery_init();
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kfree(data->ntmp);
|
||||
kfree(data->nfan);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Remove a System76 ACPI device
|
||||
@ -355,13 +748,15 @@ static int system76_remove(struct acpi_device *acpi_dev)
|
||||
struct system76_data *data;
|
||||
|
||||
data = acpi_driver_data(acpi_dev);
|
||||
if (data->kb_color >= 0)
|
||||
device_remove_file(data->kb_led.dev, &kb_led_color_dev_attr);
|
||||
|
||||
system76_battery_exit();
|
||||
|
||||
devm_led_classdev_unregister(&acpi_dev->dev, &data->ap_led);
|
||||
|
||||
devm_led_classdev_unregister(&acpi_dev->dev, &data->kb_led);
|
||||
|
||||
kfree(data->nfan);
|
||||
kfree(data->ntmp);
|
||||
|
||||
system76_get(data, "FINI");
|
||||
|
||||
return 0;
|
||||
|
@ -1001,79 +1001,6 @@ static struct platform_driver tpacpi_hwmon_pdriver = {
|
||||
* sysfs support helpers
|
||||
*/
|
||||
|
||||
struct attribute_set {
|
||||
unsigned int members, max_members;
|
||||
struct attribute_group group;
|
||||
};
|
||||
|
||||
struct attribute_set_obj {
|
||||
struct attribute_set s;
|
||||
struct attribute *a;
|
||||
} __attribute__((packed));
|
||||
|
||||
static struct attribute_set *create_attr_set(unsigned int max_members,
|
||||
const char *name)
|
||||
{
|
||||
struct attribute_set_obj *sobj;
|
||||
|
||||
if (max_members == 0)
|
||||
return NULL;
|
||||
|
||||
/* Allocates space for implicit NULL at the end too */
|
||||
sobj = kzalloc(sizeof(struct attribute_set_obj) +
|
||||
max_members * sizeof(struct attribute *),
|
||||
GFP_KERNEL);
|
||||
if (!sobj)
|
||||
return NULL;
|
||||
sobj->s.max_members = max_members;
|
||||
sobj->s.group.attrs = &sobj->a;
|
||||
sobj->s.group.name = name;
|
||||
|
||||
return &sobj->s;
|
||||
}
|
||||
|
||||
#define destroy_attr_set(_set) \
|
||||
kfree(_set)
|
||||
|
||||
/* not multi-threaded safe, use it in a single thread per set */
|
||||
static int add_to_attr_set(struct attribute_set *s, struct attribute *attr)
|
||||
{
|
||||
if (!s || !attr)
|
||||
return -EINVAL;
|
||||
|
||||
if (s->members >= s->max_members)
|
||||
return -ENOMEM;
|
||||
|
||||
s->group.attrs[s->members] = attr;
|
||||
s->members++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_many_to_attr_set(struct attribute_set *s,
|
||||
struct attribute **attr,
|
||||
unsigned int count)
|
||||
{
|
||||
int i, res;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
res = add_to_attr_set(s, attr[i]);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void delete_attr_set(struct attribute_set *s, struct kobject *kobj)
|
||||
{
|
||||
sysfs_remove_group(kobj, &s->group);
|
||||
destroy_attr_set(s);
|
||||
}
|
||||
|
||||
#define register_attr_set_with_sysfs(_attr_set, _kobj) \
|
||||
sysfs_create_group(_kobj, &_attr_set->group)
|
||||
|
||||
static int parse_strtoul(const char *buf,
|
||||
unsigned long max, unsigned long *value)
|
||||
{
|
||||
@ -1348,7 +1275,7 @@ static ssize_t tpacpi_rfk_sysfs_enable_show(const enum tpacpi_rfk_id id,
|
||||
return status;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
return sysfs_emit(buf, "%d\n",
|
||||
(status == TPACPI_RFK_RADIO_ON) ? 1 : 0);
|
||||
}
|
||||
|
||||
@ -1441,14 +1368,14 @@ static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf)
|
||||
/* interface_version --------------------------------------------------- */
|
||||
static ssize_t interface_version_show(struct device_driver *drv, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION);
|
||||
return sysfs_emit(buf, "0x%08x\n", TPACPI_SYSFS_VERSION);
|
||||
}
|
||||
static DRIVER_ATTR_RO(interface_version);
|
||||
|
||||
/* debug_level --------------------------------------------------------- */
|
||||
static ssize_t debug_level_show(struct device_driver *drv, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level);
|
||||
return sysfs_emit(buf, "0x%04x\n", dbg_level);
|
||||
}
|
||||
|
||||
static ssize_t debug_level_store(struct device_driver *drv, const char *buf,
|
||||
@ -1468,7 +1395,7 @@ static DRIVER_ATTR_RW(debug_level);
|
||||
/* version ------------------------------------------------------------- */
|
||||
static ssize_t version_show(struct device_driver *drv, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s v%s\n",
|
||||
return sysfs_emit(buf, "%s v%s\n",
|
||||
TPACPI_DESC, TPACPI_VERSION);
|
||||
}
|
||||
static DRIVER_ATTR_RO(version);
|
||||
@ -1480,7 +1407,7 @@ static DRIVER_ATTR_RO(version);
|
||||
/* wlsw_emulstate ------------------------------------------------------ */
|
||||
static ssize_t wlsw_emulstate_show(struct device_driver *drv, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_wlsw_emulstate);
|
||||
return sysfs_emit(buf, "%d\n", !!tpacpi_wlsw_emulstate);
|
||||
}
|
||||
|
||||
static ssize_t wlsw_emulstate_store(struct device_driver *drv, const char *buf,
|
||||
@ -1503,7 +1430,7 @@ static DRIVER_ATTR_RW(wlsw_emulstate);
|
||||
/* bluetooth_emulstate ------------------------------------------------- */
|
||||
static ssize_t bluetooth_emulstate_show(struct device_driver *drv, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_bluetooth_emulstate);
|
||||
return sysfs_emit(buf, "%d\n", !!tpacpi_bluetooth_emulstate);
|
||||
}
|
||||
|
||||
static ssize_t bluetooth_emulstate_store(struct device_driver *drv,
|
||||
@ -1523,7 +1450,7 @@ static DRIVER_ATTR_RW(bluetooth_emulstate);
|
||||
/* wwan_emulstate ------------------------------------------------- */
|
||||
static ssize_t wwan_emulstate_show(struct device_driver *drv, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_wwan_emulstate);
|
||||
return sysfs_emit(buf, "%d\n", !!tpacpi_wwan_emulstate);
|
||||
}
|
||||
|
||||
static ssize_t wwan_emulstate_store(struct device_driver *drv, const char *buf,
|
||||
@ -1543,7 +1470,7 @@ static DRIVER_ATTR_RW(wwan_emulstate);
|
||||
/* uwb_emulstate ------------------------------------------------- */
|
||||
static ssize_t uwb_emulstate_show(struct device_driver *drv, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_uwb_emulstate);
|
||||
return sysfs_emit(buf, "%d\n", !!tpacpi_uwb_emulstate);
|
||||
}
|
||||
|
||||
static ssize_t uwb_emulstate_store(struct device_driver *drv, const char *buf,
|
||||
@ -2042,8 +1969,6 @@ static u32 hotkey_acpi_mask; /* events enabled in firmware */
|
||||
|
||||
static u16 *hotkey_keycode_map;
|
||||
|
||||
static struct attribute_set *hotkey_dev_attributes;
|
||||
|
||||
static void tpacpi_driver_event(const unsigned int hkey_event);
|
||||
static void hotkey_driver_event(const unsigned int scancode);
|
||||
static void hotkey_poll_setup(const bool may_warn);
|
||||
@ -2753,7 +2678,7 @@ static ssize_t hotkey_enable_show(struct device *dev,
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", status);
|
||||
return sysfs_emit(buf, "%d\n", status);
|
||||
}
|
||||
|
||||
static ssize_t hotkey_enable_store(struct device *dev,
|
||||
@ -2781,7 +2706,7 @@ static ssize_t hotkey_mask_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_user_mask);
|
||||
return sysfs_emit(buf, "0x%08x\n", hotkey_user_mask);
|
||||
}
|
||||
|
||||
static ssize_t hotkey_mask_store(struct device *dev,
|
||||
@ -2829,7 +2754,7 @@ static ssize_t hotkey_bios_mask_show(struct device *dev,
|
||||
{
|
||||
printk_deprecated_attribute("hotkey_bios_mask",
|
||||
"This attribute is useless.");
|
||||
return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_orig_mask);
|
||||
return sysfs_emit(buf, "0x%08x\n", hotkey_orig_mask);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(hotkey_bios_mask);
|
||||
@ -2839,7 +2764,7 @@ static ssize_t hotkey_all_mask_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "0x%08x\n",
|
||||
return sysfs_emit(buf, "0x%08x\n",
|
||||
hotkey_all_mask | hotkey_source_mask);
|
||||
}
|
||||
|
||||
@ -2850,7 +2775,7 @@ static ssize_t hotkey_adaptive_all_mask_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "0x%08x\n",
|
||||
return sysfs_emit(buf, "0x%08x\n",
|
||||
hotkey_adaptive_all_mask | hotkey_source_mask);
|
||||
}
|
||||
|
||||
@ -2861,7 +2786,7 @@ static ssize_t hotkey_recommended_mask_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "0x%08x\n",
|
||||
return sysfs_emit(buf, "0x%08x\n",
|
||||
(hotkey_all_mask | hotkey_source_mask)
|
||||
& ~hotkey_reserved_mask);
|
||||
}
|
||||
@ -2875,7 +2800,7 @@ static ssize_t hotkey_source_mask_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_source_mask);
|
||||
return sysfs_emit(buf, "0x%08x\n", hotkey_source_mask);
|
||||
}
|
||||
|
||||
static ssize_t hotkey_source_mask_store(struct device *dev,
|
||||
@ -2926,7 +2851,7 @@ static ssize_t hotkey_poll_freq_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_poll_freq);
|
||||
return sysfs_emit(buf, "%d\n", hotkey_poll_freq);
|
||||
}
|
||||
|
||||
static ssize_t hotkey_poll_freq_store(struct device *dev,
|
||||
@ -2968,7 +2893,7 @@ static ssize_t hotkey_radio_sw_show(struct device *dev,
|
||||
/* Opportunistic update */
|
||||
tpacpi_rfk_update_hwblock_state((res == TPACPI_RFK_RADIO_OFF));
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
return sysfs_emit(buf, "%d\n",
|
||||
(res == TPACPI_RFK_RADIO_OFF) ? 0 : 1);
|
||||
}
|
||||
|
||||
@ -2991,7 +2916,7 @@ static ssize_t hotkey_tablet_mode_show(struct device *dev,
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", !!s);
|
||||
return sysfs_emit(buf, "%d\n", !!s);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(hotkey_tablet_mode);
|
||||
@ -3008,7 +2933,7 @@ static ssize_t hotkey_wakeup_reason_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_wakeup_reason);
|
||||
return sysfs_emit(buf, "%d\n", hotkey_wakeup_reason);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wakeup_reason, S_IRUGO, hotkey_wakeup_reason_show, NULL);
|
||||
@ -3024,7 +2949,7 @@ static ssize_t hotkey_wakeup_hotunplug_complete_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_autosleep_ack);
|
||||
return sysfs_emit(buf, "%d\n", hotkey_autosleep_ack);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wakeup_hotunplug_complete, S_IRUGO,
|
||||
@ -3059,7 +2984,7 @@ static ssize_t adaptive_kbd_mode_show(struct device *dev,
|
||||
if (current_mode < 0)
|
||||
return current_mode;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", current_mode);
|
||||
return sysfs_emit(buf, "%d\n", current_mode);
|
||||
}
|
||||
|
||||
static ssize_t adaptive_kbd_mode_store(struct device *dev,
|
||||
@ -3089,7 +3014,7 @@ static const struct attribute_group adaptive_kbd_attr_group = {
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static struct attribute *hotkey_attributes[] __initdata = {
|
||||
static struct attribute *hotkey_attributes[] = {
|
||||
&dev_attr_hotkey_enable.attr,
|
||||
&dev_attr_hotkey_bios_enabled.attr,
|
||||
&dev_attr_hotkey_bios_mask.attr,
|
||||
@ -3103,6 +3028,26 @@ static struct attribute *hotkey_attributes[] __initdata = {
|
||||
&dev_attr_hotkey_source_mask.attr,
|
||||
&dev_attr_hotkey_poll_freq.attr,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
static umode_t hotkey_attr_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
if (attr == &dev_attr_hotkey_tablet_mode.attr) {
|
||||
if (!tp_features.hotkey_tablet)
|
||||
return 0;
|
||||
} else if (attr == &dev_attr_hotkey_radio_sw.attr) {
|
||||
if (!tp_features.hotkey_wlsw)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group hotkey_attr_group = {
|
||||
.is_visible = hotkey_attr_is_visible,
|
||||
.attrs = hotkey_attributes,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -3161,9 +3106,7 @@ static void hotkey_exit(void)
|
||||
hotkey_poll_stop_sync();
|
||||
mutex_unlock(&hotkey_mutex);
|
||||
#endif
|
||||
|
||||
if (hotkey_dev_attributes)
|
||||
delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
|
||||
sysfs_remove_group(&tpacpi_pdev->dev.kobj, &hotkey_attr_group);
|
||||
|
||||
dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY,
|
||||
"restoring original HKEY status and mask\n");
|
||||
@ -3249,11 +3192,6 @@ static int hotkey_init_tablet_mode(void)
|
||||
pr_info("Tablet mode switch found (type: %s), currently in %s mode\n",
|
||||
type, in_tablet_mode ? "tablet" : "laptop");
|
||||
|
||||
res = add_to_attr_set(hotkey_dev_attributes,
|
||||
&dev_attr_hotkey_tablet_mode.attr);
|
||||
if (res)
|
||||
return -1;
|
||||
|
||||
return in_tablet_mode;
|
||||
}
|
||||
|
||||
@ -3515,19 +3453,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
|
||||
|
||||
tpacpi_disable_brightness_delay();
|
||||
|
||||
/* MUST have enough space for all attributes to be added to
|
||||
* hotkey_dev_attributes */
|
||||
hotkey_dev_attributes = create_attr_set(
|
||||
ARRAY_SIZE(hotkey_attributes) + 2,
|
||||
NULL);
|
||||
if (!hotkey_dev_attributes)
|
||||
return -ENOMEM;
|
||||
res = add_many_to_attr_set(hotkey_dev_attributes,
|
||||
hotkey_attributes,
|
||||
ARRAY_SIZE(hotkey_attributes));
|
||||
if (res)
|
||||
goto err_exit;
|
||||
|
||||
/* mask not supported on 600e/x, 770e, 770x, A21e, A2xm/p,
|
||||
A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking
|
||||
for HKEY interface version 0x100 */
|
||||
@ -3636,18 +3561,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
|
||||
pr_info("radio switch found; radios are %s\n",
|
||||
enabled(status, 0));
|
||||
}
|
||||
if (tp_features.hotkey_wlsw)
|
||||
res = add_to_attr_set(hotkey_dev_attributes,
|
||||
&dev_attr_hotkey_radio_sw.attr);
|
||||
|
||||
res = hotkey_init_tablet_mode();
|
||||
if (res < 0)
|
||||
goto err_exit;
|
||||
|
||||
tabletsw_state = res;
|
||||
|
||||
res = register_attr_set_with_sysfs(hotkey_dev_attributes,
|
||||
&tpacpi_pdev->dev.kobj);
|
||||
tabletsw_state = hotkey_init_tablet_mode();
|
||||
res = sysfs_create_group(&tpacpi_pdev->dev.kobj, &hotkey_attr_group);
|
||||
if (res)
|
||||
goto err_exit;
|
||||
|
||||
@ -3746,11 +3662,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
|
||||
return 0;
|
||||
|
||||
err_exit:
|
||||
delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
|
||||
sysfs_remove_group(&tpacpi_pdev->dev.kobj,
|
||||
&adaptive_kbd_attr_group);
|
||||
|
||||
hotkey_dev_attributes = NULL;
|
||||
sysfs_remove_group(&tpacpi_pdev->dev.kobj, &hotkey_attr_group);
|
||||
sysfs_remove_group(&tpacpi_pdev->dev.kobj, &adaptive_kbd_attr_group);
|
||||
|
||||
return (res < 0) ? res : 1;
|
||||
}
|
||||
@ -6421,7 +6334,7 @@ static ssize_t thermal_temp_input_show(struct device *dev,
|
||||
if (value == TPACPI_THERMAL_SENSOR_NA)
|
||||
return -ENXIO;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", value);
|
||||
return sysfs_emit(buf, "%d\n", value);
|
||||
}
|
||||
|
||||
#define THERMAL_SENSOR_ATTR_TEMP(_idxA, _idxB) \
|
||||
@ -8654,7 +8567,7 @@ static ssize_t fan_pwm1_enable_show(struct device *dev,
|
||||
} else
|
||||
mode = 1;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", mode);
|
||||
return sysfs_emit(buf, "%d\n", mode);
|
||||
}
|
||||
|
||||
static ssize_t fan_pwm1_enable_store(struct device *dev,
|
||||
@ -8720,7 +8633,7 @@ static ssize_t fan_pwm1_show(struct device *dev,
|
||||
if (status > 7)
|
||||
status = 7;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", (status * 255) / 7);
|
||||
return sysfs_emit(buf, "%u\n", (status * 255) / 7);
|
||||
}
|
||||
|
||||
static ssize_t fan_pwm1_store(struct device *dev,
|
||||
@ -8773,7 +8686,7 @@ static ssize_t fan_fan1_input_show(struct device *dev,
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", speed);
|
||||
return sysfs_emit(buf, "%u\n", speed);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(fan1_input, S_IRUGO, fan_fan1_input_show, NULL);
|
||||
@ -8790,7 +8703,7 @@ static ssize_t fan_fan2_input_show(struct device *dev,
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", speed);
|
||||
return sysfs_emit(buf, "%u\n", speed);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(fan2_input, S_IRUGO, fan_fan2_input_show, NULL);
|
||||
@ -8798,7 +8711,7 @@ static DEVICE_ATTR(fan2_input, S_IRUGO, fan_fan2_input_show, NULL);
|
||||
/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */
|
||||
static ssize_t fan_watchdog_show(struct device_driver *drv, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval);
|
||||
return sysfs_emit(buf, "%u\n", fan_watchdog_maxinterval);
|
||||
}
|
||||
|
||||
static ssize_t fan_watchdog_store(struct device_driver *drv, const char *buf,
|
||||
@ -9145,7 +9058,7 @@ static int fan_write_cmd_level(const char *cmd, int *rc)
|
||||
|
||||
if (strlencmp(cmd, "level auto") == 0)
|
||||
level = TP_EC_FAN_AUTO;
|
||||
else if ((strlencmp(cmd, "level disengaged") == 0) |
|
||||
else if ((strlencmp(cmd, "level disengaged") == 0) ||
|
||||
(strlencmp(cmd, "level full-speed") == 0))
|
||||
level = TP_EC_FAN_FULLSPEED;
|
||||
else if (sscanf(cmd, "level %d", &level) != 1)
|
||||
|
@ -938,6 +938,23 @@ static const struct ts_dmi_data trekstor_surftab_wintron70_data = {
|
||||
.properties = trekstor_surftab_wintron70_props,
|
||||
};
|
||||
|
||||
static const struct property_entry viglen_connect_10_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1890),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
|
||||
PROPERTY_ENTRY_U32("touchscreen-fuzz-x", 6),
|
||||
PROPERTY_ENTRY_U32("touchscreen-fuzz-y", 6),
|
||||
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
|
||||
PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-viglen-connect-10.fw"),
|
||||
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
|
||||
PROPERTY_ENTRY_BOOL("silead,home-button"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct ts_dmi_data viglen_connect_10_data = {
|
||||
.acpi_name = "MSSL1680:00",
|
||||
.properties = viglen_connect_10_props,
|
||||
};
|
||||
|
||||
static const struct property_entry vinga_twizzle_j116_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1920),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
|
||||
@ -1521,6 +1538,14 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "YOURBOOK C11B"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Viglen Connect 10 */
|
||||
.driver_data = (void *)&viglen_connect_10_data,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Viglen Ltd."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Connect 10'' Tablet PC"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Vinga Twizzle J116 */
|
||||
.driver_data = (void *)&vinga_twizzle_j116_data,
|
||||
|
@ -17,6 +17,8 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -25,6 +27,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/uuid.h>
|
||||
@ -39,7 +42,7 @@ MODULE_LICENSE("GPL");
|
||||
static LIST_HEAD(wmi_block_list);
|
||||
|
||||
struct guid_block {
|
||||
char guid[16];
|
||||
guid_t guid;
|
||||
union {
|
||||
char object_id[2];
|
||||
struct {
|
||||
@ -49,7 +52,10 @@ struct guid_block {
|
||||
};
|
||||
u8 instance_count;
|
||||
u8 flags;
|
||||
};
|
||||
} __packed;
|
||||
static_assert(sizeof(typeof_member(struct guid_block, guid)) == 16);
|
||||
static_assert(sizeof(struct guid_block) == 20);
|
||||
static_assert(__alignof__(struct guid_block) == 1);
|
||||
|
||||
struct wmi_block {
|
||||
struct wmi_device dev;
|
||||
@ -70,10 +76,10 @@ struct wmi_block {
|
||||
* If the GUID data block is marked as expensive, we must enable and
|
||||
* explicitily disable data collection.
|
||||
*/
|
||||
#define ACPI_WMI_EXPENSIVE 0x1
|
||||
#define ACPI_WMI_METHOD 0x2 /* GUID is a method */
|
||||
#define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */
|
||||
#define ACPI_WMI_EVENT 0x8 /* GUID is an event */
|
||||
#define ACPI_WMI_EXPENSIVE BIT(0)
|
||||
#define ACPI_WMI_METHOD BIT(1) /* GUID is a method */
|
||||
#define ACPI_WMI_STRING BIT(2) /* GUID takes & returns a string */
|
||||
#define ACPI_WMI_EVENT BIT(3) /* GUID is an event */
|
||||
|
||||
static bool debug_event;
|
||||
module_param(debug_event, bool, 0444);
|
||||
@ -91,7 +97,7 @@ static int acpi_wmi_probe(struct platform_device *device);
|
||||
static const struct acpi_device_id wmi_device_ids[] = {
|
||||
{"PNP0C14", 0},
|
||||
{"pnp0c14", 0},
|
||||
{"", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
|
||||
|
||||
@ -108,43 +114,44 @@ static struct platform_driver acpi_wmi_driver = {
|
||||
* GUID parsing functions
|
||||
*/
|
||||
|
||||
static bool find_guid(const char *guid_string, struct wmi_block **out)
|
||||
static acpi_status find_guid(const char *guid_string, struct wmi_block **out)
|
||||
{
|
||||
guid_t guid_input;
|
||||
struct wmi_block *wblock;
|
||||
struct guid_block *block;
|
||||
|
||||
if (!guid_string)
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
if (guid_parse(guid_string, &guid_input))
|
||||
return false;
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
list_for_each_entry(wblock, &wmi_block_list, list) {
|
||||
block = &wblock->gblock;
|
||||
|
||||
if (memcmp(block->guid, &guid_input, 16) == 0) {
|
||||
if (guid_equal(&wblock->gblock.guid, &guid_input)) {
|
||||
if (out)
|
||||
*out = wblock;
|
||||
return true;
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
return AE_NOT_FOUND;
|
||||
}
|
||||
|
||||
static const void *find_guid_context(struct wmi_block *wblock,
|
||||
struct wmi_driver *wdriver)
|
||||
struct wmi_driver *wdriver)
|
||||
{
|
||||
const struct wmi_device_id *id;
|
||||
guid_t guid_input;
|
||||
|
||||
if (wblock == NULL || wdriver == NULL)
|
||||
return NULL;
|
||||
if (wdriver->id_table == NULL)
|
||||
return NULL;
|
||||
|
||||
id = wdriver->id_table;
|
||||
if (!id)
|
||||
return NULL;
|
||||
|
||||
while (*id->guid_string) {
|
||||
guid_t guid_input;
|
||||
|
||||
if (guid_parse(id->guid_string, &guid_input))
|
||||
continue;
|
||||
if (!memcmp(wblock->gblock.guid, &guid_input, 16))
|
||||
if (guid_equal(&wblock->gblock.guid, &guid_input))
|
||||
return id->context;
|
||||
id++;
|
||||
}
|
||||
@ -175,9 +182,9 @@ static int get_subobj_info(acpi_handle handle, const char *pathname,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
|
||||
static acpi_status wmi_method_enable(struct wmi_block *wblock, bool enable)
|
||||
{
|
||||
struct guid_block *block = NULL;
|
||||
struct guid_block *block;
|
||||
char method[5];
|
||||
acpi_status status;
|
||||
acpi_handle handle;
|
||||
@ -187,11 +194,50 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
|
||||
|
||||
snprintf(method, 5, "WE%02X", block->notify_id);
|
||||
status = acpi_execute_simple_method(handle, method, enable);
|
||||
|
||||
if (status != AE_OK && status != AE_NOT_FOUND)
|
||||
return status;
|
||||
else
|
||||
if (status == AE_NOT_FOUND)
|
||||
return AE_OK;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#define WMI_ACPI_METHOD_NAME_SIZE 5
|
||||
|
||||
static inline void get_acpi_method_name(const struct wmi_block *wblock,
|
||||
const char method,
|
||||
char buffer[static WMI_ACPI_METHOD_NAME_SIZE])
|
||||
{
|
||||
static_assert(ARRAY_SIZE(wblock->gblock.object_id) == 2);
|
||||
static_assert(WMI_ACPI_METHOD_NAME_SIZE >= 5);
|
||||
|
||||
buffer[0] = 'W';
|
||||
buffer[1] = method;
|
||||
buffer[2] = wblock->gblock.object_id[0];
|
||||
buffer[3] = wblock->gblock.object_id[1];
|
||||
buffer[4] = '\0';
|
||||
}
|
||||
|
||||
static inline acpi_object_type get_param_acpi_type(const struct wmi_block *wblock)
|
||||
{
|
||||
if (wblock->gblock.flags & ACPI_WMI_STRING)
|
||||
return ACPI_TYPE_STRING;
|
||||
else
|
||||
return ACPI_TYPE_BUFFER;
|
||||
}
|
||||
|
||||
static acpi_status get_event_data(const struct wmi_block *wblock, struct acpi_buffer *out)
|
||||
{
|
||||
union acpi_object param = {
|
||||
.integer = {
|
||||
.type = ACPI_TYPE_INTEGER,
|
||||
.value = wblock->gblock.notify_id,
|
||||
}
|
||||
};
|
||||
struct acpi_object_list input = {
|
||||
.count = 1,
|
||||
.pointer = ¶m,
|
||||
};
|
||||
|
||||
return acpi_evaluate_object(wblock->acpi_device->handle, "_WED", &input, out);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -226,13 +272,16 @@ EXPORT_SYMBOL_GPL(set_required_buffer_size);
|
||||
*
|
||||
* Call an ACPI-WMI method
|
||||
*/
|
||||
acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
|
||||
u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
|
||||
acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, u32 method_id,
|
||||
const struct acpi_buffer *in, struct acpi_buffer *out)
|
||||
{
|
||||
struct wmi_block *wblock = NULL;
|
||||
acpi_status status;
|
||||
|
||||
status = find_guid(guid_string, &wblock);
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
if (!find_guid(guid_string, &wblock))
|
||||
return AE_ERROR;
|
||||
return wmidev_evaluate_method(&wblock->dev, instance, method_id,
|
||||
in, out);
|
||||
}
|
||||
@ -248,16 +297,15 @@ EXPORT_SYMBOL_GPL(wmi_evaluate_method);
|
||||
*
|
||||
* Call an ACPI-WMI method
|
||||
*/
|
||||
acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance,
|
||||
u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
|
||||
acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 method_id,
|
||||
const struct acpi_buffer *in, struct acpi_buffer *out)
|
||||
{
|
||||
struct guid_block *block = NULL;
|
||||
struct wmi_block *wblock = NULL;
|
||||
struct guid_block *block;
|
||||
struct wmi_block *wblock;
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
struct acpi_object_list input;
|
||||
union acpi_object params[3];
|
||||
char method[5] = "WM";
|
||||
char method[WMI_ACPI_METHOD_NAME_SIZE];
|
||||
|
||||
wblock = container_of(wdev, struct wmi_block, dev);
|
||||
block = &wblock->gblock;
|
||||
@ -279,33 +327,27 @@ acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance,
|
||||
if (in) {
|
||||
input.count = 3;
|
||||
|
||||
if (block->flags & ACPI_WMI_STRING) {
|
||||
params[2].type = ACPI_TYPE_STRING;
|
||||
} else {
|
||||
params[2].type = ACPI_TYPE_BUFFER;
|
||||
}
|
||||
params[2].type = get_param_acpi_type(wblock);
|
||||
params[2].buffer.length = in->length;
|
||||
params[2].buffer.pointer = in->pointer;
|
||||
}
|
||||
|
||||
strncat(method, block->object_id, 2);
|
||||
get_acpi_method_name(wblock, 'M', method);
|
||||
|
||||
status = acpi_evaluate_object(handle, method, &input, out);
|
||||
|
||||
return status;
|
||||
return acpi_evaluate_object(handle, method, &input, out);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wmidev_evaluate_method);
|
||||
|
||||
static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
|
||||
struct acpi_buffer *out)
|
||||
{
|
||||
struct guid_block *block = NULL;
|
||||
struct guid_block *block;
|
||||
acpi_handle handle;
|
||||
acpi_status status, wc_status = AE_ERROR;
|
||||
struct acpi_object_list input;
|
||||
union acpi_object wq_params[1];
|
||||
char method[5];
|
||||
char wc_method[5] = "WC";
|
||||
char wc_method[WMI_ACPI_METHOD_NAME_SIZE];
|
||||
char method[WMI_ACPI_METHOD_NAME_SIZE];
|
||||
|
||||
if (!out)
|
||||
return AE_BAD_PARAMETER;
|
||||
@ -333,7 +375,7 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
|
||||
* enable collection.
|
||||
*/
|
||||
if (block->flags & ACPI_WMI_EXPENSIVE) {
|
||||
strncat(wc_method, block->object_id, 2);
|
||||
get_acpi_method_name(wblock, 'C', wc_method);
|
||||
|
||||
/*
|
||||
* Some GUIDs break the specification by declaring themselves
|
||||
@ -343,9 +385,7 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
|
||||
wc_status = acpi_execute_simple_method(handle, wc_method, 1);
|
||||
}
|
||||
|
||||
strcpy(method, "WQ");
|
||||
strncat(method, block->object_id, 2);
|
||||
|
||||
get_acpi_method_name(wblock, 'Q', method);
|
||||
status = acpi_evaluate_object(handle, method, &input, out);
|
||||
|
||||
/*
|
||||
@ -353,7 +393,14 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
|
||||
* the WQxx method failed - we should disable collection anyway.
|
||||
*/
|
||||
if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
|
||||
status = acpi_execute_simple_method(handle, wc_method, 0);
|
||||
/*
|
||||
* Ignore whether this WCxx call succeeds or not since
|
||||
* the previously executed WQxx method call might have
|
||||
* succeeded, and returning the failing status code
|
||||
* of this call would throw away the result of the WQxx
|
||||
* call, potentially leaking memory.
|
||||
*/
|
||||
acpi_execute_simple_method(handle, wc_method, 0);
|
||||
}
|
||||
|
||||
return status;
|
||||
@ -371,12 +418,11 @@ acpi_status wmi_query_block(const char *guid_string, u8 instance,
|
||||
struct acpi_buffer *out)
|
||||
{
|
||||
struct wmi_block *wblock;
|
||||
acpi_status status;
|
||||
|
||||
if (!guid_string)
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
if (!find_guid(guid_string, &wblock))
|
||||
return AE_ERROR;
|
||||
status = find_guid(guid_string, &wblock);
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
return __query_block(wblock, instance, out);
|
||||
}
|
||||
@ -390,7 +436,7 @@ union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance)
|
||||
if (ACPI_FAILURE(__query_block(wblock, instance, &out)))
|
||||
return NULL;
|
||||
|
||||
return (union acpi_object *)out.pointer;
|
||||
return out.pointer;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wmidev_block_query);
|
||||
|
||||
@ -405,18 +451,20 @@ EXPORT_SYMBOL_GPL(wmidev_block_query);
|
||||
acpi_status wmi_set_block(const char *guid_string, u8 instance,
|
||||
const struct acpi_buffer *in)
|
||||
{
|
||||
struct guid_block *block = NULL;
|
||||
struct wmi_block *wblock = NULL;
|
||||
struct guid_block *block;
|
||||
acpi_handle handle;
|
||||
struct acpi_object_list input;
|
||||
union acpi_object params[2];
|
||||
char method[5] = "WS";
|
||||
char method[WMI_ACPI_METHOD_NAME_SIZE];
|
||||
acpi_status status;
|
||||
|
||||
if (!guid_string || !in)
|
||||
if (!in)
|
||||
return AE_BAD_DATA;
|
||||
|
||||
if (!find_guid(guid_string, &wblock))
|
||||
return AE_ERROR;
|
||||
status = find_guid(guid_string, &wblock);
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
block = &wblock->gblock;
|
||||
handle = wblock->acpi_device->handle;
|
||||
@ -432,16 +480,11 @@ acpi_status wmi_set_block(const char *guid_string, u8 instance,
|
||||
input.pointer = params;
|
||||
params[0].type = ACPI_TYPE_INTEGER;
|
||||
params[0].integer.value = instance;
|
||||
|
||||
if (block->flags & ACPI_WMI_STRING) {
|
||||
params[1].type = ACPI_TYPE_STRING;
|
||||
} else {
|
||||
params[1].type = ACPI_TYPE_BUFFER;
|
||||
}
|
||||
params[1].type = get_param_acpi_type(wblock);
|
||||
params[1].buffer.length = in->length;
|
||||
params[1].buffer.pointer = in->pointer;
|
||||
|
||||
strncat(method, block->object_id, 2);
|
||||
get_acpi_method_name(wblock, 'S', method);
|
||||
|
||||
return acpi_evaluate_object(handle, method, &input, NULL);
|
||||
}
|
||||
@ -449,7 +492,7 @@ EXPORT_SYMBOL_GPL(wmi_set_block);
|
||||
|
||||
static void wmi_dump_wdg(const struct guid_block *g)
|
||||
{
|
||||
pr_info("%pUL:\n", g->guid);
|
||||
pr_info("%pUL:\n", &g->guid);
|
||||
if (g->flags & ACPI_WMI_EVENT)
|
||||
pr_info("\tnotify_id: 0x%02X\n", g->notify_id);
|
||||
else
|
||||
@ -482,15 +525,14 @@ static void wmi_notify_debug(u32 value, void *context)
|
||||
return;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)response.pointer;
|
||||
|
||||
obj = response.pointer;
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
pr_info("DEBUG Event ");
|
||||
switch(obj->type) {
|
||||
pr_info("DEBUG: event 0x%02X ", value);
|
||||
switch (obj->type) {
|
||||
case ACPI_TYPE_BUFFER:
|
||||
pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length);
|
||||
pr_cont("BUFFER_TYPE - length %u\n", obj->buffer.length);
|
||||
break;
|
||||
case ACPI_TYPE_STRING:
|
||||
pr_cont("STRING_TYPE - %s\n", obj->string.pointer);
|
||||
@ -499,7 +541,7 @@ static void wmi_notify_debug(u32 value, void *context)
|
||||
pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value);
|
||||
break;
|
||||
case ACPI_TYPE_PACKAGE:
|
||||
pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count);
|
||||
pr_cont("PACKAGE_TYPE - %u elements\n", obj->package.count);
|
||||
break;
|
||||
default:
|
||||
pr_cont("object type 0x%X\n", obj->type);
|
||||
@ -516,7 +558,8 @@ static void wmi_notify_debug(u32 value, void *context)
|
||||
* Register a handler for events sent to the ACPI-WMI mapper device.
|
||||
*/
|
||||
acpi_status wmi_install_notify_handler(const char *guid,
|
||||
wmi_notify_handler handler, void *data)
|
||||
wmi_notify_handler handler,
|
||||
void *data)
|
||||
{
|
||||
struct wmi_block *block;
|
||||
acpi_status status = AE_NOT_EXIST;
|
||||
@ -531,7 +574,7 @@ wmi_notify_handler handler, void *data)
|
||||
list_for_each_entry(block, &wmi_block_list, list) {
|
||||
acpi_status wmi_status;
|
||||
|
||||
if (memcmp(block->gblock.guid, &guid_input, 16) == 0) {
|
||||
if (guid_equal(&block->gblock.guid, &guid_input)) {
|
||||
if (block->handler &&
|
||||
block->handler != wmi_notify_debug)
|
||||
return AE_ALREADY_ACQUIRED;
|
||||
@ -539,7 +582,7 @@ wmi_notify_handler handler, void *data)
|
||||
block->handler = handler;
|
||||
block->handler_data = data;
|
||||
|
||||
wmi_status = wmi_method_enable(block, 1);
|
||||
wmi_status = wmi_method_enable(block, true);
|
||||
if ((wmi_status != AE_OK) ||
|
||||
((wmi_status == AE_OK) && (status == AE_NOT_EXIST)))
|
||||
status = wmi_status;
|
||||
@ -551,7 +594,7 @@ wmi_notify_handler handler, void *data)
|
||||
EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
|
||||
|
||||
/**
|
||||
* wmi_uninstall_notify_handler - Unregister handler for WMI events
|
||||
* wmi_remove_notify_handler - Unregister handler for WMI events
|
||||
* @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
|
||||
*
|
||||
* Unregister handler for events sent to the ACPI-WMI mapper device.
|
||||
@ -571,7 +614,7 @@ acpi_status wmi_remove_notify_handler(const char *guid)
|
||||
list_for_each_entry(block, &wmi_block_list, list) {
|
||||
acpi_status wmi_status;
|
||||
|
||||
if (memcmp(block->gblock.guid, &guid_input, 16) == 0) {
|
||||
if (guid_equal(&block->gblock.guid, &guid_input)) {
|
||||
if (!block->handler ||
|
||||
block->handler == wmi_notify_debug)
|
||||
return AE_NULL_ENTRY;
|
||||
@ -580,7 +623,7 @@ acpi_status wmi_remove_notify_handler(const char *guid)
|
||||
block->handler = wmi_notify_debug;
|
||||
status = AE_OK;
|
||||
} else {
|
||||
wmi_status = wmi_method_enable(block, 0);
|
||||
wmi_status = wmi_method_enable(block, false);
|
||||
block->handler = NULL;
|
||||
block->handler_data = NULL;
|
||||
if ((wmi_status != AE_OK) ||
|
||||
@ -605,23 +648,13 @@ EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
|
||||
*/
|
||||
acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
|
||||
{
|
||||
struct acpi_object_list input;
|
||||
union acpi_object params[1];
|
||||
struct guid_block *gblock;
|
||||
struct wmi_block *wblock;
|
||||
|
||||
input.count = 1;
|
||||
input.pointer = params;
|
||||
params[0].type = ACPI_TYPE_INTEGER;
|
||||
params[0].integer.value = event;
|
||||
|
||||
list_for_each_entry(wblock, &wmi_block_list, list) {
|
||||
gblock = &wblock->gblock;
|
||||
struct guid_block *gblock = &wblock->gblock;
|
||||
|
||||
if ((gblock->flags & ACPI_WMI_EVENT) &&
|
||||
(gblock->notify_id == event))
|
||||
return acpi_evaluate_object(wblock->acpi_device->handle,
|
||||
"_WED", &input, out);
|
||||
if ((gblock->flags & ACPI_WMI_EVENT) && gblock->notify_id == event)
|
||||
return get_event_data(wblock, out);
|
||||
}
|
||||
|
||||
return AE_NOT_FOUND;
|
||||
@ -636,7 +669,7 @@ EXPORT_SYMBOL_GPL(wmi_get_event_data);
|
||||
*/
|
||||
bool wmi_has_guid(const char *guid_string)
|
||||
{
|
||||
return find_guid(guid_string, NULL);
|
||||
return ACPI_SUCCESS(find_guid(guid_string, NULL));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wmi_has_guid);
|
||||
|
||||
@ -651,8 +684,10 @@ EXPORT_SYMBOL_GPL(wmi_has_guid);
|
||||
char *wmi_get_acpi_device_uid(const char *guid_string)
|
||||
{
|
||||
struct wmi_block *wblock = NULL;
|
||||
acpi_status status;
|
||||
|
||||
if (!find_guid(guid_string, &wblock))
|
||||
status = find_guid(guid_string, &wblock);
|
||||
if (ACPI_FAILURE(status))
|
||||
return NULL;
|
||||
|
||||
return acpi_device_uid(wblock->acpi_device);
|
||||
@ -669,6 +704,11 @@ static struct wmi_device *dev_to_wdev(struct device *dev)
|
||||
return container_of(dev, struct wmi_device, dev);
|
||||
}
|
||||
|
||||
static inline struct wmi_driver *drv_to_wdrv(struct device_driver *drv)
|
||||
{
|
||||
return container_of(drv, struct wmi_driver, driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* sysfs interface
|
||||
*/
|
||||
@ -677,7 +717,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
|
||||
return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid);
|
||||
return sysfs_emit(buf, "wmi:%pUL\n", &wblock->gblock.guid);
|
||||
}
|
||||
static DEVICE_ATTR_RO(modalias);
|
||||
|
||||
@ -686,7 +726,7 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
|
||||
return sprintf(buf, "%pUL\n", wblock->gblock.guid);
|
||||
return sysfs_emit(buf, "%pUL\n", &wblock->gblock.guid);
|
||||
}
|
||||
static DEVICE_ATTR_RO(guid);
|
||||
|
||||
@ -695,7 +735,7 @@ static ssize_t instance_count_show(struct device *dev,
|
||||
{
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", (int)wblock->gblock.instance_count);
|
||||
return sysfs_emit(buf, "%d\n", (int)wblock->gblock.instance_count);
|
||||
}
|
||||
static DEVICE_ATTR_RO(instance_count);
|
||||
|
||||
@ -704,8 +744,8 @@ static ssize_t expensive_show(struct device *dev,
|
||||
{
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
(wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0);
|
||||
return sysfs_emit(buf, "%d\n",
|
||||
(wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0);
|
||||
}
|
||||
static DEVICE_ATTR_RO(expensive);
|
||||
|
||||
@ -714,7 +754,7 @@ static struct attribute *wmi_attrs[] = {
|
||||
&dev_attr_guid.attr,
|
||||
&dev_attr_instance_count.attr,
|
||||
&dev_attr_expensive.attr,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(wmi);
|
||||
|
||||
@ -723,13 +763,13 @@ static ssize_t notify_id_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
|
||||
return sprintf(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id);
|
||||
return sysfs_emit(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id);
|
||||
}
|
||||
static DEVICE_ATTR_RO(notify_id);
|
||||
|
||||
static struct attribute *wmi_event_attrs[] = {
|
||||
&dev_attr_notify_id.attr,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(wmi_event);
|
||||
|
||||
@ -738,8 +778,8 @@ static ssize_t object_id_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
|
||||
return sprintf(buf, "%c%c\n", wblock->gblock.object_id[0],
|
||||
wblock->gblock.object_id[1]);
|
||||
return sysfs_emit(buf, "%c%c\n", wblock->gblock.object_id[0],
|
||||
wblock->gblock.object_id[1]);
|
||||
}
|
||||
static DEVICE_ATTR_RO(object_id);
|
||||
|
||||
@ -748,20 +788,20 @@ static ssize_t setable_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct wmi_device *wdev = dev_to_wdev(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", (int)wdev->setable);
|
||||
return sysfs_emit(buf, "%d\n", (int)wdev->setable);
|
||||
}
|
||||
static DEVICE_ATTR_RO(setable);
|
||||
|
||||
static struct attribute *wmi_data_attrs[] = {
|
||||
&dev_attr_object_id.attr,
|
||||
&dev_attr_setable.attr,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(wmi_data);
|
||||
|
||||
static struct attribute *wmi_method_attrs[] = {
|
||||
&dev_attr_object_id.attr,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(wmi_method);
|
||||
|
||||
@ -769,10 +809,10 @@ static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
|
||||
if (add_uevent_var(env, "MODALIAS=wmi:%pUL", wblock->gblock.guid))
|
||||
if (add_uevent_var(env, "MODALIAS=wmi:%pUL", &wblock->gblock.guid))
|
||||
return -ENOMEM;
|
||||
|
||||
if (add_uevent_var(env, "WMI_GUID=%pUL", wblock->gblock.guid))
|
||||
if (add_uevent_var(env, "WMI_GUID=%pUL", &wblock->gblock.guid))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
@ -787,8 +827,7 @@ static void wmi_dev_release(struct device *dev)
|
||||
|
||||
static int wmi_dev_match(struct device *dev, struct device_driver *driver)
|
||||
{
|
||||
struct wmi_driver *wmi_driver =
|
||||
container_of(driver, struct wmi_driver, driver);
|
||||
struct wmi_driver *wmi_driver = drv_to_wdrv(driver);
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
const struct wmi_device_id *id = wmi_driver->id_table;
|
||||
|
||||
@ -800,7 +839,7 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver)
|
||||
|
||||
if (WARN_ON(guid_parse(id->guid_string, &driver_guid)))
|
||||
continue;
|
||||
if (!memcmp(&driver_guid, wblock->gblock.guid, 16))
|
||||
if (guid_equal(&driver_guid, &wblock->gblock.guid))
|
||||
return 1;
|
||||
|
||||
id++;
|
||||
@ -811,8 +850,8 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver)
|
||||
static int wmi_char_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
const char *driver_name = filp->f_path.dentry->d_iname;
|
||||
struct wmi_block *wblock = NULL;
|
||||
struct wmi_block *next = NULL;
|
||||
struct wmi_block *wblock;
|
||||
struct wmi_block *next;
|
||||
|
||||
list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
|
||||
if (!wblock->dev.dev.driver)
|
||||
@ -830,7 +869,7 @@ static int wmi_char_open(struct inode *inode, struct file *filp)
|
||||
}
|
||||
|
||||
static ssize_t wmi_char_read(struct file *filp, char __user *buffer,
|
||||
size_t length, loff_t *offset)
|
||||
size_t length, loff_t *offset)
|
||||
{
|
||||
struct wmi_block *wblock = filp->private_data;
|
||||
|
||||
@ -844,8 +883,8 @@ static long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
struct wmi_ioctl_buffer __user *input =
|
||||
(struct wmi_ioctl_buffer __user *) arg;
|
||||
struct wmi_block *wblock = filp->private_data;
|
||||
struct wmi_ioctl_buffer *buf = NULL;
|
||||
struct wmi_driver *wdriver = NULL;
|
||||
struct wmi_ioctl_buffer *buf;
|
||||
struct wmi_driver *wdriver;
|
||||
int ret;
|
||||
|
||||
if (_IOC_TYPE(cmd) != WMI_IOC)
|
||||
@ -885,8 +924,7 @@ static long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
}
|
||||
|
||||
/* let the driver do any filtering and do the call */
|
||||
wdriver = container_of(wblock->dev.dev.driver,
|
||||
struct wmi_driver, driver);
|
||||
wdriver = drv_to_wdrv(wblock->dev.dev.driver);
|
||||
if (!try_module_get(wdriver->driver.owner)) {
|
||||
ret = -EBUSY;
|
||||
goto out_ioctl;
|
||||
@ -919,12 +957,11 @@ static const struct file_operations wmi_fops = {
|
||||
static int wmi_dev_probe(struct device *dev)
|
||||
{
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
struct wmi_driver *wdriver =
|
||||
container_of(dev->driver, struct wmi_driver, driver);
|
||||
struct wmi_driver *wdriver = drv_to_wdrv(dev->driver);
|
||||
int ret = 0;
|
||||
char *buf;
|
||||
|
||||
if (ACPI_FAILURE(wmi_method_enable(wblock, 1)))
|
||||
if (ACPI_FAILURE(wmi_method_enable(wblock, true)))
|
||||
dev_warn(dev, "failed to enable device -- probing anyway\n");
|
||||
|
||||
if (wdriver->probe) {
|
||||
@ -975,7 +1012,7 @@ probe_misc_failure:
|
||||
probe_string_failure:
|
||||
kfree(wblock->handler_data);
|
||||
probe_failure:
|
||||
if (ACPI_FAILURE(wmi_method_enable(wblock, 0)))
|
||||
if (ACPI_FAILURE(wmi_method_enable(wblock, false)))
|
||||
dev_warn(dev, "failed to disable device\n");
|
||||
return ret;
|
||||
}
|
||||
@ -983,8 +1020,7 @@ probe_failure:
|
||||
static void wmi_dev_remove(struct device *dev)
|
||||
{
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
struct wmi_driver *wdriver =
|
||||
container_of(dev->driver, struct wmi_driver, driver);
|
||||
struct wmi_driver *wdriver = drv_to_wdrv(dev->driver);
|
||||
|
||||
if (wdriver->filter_callback) {
|
||||
misc_deregister(&wblock->char_dev);
|
||||
@ -995,7 +1031,7 @@ static void wmi_dev_remove(struct device *dev)
|
||||
if (wdriver->remove)
|
||||
wdriver->remove(dev_to_wdev(dev));
|
||||
|
||||
if (ACPI_FAILURE(wmi_method_enable(wblock, 0)))
|
||||
if (ACPI_FAILURE(wmi_method_enable(wblock, false)))
|
||||
dev_warn(dev, "failed to disable device\n");
|
||||
}
|
||||
|
||||
@ -1031,20 +1067,19 @@ static const struct device_type wmi_type_data = {
|
||||
};
|
||||
|
||||
static int wmi_create_device(struct device *wmi_bus_dev,
|
||||
const struct guid_block *gblock,
|
||||
struct wmi_block *wblock,
|
||||
struct acpi_device *device)
|
||||
{
|
||||
struct acpi_device_info *info;
|
||||
char method[5];
|
||||
char method[WMI_ACPI_METHOD_NAME_SIZE];
|
||||
int result;
|
||||
|
||||
if (gblock->flags & ACPI_WMI_EVENT) {
|
||||
if (wblock->gblock.flags & ACPI_WMI_EVENT) {
|
||||
wblock->dev.dev.type = &wmi_type_event;
|
||||
goto out_init;
|
||||
}
|
||||
|
||||
if (gblock->flags & ACPI_WMI_METHOD) {
|
||||
if (wblock->gblock.flags & ACPI_WMI_METHOD) {
|
||||
wblock->dev.dev.type = &wmi_type_method;
|
||||
mutex_init(&wblock->char_mutex);
|
||||
goto out_init;
|
||||
@ -1055,8 +1090,7 @@ static int wmi_create_device(struct device *wmi_bus_dev,
|
||||
* required per the WMI documentation. If it is not present,
|
||||
* we ignore this data block.
|
||||
*/
|
||||
strcpy(method, "WQ");
|
||||
strncat(method, wblock->gblock.object_id, 2);
|
||||
get_acpi_method_name(wblock, 'Q', method);
|
||||
result = get_subobj_info(device->handle, method, &info);
|
||||
|
||||
if (result) {
|
||||
@ -1083,8 +1117,7 @@ static int wmi_create_device(struct device *wmi_bus_dev,
|
||||
|
||||
kfree(info);
|
||||
|
||||
strcpy(method, "WS");
|
||||
strncat(method, wblock->gblock.object_id, 2);
|
||||
get_acpi_method_name(wblock, 'S', method);
|
||||
result = get_subobj_info(device->handle, method, NULL);
|
||||
|
||||
if (result == 0)
|
||||
@ -1094,7 +1127,7 @@ static int wmi_create_device(struct device *wmi_bus_dev,
|
||||
wblock->dev.dev.bus = &wmi_bus_type;
|
||||
wblock->dev.dev.parent = wmi_bus_dev;
|
||||
|
||||
dev_set_name(&wblock->dev.dev, "%pUL", gblock->guid);
|
||||
dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid);
|
||||
|
||||
device_initialize(&wblock->dev.dev);
|
||||
|
||||
@ -1114,12 +1147,12 @@ static void wmi_free_devices(struct acpi_device *device)
|
||||
}
|
||||
}
|
||||
|
||||
static bool guid_already_parsed(struct acpi_device *device, const u8 *guid)
|
||||
static bool guid_already_parsed(struct acpi_device *device, const guid_t *guid)
|
||||
{
|
||||
struct wmi_block *wblock;
|
||||
|
||||
list_for_each_entry(wblock, &wmi_block_list, list) {
|
||||
if (memcmp(wblock->gblock.guid, guid, 16) == 0) {
|
||||
if (guid_equal(&wblock->gblock.guid, guid)) {
|
||||
/*
|
||||
* Because we historically didn't track the relationship
|
||||
* between GUIDs and ACPI nodes, we don't know whether
|
||||
@ -1152,7 +1185,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device)
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENXIO;
|
||||
|
||||
obj = (union acpi_object *) out.pointer;
|
||||
obj = out.pointer;
|
||||
if (!obj)
|
||||
return -ENXIO;
|
||||
|
||||
@ -1174,10 +1207,10 @@ static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device)
|
||||
* case yet, so for now, we'll just ignore the duplicate
|
||||
* for device creation.
|
||||
*/
|
||||
if (guid_already_parsed(device, gblock[i].guid))
|
||||
if (guid_already_parsed(device, &gblock[i].guid))
|
||||
continue;
|
||||
|
||||
wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
|
||||
wblock = kzalloc(sizeof(*wblock), GFP_KERNEL);
|
||||
if (!wblock) {
|
||||
retval = -ENOMEM;
|
||||
break;
|
||||
@ -1186,7 +1219,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device)
|
||||
wblock->acpi_device = device;
|
||||
wblock->gblock = gblock[i];
|
||||
|
||||
retval = wmi_create_device(wmi_bus_dev, &gblock[i], wblock, device);
|
||||
retval = wmi_create_device(wmi_bus_dev, wblock, device);
|
||||
if (retval) {
|
||||
kfree(wblock);
|
||||
continue;
|
||||
@ -1196,7 +1229,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device)
|
||||
|
||||
if (debug_event) {
|
||||
wblock->handler = wmi_notify_debug;
|
||||
wmi_method_enable(wblock, 1);
|
||||
wmi_method_enable(wblock, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1211,9 +1244,9 @@ static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device)
|
||||
retval = device_add(&wblock->dev.dev);
|
||||
if (retval) {
|
||||
dev_err(wmi_bus_dev, "failed to register %pUL\n",
|
||||
wblock->gblock.guid);
|
||||
&wblock->gblock.guid);
|
||||
if (debug_event)
|
||||
wmi_method_enable(wblock, 0);
|
||||
wmi_method_enable(wblock, false);
|
||||
list_del(&wblock->list);
|
||||
put_device(&wblock->dev.dev);
|
||||
}
|
||||
@ -1230,8 +1263,8 @@ out_free_pointer:
|
||||
*/
|
||||
static acpi_status
|
||||
acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
|
||||
u32 bits, u64 *value,
|
||||
void *handler_context, void *region_context)
|
||||
u32 bits, u64 *value,
|
||||
void *handler_context, void *region_context)
|
||||
{
|
||||
int result = 0, i = 0;
|
||||
u8 temp = 0;
|
||||
@ -1268,17 +1301,15 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
|
||||
static void acpi_wmi_notify_handler(acpi_handle handle, u32 event,
|
||||
void *context)
|
||||
{
|
||||
struct guid_block *block;
|
||||
struct wmi_block *wblock;
|
||||
bool found_it = false;
|
||||
|
||||
list_for_each_entry(wblock, &wmi_block_list, list) {
|
||||
block = &wblock->gblock;
|
||||
struct guid_block *block = &wblock->gblock;
|
||||
|
||||
if (wblock->acpi_device->handle == handle &&
|
||||
(block->flags & ACPI_WMI_EVENT) &&
|
||||
(block->notify_id == event))
|
||||
{
|
||||
(block->notify_id == event)) {
|
||||
found_it = true;
|
||||
break;
|
||||
}
|
||||
@ -1289,31 +1320,18 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event,
|
||||
|
||||
/* If a driver is bound, then notify the driver. */
|
||||
if (wblock->dev.dev.driver) {
|
||||
struct wmi_driver *driver;
|
||||
struct acpi_object_list input;
|
||||
union acpi_object params[1];
|
||||
struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver);
|
||||
struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
acpi_status status;
|
||||
|
||||
driver = container_of(wblock->dev.dev.driver,
|
||||
struct wmi_driver, driver);
|
||||
|
||||
input.count = 1;
|
||||
input.pointer = params;
|
||||
params[0].type = ACPI_TYPE_INTEGER;
|
||||
params[0].integer.value = event;
|
||||
|
||||
status = acpi_evaluate_object(wblock->acpi_device->handle,
|
||||
"_WED", &input, &evdata);
|
||||
status = get_event_data(wblock, &evdata);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_warn(&wblock->dev.dev,
|
||||
"failed to get event data\n");
|
||||
dev_warn(&wblock->dev.dev, "failed to get event data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (driver->notify)
|
||||
driver->notify(&wblock->dev,
|
||||
(union acpi_object *)evdata.pointer);
|
||||
driver->notify(&wblock->dev, evdata.pointer);
|
||||
|
||||
kfree(evdata.pointer);
|
||||
} else if (wblock->handler) {
|
||||
@ -1322,25 +1340,24 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event,
|
||||
}
|
||||
|
||||
if (debug_event)
|
||||
pr_info("DEBUG Event GUID: %pUL\n", wblock->gblock.guid);
|
||||
pr_info("DEBUG: GUID %pUL event 0x%02X\n", &wblock->gblock.guid, event);
|
||||
|
||||
acpi_bus_generate_netlink_event(
|
||||
wblock->acpi_device->pnp.device_class,
|
||||
dev_name(&wblock->dev.dev),
|
||||
event, 0);
|
||||
|
||||
}
|
||||
|
||||
static int acpi_wmi_remove(struct platform_device *device)
|
||||
{
|
||||
struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev);
|
||||
|
||||
acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY,
|
||||
acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY,
|
||||
acpi_wmi_notify_handler);
|
||||
acpi_remove_address_space_handler(acpi_device->handle,
|
||||
ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
|
||||
wmi_free_devices(acpi_device);
|
||||
device_unregister((struct device *)dev_get_drvdata(&device->dev));
|
||||
device_unregister(dev_get_drvdata(&device->dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1368,7 +1385,7 @@ static int acpi_wmi_probe(struct platform_device *device)
|
||||
}
|
||||
|
||||
status = acpi_install_notify_handler(acpi_device->handle,
|
||||
ACPI_DEVICE_NOTIFY,
|
||||
ACPI_ALL_NOTIFY,
|
||||
acpi_wmi_notify_handler,
|
||||
NULL);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
@ -1397,7 +1414,7 @@ err_remove_busdev:
|
||||
device_unregister(wmi_bus_dev);
|
||||
|
||||
err_remove_notify_handler:
|
||||
acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY,
|
||||
acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY,
|
||||
acpi_wmi_notify_handler);
|
||||
|
||||
err_remove_ec_handler:
|
||||
|
@ -24,6 +24,64 @@ enum mlxreg_wdt_type {
|
||||
MLX_WDT_TYPE3,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum mlxreg_hotplug_kind - kind of hotplug entry
|
||||
*
|
||||
* @MLXREG_HOTPLUG_DEVICE_NA: do not care;
|
||||
* @MLXREG_HOTPLUG_LC_PRESENT: entry for line card presence in/out events;
|
||||
* @MLXREG_HOTPLUG_LC_VERIFIED: entry for line card verification status events
|
||||
* coming after line card security signature validation;
|
||||
* @MLXREG_HOTPLUG_LC_POWERED: entry for line card power on/off events;
|
||||
* @MLXREG_HOTPLUG_LC_SYNCED: entry for line card synchronization events, coming
|
||||
* after hardware-firmware synchronization handshake;
|
||||
* @MLXREG_HOTPLUG_LC_READY: entry for line card ready events, indicating line card
|
||||
PHYs ready / unready state;
|
||||
* @MLXREG_HOTPLUG_LC_ACTIVE: entry for line card active events, indicating firmware
|
||||
* availability / unavailability for the ports on line card;
|
||||
* @MLXREG_HOTPLUG_LC_THERMAL: entry for line card thermal shutdown events, positive
|
||||
* event indicates that system should power off the line
|
||||
* card for which this event has been received;
|
||||
*/
|
||||
enum mlxreg_hotplug_kind {
|
||||
MLXREG_HOTPLUG_DEVICE_NA = 0,
|
||||
MLXREG_HOTPLUG_LC_PRESENT = 1,
|
||||
MLXREG_HOTPLUG_LC_VERIFIED = 2,
|
||||
MLXREG_HOTPLUG_LC_POWERED = 3,
|
||||
MLXREG_HOTPLUG_LC_SYNCED = 4,
|
||||
MLXREG_HOTPLUG_LC_READY = 5,
|
||||
MLXREG_HOTPLUG_LC_ACTIVE = 6,
|
||||
MLXREG_HOTPLUG_LC_THERMAL = 7,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum mlxreg_hotplug_device_action - hotplug device action required for
|
||||
* driver's connectivity
|
||||
*
|
||||
* @MLXREG_HOTPLUG_DEVICE_DEFAULT_ACTION: probe device for 'on' event, remove
|
||||
* for 'off' event;
|
||||
* @MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION: probe platform device for 'on'
|
||||
* event, remove for 'off' event;
|
||||
* @MLXREG_HOTPLUG_DEVICE_NO_ACTION: no connectivity action is required;
|
||||
*/
|
||||
enum mlxreg_hotplug_device_action {
|
||||
MLXREG_HOTPLUG_DEVICE_DEFAULT_ACTION = 0,
|
||||
MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION = 1,
|
||||
MLXREG_HOTPLUG_DEVICE_NO_ACTION = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mlxreg_core_hotplug_notifier - hotplug notifier block:
|
||||
*
|
||||
* @identity: notifier identity name;
|
||||
* @handle: user handle to be passed by user handler function;
|
||||
* @user_handler: user handler function associated with the event;
|
||||
*/
|
||||
struct mlxreg_core_hotplug_notifier {
|
||||
char identity[MLXREG_CORE_LABEL_MAX_SIZE];
|
||||
void *handle;
|
||||
int (*user_handler)(void *handle, enum mlxreg_hotplug_kind kind, u8 action);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mlxreg_hotplug_device - I2C device data:
|
||||
*
|
||||
@ -31,6 +89,11 @@ enum mlxreg_wdt_type {
|
||||
* @client: I2C device client;
|
||||
* @brdinfo: device board information;
|
||||
* @nr: I2C device adapter number, to which device is to be attached;
|
||||
* @pdev: platform device, if device is instantiated as a platform device;
|
||||
* @action: action to be performed upon event receiving;
|
||||
* @handle: user handle to be passed by user handler function;
|
||||
* @user_handler: user handler function associated with the event;
|
||||
* @notifier: pointer to event notifier block;
|
||||
*
|
||||
* Structure represents I2C hotplug device static data (board topology) and
|
||||
* dynamic data (related kernel objects handles).
|
||||
@ -40,6 +103,11 @@ struct mlxreg_hotplug_device {
|
||||
struct i2c_client *client;
|
||||
struct i2c_board_info *brdinfo;
|
||||
int nr;
|
||||
struct platform_device *pdev;
|
||||
enum mlxreg_hotplug_device_action action;
|
||||
void *handle;
|
||||
int (*user_handler)(void *handle, enum mlxreg_hotplug_kind kind, u8 action);
|
||||
struct mlxreg_core_hotplug_notifier *notifier;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -51,12 +119,18 @@ struct mlxreg_hotplug_device {
|
||||
* @bit: attribute effective bit;
|
||||
* @capability: attribute capability register;
|
||||
* @reg_prsnt: attribute presence register;
|
||||
* @reg_sync: attribute synch register;
|
||||
* @reg_pwr: attribute power register;
|
||||
* @reg_ena: attribute enable register;
|
||||
* @mode: access mode;
|
||||
* @np - pointer to node platform associated with attribute;
|
||||
* @hpdev - hotplug device data;
|
||||
* @notifier: pointer to event notifier block;
|
||||
* @health_cntr: dynamic device health indication counter;
|
||||
* @attached: true if device has been attached after good health indication;
|
||||
* @regnum: number of registers occupied by multi-register attribute;
|
||||
* @slot: slot number, at which device is located;
|
||||
* @secured: if set indicates that entry access is secured;
|
||||
*/
|
||||
struct mlxreg_core_data {
|
||||
char label[MLXREG_CORE_LABEL_MAX_SIZE];
|
||||
@ -65,18 +139,25 @@ struct mlxreg_core_data {
|
||||
u32 bit;
|
||||
u32 capability;
|
||||
u32 reg_prsnt;
|
||||
u32 reg_sync;
|
||||
u32 reg_pwr;
|
||||
u32 reg_ena;
|
||||
umode_t mode;
|
||||
struct device_node *np;
|
||||
struct mlxreg_hotplug_device hpdev;
|
||||
struct mlxreg_core_hotplug_notifier *notifier;
|
||||
u32 health_cntr;
|
||||
bool attached;
|
||||
u8 regnum;
|
||||
u8 slot;
|
||||
u8 secured;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mlxreg_core_item - same type components controlled by the driver:
|
||||
*
|
||||
* @data: component data;
|
||||
* @kind: kind of hotplug attribute;
|
||||
* @aggr_mask: group aggregation mask;
|
||||
* @reg: group interrupt status register;
|
||||
* @mask: group interrupt mask;
|
||||
@ -89,6 +170,7 @@ struct mlxreg_core_data {
|
||||
*/
|
||||
struct mlxreg_core_item {
|
||||
struct mlxreg_core_data *data;
|
||||
enum mlxreg_hotplug_kind kind;
|
||||
u32 aggr_mask;
|
||||
u32 reg;
|
||||
u32 mask;
|
||||
|
65
include/linux/platform_data/x86/soc.h
Normal file
65
include/linux/platform_data/x86/soc.h
Normal file
@ -0,0 +1,65 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Helpers for Intel SoC model detection
|
||||
*
|
||||
* Copyright (c) 2019, Intel Corporation.
|
||||
*/
|
||||
|
||||
#ifndef __PLATFORM_DATA_X86_SOC_H
|
||||
#define __PLATFORM_DATA_X86_SOC_H
|
||||
|
||||
#if IS_ENABLED(CONFIG_X86)
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
|
||||
#define SOC_INTEL_IS_CPU(soc, type) \
|
||||
static inline bool soc_intel_is_##soc(void) \
|
||||
{ \
|
||||
static const struct x86_cpu_id soc##_cpu_ids[] = { \
|
||||
X86_MATCH_INTEL_FAM6_MODEL(type, NULL), \
|
||||
{} \
|
||||
}; \
|
||||
const struct x86_cpu_id *id; \
|
||||
\
|
||||
id = x86_match_cpu(soc##_cpu_ids); \
|
||||
if (id) \
|
||||
return true; \
|
||||
return false; \
|
||||
}
|
||||
|
||||
SOC_INTEL_IS_CPU(byt, ATOM_SILVERMONT);
|
||||
SOC_INTEL_IS_CPU(cht, ATOM_AIRMONT);
|
||||
SOC_INTEL_IS_CPU(apl, ATOM_GOLDMONT);
|
||||
SOC_INTEL_IS_CPU(glk, ATOM_GOLDMONT_PLUS);
|
||||
SOC_INTEL_IS_CPU(cml, KABYLAKE_L);
|
||||
|
||||
#else /* IS_ENABLED(CONFIG_X86) */
|
||||
|
||||
static inline bool soc_intel_is_byt(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool soc_intel_is_cht(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool soc_intel_is_apl(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool soc_intel_is_glk(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool soc_intel_is_cml(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* IS_ENABLED(CONFIG_X86) */
|
||||
|
||||
#endif /* __PLATFORM_DATA_X86_SOC_H */
|
@ -792,8 +792,8 @@ enum ssam_event_mask {
|
||||
#define SSAM_EVENT_REGISTRY_KIP \
|
||||
SSAM_EVENT_REGISTRY(SSAM_SSH_TC_KIP, 0x02, 0x27, 0x28)
|
||||
|
||||
#define SSAM_EVENT_REGISTRY_REG \
|
||||
SSAM_EVENT_REGISTRY(SSAM_SSH_TC_REG, 0x02, 0x01, 0x02)
|
||||
#define SSAM_EVENT_REGISTRY_REG(tid)\
|
||||
SSAM_EVENT_REGISTRY(SSAM_SSH_TC_REG, tid, 0x01, 0x02)
|
||||
|
||||
/**
|
||||
* enum ssam_event_notifier_flags - Flags for event notifiers.
|
||||
|
@ -9,34 +9,13 @@
|
||||
#ifndef _SND_SOC_INTEL_QUIRKS_H
|
||||
#define _SND_SOC_INTEL_QUIRKS_H
|
||||
|
||||
#include <linux/platform_data/x86/soc.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_X86)
|
||||
|
||||
#include <linux/dmi.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/iosf_mbi.h>
|
||||
|
||||
#define SOC_INTEL_IS_CPU(soc, type) \
|
||||
static inline bool soc_intel_is_##soc(void) \
|
||||
{ \
|
||||
static const struct x86_cpu_id soc##_cpu_ids[] = { \
|
||||
X86_MATCH_INTEL_FAM6_MODEL(type, NULL), \
|
||||
{} \
|
||||
}; \
|
||||
const struct x86_cpu_id *id; \
|
||||
\
|
||||
id = x86_match_cpu(soc##_cpu_ids); \
|
||||
if (id) \
|
||||
return true; \
|
||||
return false; \
|
||||
}
|
||||
|
||||
SOC_INTEL_IS_CPU(byt, ATOM_SILVERMONT);
|
||||
SOC_INTEL_IS_CPU(cht, ATOM_AIRMONT);
|
||||
SOC_INTEL_IS_CPU(apl, ATOM_GOLDMONT);
|
||||
SOC_INTEL_IS_CPU(glk, ATOM_GOLDMONT_PLUS);
|
||||
SOC_INTEL_IS_CPU(cml, KABYLAKE_L);
|
||||
|
||||
static inline bool soc_intel_is_byt_cr(struct platform_device *pdev)
|
||||
{
|
||||
/*
|
||||
@ -114,30 +93,6 @@ static inline bool soc_intel_is_byt_cr(struct platform_device *pdev)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool soc_intel_is_byt(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool soc_intel_is_cht(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool soc_intel_is_apl(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool soc_intel_is_glk(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool soc_intel_is_cml(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SND_SOC_INTEL_QUIRKS_H */
|
||||
#endif /* _SND_SOC_INTEL_QUIRKS_H */
|
||||
|
Loading…
Reference in New Issue
Block a user