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:
Linus Torvalds 2021-11-02 21:54:26 -07:00
commit 6ab1d4839a
41 changed files with 6206 additions and 598 deletions

View File

@ -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.

View File

@ -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
Dells 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]

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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);
}
}

View File

@ -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:

View 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, &regval);
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, &regval);
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, &regval);
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, &regval);
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, &regval) : 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");

View File

@ -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))

View File

@ -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,

View File

@ -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);

View File

@ -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,
},
{ }
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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,

View 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");

View File

@ -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);
}
/*

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);

View 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:*");

View File

@ -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

View 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");

View File

@ -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,

View File

@ -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) \

View File

@ -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 = &param;
// 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;

View File

@ -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)

View File

@ -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,

View File

@ -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 = &param,
};
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:

View File

@ -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;

View 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 */

View File

@ -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.

View File

@ -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 */