platform-drivers-x86 for v6.0-1

Highlights:
  -  Microsoft Surface:
     - SSAM hot unplug support
     - Surface Pro 8 keyboard cover support
     - Tablet mode switch support for Surface Pro 8 and Surface Laptop Studio
  -  thinkpad_acpi: AMD Automatice Mode Transitions (AMT) support
  -  Mellanox:
     - Vulcan chassis COMe NVSwitch management support
     - XH3000 support
  - New generic/shared Intel P2SB (Primary to Sideband) support
  - Lots of small cleanups
  - Various small bugfixes
  - Various new hardware ids / quirks additions
 
 The following is an automated git shortlog grouped by driver:
 
 ACPI:
  -  video: Fix acpi_video_handles_brightness_key_presses()
  -  video: Change how we determine if brightness key-presses are handled
 
 Documentation/ABI:
  -  Add new attributes for mlxreg-io sysfs interfaces
  -  mlxreg-io: Fix contact info
 
 Drop the PMC_ATOM Kconfig option:
  - Drop the PMC_ATOM Kconfig option
 
 EDAC, pnd2:
  -  convert to use common P2SB accessor
  -  Use proper I/O accessors and address space annotation
 
 HID:
  -  surface-hid: Add support for hot-removal
 
 ISST:
  -  PUNIT device mapping with Sub-NUMA clustering
 
 Kconfig:
  -  Remove unnecessary "if X86"
 
 MAINTAINERS:
  -  repair file entry in MICROSOFT SURFACE AGGREGATOR TABLET-MODE SWITCH
 
 Merge tag 'ib-mfd-edac-i2c-leds-pinctrl-platform-watchdog-v5.20' into review-hans:
  - Merge tag 'ib-mfd-edac-i2c-leds-pinctrl-platform-watchdog-v5.20' into review-hans
 
 Move AMD platform drivers to separate directory:
  - Move AMD platform drivers to separate directory
 
 acer-wmi:
  -  Use backlight helper
 
 acer_wmi:
  -  Cleanup Kconfig selects
 
 apple-gmux:
  -  Use backlight helper
 
 asus-wmi:
  -  Add mic-mute LED classdev support
  -  Add key mappings
 
 compal-laptop:
  -  Use backlight helper
 
 efi:
  -  Fix efi_power_off() not being run before acpi_power_off() when necessary
 
 gigabyte-wmi:
  -  add support for B660I AORUS PRO DDR4
 
 hp-wmi:
  -  Ignore Sanitization Mode event
 
 i2c:
  -  i801: convert to use common P2SB accessor
 
 ideapad-laptop:
  -  Add Ideapad 5 15ITL05 to ideapad_dytc_v4_allow_table[]
  -  Add allow_v4_dytc module parameter
 
 intel/pmc:
  -  Add Alder Lake N support to PMC core driver
 
 intel_atomisp2_led:
  -  Also turn off the always-on camera LED on the Asus T100TAF
 
 leds:
  -  simatic-ipc-leds-gpio: Add GPIO version of Siemens driver
  -  simatic-ipc-leds: Convert to use P2SB accessor
 
 mfd:
  -  lpc_ich: Add support for pinctrl in non-ACPI system
  -  lpc_ich: Switch to generic p2sb_bar()
  -  lpc_ich: Factor out lpc_ich_enable_spi_write()
 
 mlx-platform:
  -  Add COME board revision register
  -  Add support for new system XH3000
  -  Introduce support for COMe NVSwitch management module for Vulcan chassis
  -  Add support for systems equipped with two ASICs
  -  Add cosmetic changes for alignment
  -  Make activation of some drivers conditional
 
 p2sb:
  -  Move out of X86_PLATFORM_DEVICES dependency
 
 panasonic-laptop:
  -  Use acpi_video_get_backlight_type()
  -  filter out duplicate volume up/down/mute keypresses
  -  don't report duplicate brightness key-presses
  -  revert "Resolve hotkey double trigger bug"
  -  sort includes alphabetically
  -  de-obfuscate button codes
 
 pinctrl:
  -  intel: Check against matching data instead of ACPI companion
 
 platform/mellanox:
  -  mlxreg-lc: Fix error flow and extend verbosity
  -  mlxreg-io: Add locking for io operations
  -  nvsw-sn2201: fix error code in nvsw_sn2201_create_static_devices()
 
 platform/olpc:
  -  Fix uninitialized data in debugfs write
 
 platform/surface:
  -  gpe: Add support for 13" Intel version of Surface Laptop 4
  -  tabletsw: Fix __le32 integer access
  -  Update copyright year of various drivers
  -  aggregator: Move subsystem hub drivers to their own module
  -  aggregator: Move device registry helper functions to core module
  -  aggregator_registry: Add support for tablet mode switch on Surface Laptop Studio
  -  aggregator_registry: Add support for tablet mode switch on Surface Pro 8
  -  Add KIP/POS tablet-mode switch driver
  -  aggregator: Add helper macros for requests with argument and return value
  -  aggregator: Reserve more event- and target-categories
  -  avoid flush_scheduled_work() usage
  -  aggregator_registry: Add support for keyboard cover on Surface Pro 8
  -  aggregator_registry: Add KIP device hub
  -  aggregator_registry: Change device ID for base hub
  -  aggregator_registry: Generify subsystem hub functionality
  -  aggregator: Add comment for KIP subsystem category
  -  aggregator_registry: Use client device wrappers for notifier registration
  -  aggregator: Allow notifiers to avoid communication on unregistering
  -  aggregator: Allow devices to be marked as hot-removed
  -  aggregator: Allow is_ssam_device() to be used when CONFIG_SURFACE_AGGREGATOR_BUS is disabled
 
 platform/x86/amd/pmc:
  -  Add new platform support
  -  Add new acpi id for PMC controller
 
 platform/x86/dell:
  -  Kconfig: Remove unnecessary "depends on X86_PLATFORM_DEVICES"
 
 platform/x86/intel:
  -  Add Primary to Sideband (P2SB) bridge support
 
 platform/x86/intel/ifs:
  -  Mark as BROKEN
 
 platform/x86/intel/pmt:
  -  telemetry: Fix fixed region handling
 
 platform/x86/intel/vsec:
  -  Fix wrong type for local status variables
  -  Add PCI error recovery support to Intel PMT
  -  Add support for Raptor Lake
  -  Rework early hardware code
 
 pmc_atom:
  -  Fix comment typo
  -  Match all Lex BayTrail boards with critclk_systems DMI table
 
 power/supply:
  -  surface_battery: Use client device wrappers for notifier registration
  -  surface_charger: Use client device wrappers for notifier registration
 
 serial-multi-instantiate:
  -  Sort ACPI IDs by HID
  -  Get rid of redundant 'else'
  -  Use while (i--) pattern to clean up
  -  Improve dev_err_probe() messaging
  -  Drop duplicate check
  -  Improve autodetection
 
 simatic-ipc:
  -  drop custom P2SB bar code
 
 sony-laptop:
  -  Remove useless comparisons in sony_pic_read_possible_resource()
 
 system76_acpi:
  -  Use dev_get_drvdata
 
 thinkpad_acpi:
  -  Enable AMT by default on supported systems
  -  Add support for hotkey 0x131a
  -  Add support for automatic mode transitions
  -  profile capabilities as integer
  -  do not use PSC mode on Intel platforms
  -  Fix a memory leak of EFCH MMIO resource
  -  Replace custom str_on_off() etc
  -  Sort headers for better maintenance
  -  Use backlight helper
 
 tools/power/x86/intel-speed-select:
  -  Remove unneeded semicolon
  -  Fix off by one check
 
 watchdog:
  -  simatic-ipc-wdt: convert to use P2SB accessor
 
 x86-android-tablets:
  -  Fix Lenovo Yoga Tablet 2 830/1050 poweroff again
 -----BEGIN PGP SIGNATURE-----
 
 iQFIBAABCAAyFiEEuvA7XScYQRpenhd+kuxHeUQDJ9wFAmLrndEUHGhkZWdvZWRl
 QHJlZGhhdC5jb20ACgkQkuxHeUQDJ9zeMgf7BjSCz6ZA8SSY1i8QHDTvdjySihHJ
 j07Gn3j1T/5G00R/r6viMDE4PxcYvMAPXjq3azepKQd8H5kGfE323SA6fgWFPAvi
 P2OvEfvWfI5S8FYGYPBkNP2MjQ5MFe7qzLEh3+wQH0ocJ7WRCi457B4Xvtd2gWI3
 dHj5gMSWC3O5xNa2S4Mg3dnD9uJlwhX+FNjWIuRy8eh5+DikgByyC4B+uW6WtO5e
 t0rmIm6q5wUzB7dIetJLoAQwrcpYAOkK7L33G9h/7knWAfiJfklaKTbftnoxbDSv
 iGWODkLDyob4C48DmVusS6WMEhPUzl/R33+tk6LjVt/YOYOP030EMtCECQ==
 =Krhc
 -----END PGP SIGNATURE-----

Merge tag 'platform-drivers-x86-v6.0-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86

Pull x86 platform driver updates from Hans de Goede:

 - Microsoft Surface:
     - SSAM hot unplug support
     - Surface Pro 8 keyboard cover support
     - Tablet mode switch support for Surface Pro 8 and Surface Laptop
       Studio

 - thinkpad_acpi:
     - AMD Automatice Mode Transitions (AMT) support

 - Mellanox:
     - Vulcan chassis COMe NVSwitch management support
     - XH3000 support

 - New generic/shared Intel P2SB (Primary to Sideband) support

 - Lots of small cleanups

 - Various small bugfixes

 - Various new hardware ids / quirks additions

* tag 'platform-drivers-x86-v6.0-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (105 commits)
  platform/x86/intel/vsec: Fix wrong type for local status variables
  platform/x86: p2sb: Move out of X86_PLATFORM_DEVICES dependency
  platform/x86: pmc_atom: Fix comment typo
  platform/surface: gpe: Add support for 13" Intel version of Surface Laptop 4
  platform/olpc: Fix uninitialized data in debugfs write
  platform/mellanox: mlxreg-lc: Fix error flow and extend verbosity
  platform/x86: pmc_atom: Match all Lex BayTrail boards with critclk_systems DMI table
  platform/x86: sony-laptop: Remove useless comparisons in sony_pic_read_possible_resource()
  tools/power/x86/intel-speed-select: Remove unneeded semicolon
  tools/power/x86/intel-speed-select: Fix off by one check
  platform/surface: tabletsw: Fix __le32 integer access
  Documentation/ABI: Add new attributes for mlxreg-io sysfs interfaces
  Documentation/ABI: mlxreg-io: Fix contact info
  platform/mellanox: mlxreg-io: Add locking for io operations
  platform/x86: mlx-platform: Add COME board revision register
  platform/x86: mlx-platform: Add support for new system XH3000
  platform/x86: mlx-platform: Introduce support for COMe NVSwitch management module for Vulcan chassis
  platform/x86: mlx-platform: Add support for systems equipped with two ASICs
  platform/x86: mlx-platform: Add cosmetic changes for alignment
  platform/x86: mlx-platform: Make activation of some drivers conditional
  ...
This commit is contained in:
Linus Torvalds 2022-08-04 18:19:14 -07:00
commit 5f0848190c
85 changed files with 3267 additions and 1032 deletions

View File

@ -1,7 +1,7 @@
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic_health
Date: June 2018
KernelVersion: 4.19
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file shows ASIC health status. The possible values are:
0 - health failed, 2 - health OK, 3 - ASIC in booting state.
@ -11,7 +11,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld1_version
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld2_version
Date: June 2018
KernelVersion: 4.19
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: These files show with which CPLD versions have been burned
on carrier and switch boards.
@ -20,7 +20,7 @@ Description: These files show with which CPLD versions have been burned
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/fan_dir
Date: December 2018
KernelVersion: 5.0
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file shows the system fans direction:
forward direction - relevant bit is set 0;
reversed direction - relevant bit is set 1.
@ -30,7 +30,7 @@ Description: This file shows the system fans direction:
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld3_version
Date: November 2018
KernelVersion: 5.0
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: These files show with which CPLD versions have been burned
on LED or Gearbox board.
@ -39,7 +39,7 @@ Description: These files show with which CPLD versions have been burned
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/jtag_enable
Date: November 2018
KernelVersion: 5.0
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: These files enable and disable the access to the JTAG domain.
By default access to the JTAG domain is disabled.
@ -48,7 +48,7 @@ Description: These files enable and disable the access to the JTAG domain.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/select_iio
Date: June 2018
KernelVersion: 4.19
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file allows iio devices selection.
Attribute select_iio can be written with 0 or with 1. It
@ -62,7 +62,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/psu1_on
/sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pwr_down
Date: June 2018
KernelVersion: 4.19
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: These files allow asserting system power cycling, switching
power supply units on and off and system's main power domain
shutdown.
@ -89,7 +89,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_short_pb
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_sw_reset
Date: June 2018
KernelVersion: 4.19
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: These files show the system reset cause, as following: power
auxiliary outage or power refresh, ASIC thermal shutdown, halt,
hotswap, watchdog, firmware reset, long press power button,
@ -106,7 +106,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_system
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_voltmon_upgrade_fail
Date: November 2018
KernelVersion: 5.0
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: These files show the system reset cause, as following: ComEx
power fail, reset from ComEx, system platform reset, reset
due to voltage monitor devices upgrade failure,
@ -119,7 +119,7 @@ Description: These files show the system reset cause, as following: ComEx
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld4_version
Date: November 2018
KernelVersion: 5.0
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: These files show with which CPLD versions have been burned
on LED board.
@ -133,7 +133,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_sff_wd
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_swb_wd
Date: June 2019
KernelVersion: 5.3
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: These files show the system reset cause, as following:
COMEX thermal shutdown; wathchdog power off or reset was derived
by one of the next components: COMEX, switch board or by Small Form
@ -148,7 +148,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/config1
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/config2
Date: January 2020
KernelVersion: 5.6
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: These files show system static topology identification
like system's static I2C topology, number and type of FPGA
devices within the system and so on.
@ -161,7 +161,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_soc
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_sw_pwr_off
Date: January 2020
KernelVersion: 5.6
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: These files show the system reset causes, as following: reset
due to AC power failure, reset invoked from software by
assertion reset signal through CPLD. reset caused by signal
@ -173,7 +173,7 @@ Description: These files show the system reset causes, as following: reset
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pcie_asic_reset_dis
Date: January 2020
KernelVersion: 5.6
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file allows to retain ASIC up during PCIe root complex
reset, when attribute is set 1.
@ -182,7 +182,7 @@ Description: This file allows to retain ASIC up during PCIe root complex
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/vpd_wp
Date: January 2020
KernelVersion: 5.6
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file allows to overwrite system VPD hardware write
protection when attribute is set 1.
@ -191,7 +191,7 @@ Description: This file allows to overwrite system VPD hardware write
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/voltreg_update_status
Date: January 2020
KernelVersion: 5.6
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file exposes the configuration update status of burnable
voltage regulator devices. The status values are as following:
0 - OK; 1 - CRC failure; 2 = I2C failure; 3 - in progress.
@ -201,7 +201,7 @@ Description: This file exposes the configuration update status of burnable
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/ufm_version
Date: January 2020
KernelVersion: 5.6
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file exposes the firmware version of burnable voltage
regulator devices.
@ -217,7 +217,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld3_version_min
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld4_version_min
Date: July 2020
KernelVersion: 5.9
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: These files show with which CPLD part numbers and minor
versions have been burned CPLD devices equipped on a
system.
@ -471,7 +471,7 @@ Description: These files provide the maximum powered required for line card
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/phy_reset
Date: May 2022
KernelVersion: 5.19
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file allows to reset PHY 88E1548 when attribute is set 0
due to some abnormal PHY behavior.
Expected behavior:
@ -483,7 +483,7 @@ Description: This file allows to reset PHY 88E1548 when attribute is set 0
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/mac_reset
Date: May 2022
KernelVersion: 5.19
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file allows to reset ASIC MT52132 when attribute is set 0
due to some abnormal ASIC behavior.
Expected behavior:
@ -495,7 +495,7 @@ Description: This file allows to reset ASIC MT52132 when attribute is set 0
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/qsfp_pwr_good
Date: May 2022
KernelVersion: 5.19
Contact: Vadim Pasternak <vadimpmellanox.com>
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file shows QSFP ports power status. The value is set to 0
when one of any QSFP ports is plugged. The value is set to 1 when
there are no any QSFP ports are plugged.
@ -503,3 +503,42 @@ Description: This file shows QSFP ports power status. The value is set to 0
0 - Power good, 1 - Not power good.
The files are read only.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic2_health
Date: July 2022
KernelVersion: 5.20
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file shows 2-nd ASIC health status. The possible values are:
0 - health failed, 2 - health OK, 3 - ASIC in booting state.
The file is read only.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic_reset
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic2_reset
Date: July 2022
KernelVersion: 5.20
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: These files allow to each of ASICs by writing 1.
The files are write only.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/comm_chnl_ready
Date: July 2022
KernelVersion: 5.20
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file is used to indicate remote end (for example BMC) that system
host CPU is ready for sending telemetry data to remote end.
For indication the file should be written 1.
The file is write only.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/config3
Date: January 2020
KernelVersion: 5.6
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: The file indicates COME module hardware configuration.
The value is pushed by hardware through GPIO pins.
The purpose is to expose some minor BOM changes for the same system SKU.
The file is read only.

View File

@ -0,0 +1,57 @@
What: /sys/bus/surface_aggregator/devices/01:0e:01:00:01/state
Date: July 2022
KernelVersion: 5.20
Contact: Maximilian Luz <luzmaximilian@gmail.com>
Description:
This attribute returns a string with the current type-cover
or device posture, as indicated by the embedded controller.
Currently returned posture states are:
- "disconnected": The type-cover has been disconnected.
- "closed": The type-cover has been folded closed and lies on
top of the display.
- "laptop": The type-cover is open and in laptop-mode, i.e.,
ready for normal use.
- "folded-canvas": The type-cover has been folded back
part-ways, but does not lie flush with the back side of the
device. In general, this means that the kick-stand is used
and extended atop of the cover.
- "folded-back": The type cover has been fully folded back and
lies flush with the back side of the device.
- "<unknown>": The current state is unknown to the driver, for
example due to newer as-of-yet unsupported hardware.
New states may be introduced with new hardware. Users therefore
must not rely on this list of states being exhaustive and
gracefully handle unknown states.
What: /sys/bus/surface_aggregator/devices/01:26:01:00:01/state
Date: July 2022
KernelVersion: 5.20
Contact: Maximilian Luz <luzmaximilian@gmail.com>
Description:
This attribute returns a string with the current device posture, as indicated by the embedded controller. Currently
returned posture states are:
- "closed": The lid of the device is closed.
- "laptop": The lid of the device is opened and the device
operates as a normal laptop.
- "slate": The screen covers the keyboard or has been flipped
back and the device operates mainly based on touch input.
- "tablet": The device operates as tablet and exclusively
relies on touch input (or external peripherals).
- "<unknown>": The current state is unknown to the driver, for
example due to newer as-of-yet unsupported hardware.
New states may be introduced with new hardware. Users therefore
must not rely on this list of states being exhaustive and
gracefully handle unknown states.

View File

@ -17,6 +17,8 @@
.. |SSAM_DEVICE| replace:: :c:func:`SSAM_DEVICE`
.. |ssam_notifier_register| replace:: :c:func:`ssam_notifier_register`
.. |ssam_notifier_unregister| replace:: :c:func:`ssam_notifier_unregister`
.. |ssam_device_notifier_register| replace:: :c:func:`ssam_device_notifier_register`
.. |ssam_device_notifier_unregister| replace:: :c:func:`ssam_device_notifier_unregister`
.. |ssam_request_sync| replace:: :c:func:`ssam_request_sync`
.. |ssam_event_mask| replace:: :c:type:`enum ssam_event_mask <ssam_event_mask>`
@ -312,7 +314,9 @@ Handling Events
To receive events from the SAM EC, an event notifier must be registered for
the desired event via |ssam_notifier_register|. The notifier must be
unregistered via |ssam_notifier_unregister| once it is not required any
more.
more. For |ssam_device| type clients, the |ssam_device_notifier_register| and
|ssam_device_notifier_unregister| wrappers should be preferred as they properly
handle hot-removal of client devices.
Event notifiers are registered by providing (at minimum) a callback to call
in case an event has been received, the registry specifying how the event

View File

@ -1007,7 +1007,7 @@ AMD PMC DRIVER
M: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/amd-pmc.*
F: drivers/platform/x86/amd/pmc.c
AMD HSMP DRIVER
M: Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com>
@ -1017,7 +1017,7 @@ S: Maintained
F: Documentation/x86/amd_hsmp.rst
F: arch/x86/include/asm/amd_hsmp.h
F: arch/x86/include/uapi/asm/amd_hsmp.h
F: drivers/platform/x86/amd_hsmp.c
F: drivers/platform/x86/amd/hsmp.c
AMD POWERPLAY AND SWSMU
M: Evan Quan <evan.quan@amd.com>
@ -13467,6 +13467,12 @@ F: drivers/scsi/smartpqi/smartpqi*.[ch]
F: include/linux/cciss*.h
F: include/uapi/linux/cciss*.h
MICROSOFT SURFACE AGGREGATOR TABLET-MODE SWITCH
M: Maximilian Luz <luzmaximilian@gmail.com>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/surface/surface_aggregator_tabletsw.c
MICROSOFT SURFACE BATTERY AND AC DRIVERS
M: Maximilian Luz <luzmaximilian@gmail.com>
L: linux-pm@vger.kernel.org
@ -13538,6 +13544,12 @@ F: include/linux/surface_acpi_notify.h
F: include/linux/surface_aggregator/
F: include/uapi/linux/surface_aggregator/
MICROSOFT SURFACE SYSTEM AGGREGATOR HUB DRIVER
M: Maximilian Luz <luzmaximilian@gmail.com>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/surface/surface_aggregator_hub.c
MICROTEK X6 SCANNER
M: Oliver Neukum <oliver@neukum.org>
S: Maintained

View File

@ -1,6 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_PMC_ATOM) += clk-pmc-atom.o
obj-$(CONFIG_X86_AMD_PLATFORM_DEVICE) += clk-fch.o
clk-x86-lpss-y := clk-lpss-atom.o
obj-$(CONFIG_X86_INTEL_LPSS) += clk-x86-lpss.o
obj-$(CONFIG_X86_INTEL_LPSS) += clk-lpss-atom.o clk-pmc-atom.o
obj-$(CONFIG_CLK_LGM_CGU) += clk-cgu.o clk-cgu-pll.o clk-lgm.o

View File

@ -263,6 +263,7 @@ config EDAC_I10NM
config EDAC_PND2
tristate "Intel Pondicherry2"
depends on PCI && X86_64 && X86_MCE_INTEL
select P2SB if X86
help
Support for error detection and correction on the Intel
Pondicherry2 Integrated Memory Controller. This SoC IP is

View File

@ -28,6 +28,8 @@
#include <linux/bitmap.h>
#include <linux/math64.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_data/x86/p2sb.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include <asm/processor.h>
@ -232,42 +234,14 @@ static u64 get_mem_ctrl_hub_base_addr(void)
return U64_LSHIFT(hi.base, 32) | U64_LSHIFT(lo.base, 15);
}
static u64 get_sideband_reg_base_addr(void)
{
struct pci_dev *pdev;
u32 hi, lo;
u8 hidden;
pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x19dd, NULL);
if (pdev) {
/* Unhide the P2SB device, if it's hidden */
pci_read_config_byte(pdev, 0xe1, &hidden);
if (hidden)
pci_write_config_byte(pdev, 0xe1, 0);
pci_read_config_dword(pdev, 0x10, &lo);
pci_read_config_dword(pdev, 0x14, &hi);
lo &= 0xfffffff0;
/* Hide the P2SB device, if it was hidden before */
if (hidden)
pci_write_config_byte(pdev, 0xe1, hidden);
pci_dev_put(pdev);
return (U64_LSHIFT(hi, 32) | U64_LSHIFT(lo, 0));
} else {
return 0xfd000000;
}
}
#define DNV_MCHBAR_SIZE 0x8000
#define DNV_SB_PORT_SIZE 0x10000
static int dnv_rd_reg(int port, int off, int op, void *data, size_t sz, char *name)
{
struct pci_dev *pdev;
char *base;
u64 addr;
unsigned long size;
void __iomem *base;
struct resource r;
int ret;
if (op == 4) {
pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x1980, NULL);
@ -279,26 +253,30 @@ static int dnv_rd_reg(int port, int off, int op, void *data, size_t sz, char *na
} else {
/* MMIO via memory controller hub base address */
if (op == 0 && port == 0x4c) {
addr = get_mem_ctrl_hub_base_addr();
if (!addr)
memset(&r, 0, sizeof(r));
r.start = get_mem_ctrl_hub_base_addr();
if (!r.start)
return -ENODEV;
size = DNV_MCHBAR_SIZE;
r.end = r.start + DNV_MCHBAR_SIZE - 1;
} else {
/* MMIO via sideband register base address */
addr = get_sideband_reg_base_addr();
if (!addr)
return -ENODEV;
addr += (port << 16);
size = DNV_SB_PORT_SIZE;
ret = p2sb_bar(NULL, 0, &r);
if (ret)
return ret;
r.start += (port << 16);
r.end = r.start + DNV_SB_PORT_SIZE - 1;
}
base = ioremap((resource_size_t)addr, size);
base = ioremap(r.start, resource_size(&r));
if (!base)
return -ENODEV;
if (sz == 8)
*(u32 *)(data + 4) = *(u32 *)(base + off + 4);
*(u32 *)data = *(u32 *)(base + off);
*(u64 *)data = readq(base + off);
else
*(u32 *)data = readl(base + off);
iounmap(base);
}

View File

@ -19,12 +19,30 @@
#include "surface_hid_core.h"
/* -- Utility functions. ---------------------------------------------------- */
static bool surface_hid_is_hot_removed(struct surface_hid_device *shid)
{
/*
* Non-ssam client devices, i.e. platform client devices, cannot be
* hot-removed.
*/
if (!is_ssam_device(shid->dev))
return false;
return ssam_device_is_hot_removed(to_ssam_device(shid->dev));
}
/* -- Device descriptor access. --------------------------------------------- */
static int surface_hid_load_hid_descriptor(struct surface_hid_device *shid)
{
int status;
if (surface_hid_is_hot_removed(shid))
return -ENODEV;
status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_HID,
(u8 *)&shid->hid_desc, sizeof(shid->hid_desc));
if (status)
@ -61,6 +79,9 @@ static int surface_hid_load_device_attributes(struct surface_hid_device *shid)
{
int status;
if (surface_hid_is_hot_removed(shid))
return -ENODEV;
status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_ATTRS,
(u8 *)&shid->attrs, sizeof(shid->attrs));
if (status)
@ -88,9 +109,18 @@ static int surface_hid_start(struct hid_device *hid)
static void surface_hid_stop(struct hid_device *hid)
{
struct surface_hid_device *shid = hid->driver_data;
bool hot_removed;
/*
* Communication may fail for devices that have been hot-removed. This
* also includes unregistration of HID events, so we need to check this
* here. Only if the device has not been marked as hot-removed, we can
* safely disable events.
*/
hot_removed = surface_hid_is_hot_removed(shid);
/* Note: This call will log errors for us, so ignore them here. */
ssam_notifier_unregister(shid->ctrl, &shid->notif);
__ssam_notifier_unregister(shid->ctrl, &shid->notif, !hot_removed);
}
static int surface_hid_open(struct hid_device *hid)
@ -109,6 +139,9 @@ static int surface_hid_parse(struct hid_device *hid)
u8 *buf;
int status;
if (surface_hid_is_hot_removed(shid))
return -ENODEV;
buf = kzalloc(len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@ -126,6 +159,9 @@ static int surface_hid_raw_request(struct hid_device *hid, unsigned char reportn
{
struct surface_hid_device *shid = hid->driver_data;
if (surface_hid_is_hot_removed(shid))
return -ENODEV;
if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT)
return shid->ops.output_report(shid, reportnum, buf, len);

View File

@ -108,6 +108,7 @@ config I2C_HIX5HD2
config I2C_I801
tristate "Intel 82801 (ICH/PCH)"
depends on PCI
select P2SB if X86
select CHECK_SIGNATURE if X86 && DMI
select I2C_SMBUS
help

View File

@ -112,6 +112,7 @@
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/platform_data/itco_wdt.h>
#include <linux/platform_data/x86/p2sb.h>
#include <linux/pm_runtime.h>
#include <linux/mutex.h>
@ -141,7 +142,6 @@
#define TCOBASE 0x050
#define TCOCTL 0x054
#define SBREG_BAR 0x10
#define SBREG_SMBCTRL 0xc6000c
#define SBREG_SMBCTRL_DNV 0xcf000c
@ -1485,45 +1485,24 @@ i801_add_tco_spt(struct i801_priv *priv, struct pci_dev *pci_dev,
.version = 4,
};
struct resource *res;
unsigned int devfn;
u64 base64_addr;
u32 base_addr;
u8 hidden;
int ret;
/*
* We must access the NO_REBOOT bit over the Primary to Sideband
* bridge (P2SB). The BIOS prevents the P2SB device from being
* enumerated by the PCI subsystem, so we need to unhide/hide it
* to lookup the P2SB BAR.
* (P2SB) bridge.
*/
pci_lock_rescan_remove();
devfn = PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 1);
/* Unhide the P2SB device, if it is hidden */
pci_bus_read_config_byte(pci_dev->bus, devfn, 0xe1, &hidden);
if (hidden)
pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x0);
pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR, &base_addr);
base64_addr = base_addr & 0xfffffff0;
pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR + 0x4, &base_addr);
base64_addr |= (u64)base_addr << 32;
/* Hide the P2SB device, if it was hidden before */
if (hidden)
pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, hidden);
pci_unlock_rescan_remove();
res = &tco_res[1];
ret = p2sb_bar(pci_dev->bus, 0, res);
if (ret)
return ERR_PTR(ret);
if (pci_dev->device == PCI_DEVICE_ID_INTEL_DNV_SMBUS)
res->start = (resource_size_t)base64_addr + SBREG_SMBCTRL_DNV;
res->start += SBREG_SMBCTRL_DNV;
else
res->start = (resource_size_t)base64_addr + SBREG_SMBCTRL;
res->start += SBREG_SMBCTRL;
res->end = res->start + 3;
res->flags = IORESOURCE_MEM;
return platform_device_register_resndata(&pci_dev->dev, "iTCO_wdt", -1,
tco_res, 2, &pldata, sizeof(pldata));

View File

@ -1,11 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
config LEDS_SIEMENS_SIMATIC_IPC
tristate "LED driver for Siemens Simatic IPCs"
depends on LEDS_CLASS
depends on LEDS_GPIO
depends on SIEMENS_SIMATIC_IPC
help
This option enables support for the LEDs of several Industrial PCs
from Siemens.
To compile this driver as a module, choose M here: the module
will be called simatic-ipc-leds.
To compile this driver as a module, choose M here: the modules
will be called simatic-ipc-leds and simatic-ipc-leds-gpio.

View File

@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds.o
obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds-gpio.o

View File

@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Siemens SIMATIC IPC driver for GPIO based LEDs
*
* Copyright (c) Siemens AG, 2022
*
* Authors:
* Henning Schild <henning.schild@siemens.com>
*/
#include <linux/gpio/machine.h>
#include <linux/gpio/consumer.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_device.h>
static struct gpiod_lookup_table simatic_ipc_led_gpio_table = {
.dev_id = "leds-gpio",
.table = {
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 51, NULL, 0, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 52, NULL, 1, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 53, NULL, 2, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 57, NULL, 3, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 58, NULL, 4, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 60, NULL, 5, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 56, NULL, 6, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 59, NULL, 7, GPIO_ACTIVE_HIGH),
},
};
static const struct gpio_led simatic_ipc_gpio_leds[] = {
{ .name = "green:" LED_FUNCTION_STATUS "-3" },
{ .name = "red:" LED_FUNCTION_STATUS "-1" },
{ .name = "green:" LED_FUNCTION_STATUS "-1" },
{ .name = "red:" LED_FUNCTION_STATUS "-2" },
{ .name = "green:" LED_FUNCTION_STATUS "-2" },
{ .name = "red:" LED_FUNCTION_STATUS "-3" },
};
static const struct gpio_led_platform_data simatic_ipc_gpio_leds_pdata = {
.num_leds = ARRAY_SIZE(simatic_ipc_gpio_leds),
.leds = simatic_ipc_gpio_leds,
};
static struct platform_device *simatic_leds_pdev;
static int simatic_ipc_leds_gpio_remove(struct platform_device *pdev)
{
gpiod_remove_lookup_table(&simatic_ipc_led_gpio_table);
platform_device_unregister(simatic_leds_pdev);
return 0;
}
static int simatic_ipc_leds_gpio_probe(struct platform_device *pdev)
{
struct gpio_desc *gpiod;
int err;
gpiod_add_lookup_table(&simatic_ipc_led_gpio_table);
simatic_leds_pdev = platform_device_register_resndata(NULL,
"leds-gpio", PLATFORM_DEVID_NONE, NULL, 0,
&simatic_ipc_gpio_leds_pdata,
sizeof(simatic_ipc_gpio_leds_pdata));
if (IS_ERR(simatic_leds_pdev)) {
err = PTR_ERR(simatic_leds_pdev);
goto out;
}
/* PM_BIOS_BOOT_N */
gpiod = gpiod_get_index(&simatic_leds_pdev->dev, NULL, 6, GPIOD_OUT_LOW);
if (IS_ERR(gpiod)) {
err = PTR_ERR(gpiod);
goto out;
}
gpiod_put(gpiod);
/* PM_WDT_OUT */
gpiod = gpiod_get_index(&simatic_leds_pdev->dev, NULL, 7, GPIOD_OUT_LOW);
if (IS_ERR(gpiod)) {
err = PTR_ERR(gpiod);
goto out;
}
gpiod_put(gpiod);
return 0;
out:
simatic_ipc_leds_gpio_remove(pdev);
return err;
}
static struct platform_driver simatic_ipc_led_gpio_driver = {
.probe = simatic_ipc_leds_gpio_probe,
.remove = simatic_ipc_leds_gpio_remove,
.driver = {
.name = KBUILD_MODNAME,
}
};
module_platform_driver(simatic_ipc_led_gpio_driver);
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" KBUILD_MODNAME);
MODULE_SOFTDEP("pre: platform:leds-gpio");
MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");

View File

@ -23,7 +23,7 @@
#define SIMATIC_IPC_LED_PORT_BASE 0x404E
struct simatic_ipc_led {
unsigned int value; /* mask for io and offset for mem */
unsigned int value; /* mask for io */
char *name;
struct led_classdev cdev;
};
@ -38,21 +38,6 @@ static struct simatic_ipc_led simatic_ipc_leds_io[] = {
{ }
};
/* the actual start will be discovered with PCI, 0 is a placeholder */
static struct resource simatic_ipc_led_mem_res = DEFINE_RES_MEM_NAMED(0, SZ_4K, KBUILD_MODNAME);
static void __iomem *simatic_ipc_led_memory;
static struct simatic_ipc_led simatic_ipc_leds_mem[] = {
{0x500 + 0x1A0, "red:" LED_FUNCTION_STATUS "-1"},
{0x500 + 0x1A8, "green:" LED_FUNCTION_STATUS "-1"},
{0x500 + 0x1C8, "red:" LED_FUNCTION_STATUS "-2"},
{0x500 + 0x1D0, "green:" LED_FUNCTION_STATUS "-2"},
{0x500 + 0x1E0, "red:" LED_FUNCTION_STATUS "-3"},
{0x500 + 0x198, "green:" LED_FUNCTION_STATUS "-3"},
{ }
};
static struct resource simatic_ipc_led_io_res =
DEFINE_RES_IO_NAMED(SIMATIC_IPC_LED_PORT_BASE, SZ_2, KBUILD_MODNAME);
@ -88,28 +73,6 @@ static enum led_brightness simatic_ipc_led_get_io(struct led_classdev *led_cd)
return inw(SIMATIC_IPC_LED_PORT_BASE) & led->value ? LED_OFF : led_cd->max_brightness;
}
static void simatic_ipc_led_set_mem(struct led_classdev *led_cd,
enum led_brightness brightness)
{
struct simatic_ipc_led *led = cdev_to_led(led_cd);
void __iomem *reg = simatic_ipc_led_memory + led->value;
u32 val;
val = readl(reg);
val = (val & ~1) | (brightness == LED_OFF);
writel(val, reg);
}
static enum led_brightness simatic_ipc_led_get_mem(struct led_classdev *led_cd)
{
struct simatic_ipc_led *led = cdev_to_led(led_cd);
void __iomem *reg = simatic_ipc_led_memory + led->value;
u32 val;
val = readl(reg);
return (val & 1) ? LED_OFF : led_cd->max_brightness;
}
static int simatic_ipc_leds_probe(struct platform_device *pdev)
{
const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
@ -117,9 +80,7 @@ static int simatic_ipc_leds_probe(struct platform_device *pdev)
struct simatic_ipc_led *ipcled;
struct led_classdev *cdev;
struct resource *res;
void __iomem *reg;
int err, type;
u32 val;
int err;
switch (plat->devmode) {
case SIMATIC_IPC_DEVICE_227D:
@ -134,52 +95,19 @@ static int simatic_ipc_leds_probe(struct platform_device *pdev)
}
ipcled = simatic_ipc_leds_io;
}
type = IORESOURCE_IO;
if (!devm_request_region(dev, res->start, resource_size(res), KBUILD_MODNAME)) {
dev_err(dev, "Unable to register IO resource at %pR\n", res);
return -EBUSY;
}
break;
case SIMATIC_IPC_DEVICE_127E:
res = &simatic_ipc_led_mem_res;
ipcled = simatic_ipc_leds_mem;
type = IORESOURCE_MEM;
/* get GPIO base from PCI */
res->start = simatic_ipc_get_membase0(PCI_DEVFN(13, 0));
if (res->start == 0)
return -ENODEV;
/* do the final address calculation */
res->start = res->start + (0xC5 << 16);
res->end += res->start;
simatic_ipc_led_memory = devm_ioremap_resource(dev, res);
if (IS_ERR(simatic_ipc_led_memory))
return PTR_ERR(simatic_ipc_led_memory);
/* initialize power/watchdog LED */
reg = simatic_ipc_led_memory + 0x500 + 0x1D8; /* PM_WDT_OUT */
val = readl(reg);
writel(val & ~1, reg);
reg = simatic_ipc_led_memory + 0x500 + 0x1C0; /* PM_BIOS_BOOT_N */
val = readl(reg);
writel(val | 1, reg);
break;
default:
return -ENODEV;
}
while (ipcled->value) {
cdev = &ipcled->cdev;
if (type == IORESOURCE_MEM) {
cdev->brightness_set = simatic_ipc_led_set_mem;
cdev->brightness_get = simatic_ipc_led_get_mem;
} else {
cdev->brightness_set = simatic_ipc_led_set_io;
cdev->brightness_get = simatic_ipc_led_get_io;
}
cdev->brightness_set = simatic_ipc_led_set_io;
cdev->brightness_get = simatic_ipc_led_get_io;
cdev->max_brightness = LED_ON;
cdev->name = ipcled->name;

View File

@ -572,6 +572,7 @@ config LPC_ICH
tristate "Intel ICH LPC"
depends on PCI
select MFD_CORE
select P2SB if X86
help
The LPC bridge function of the Intel ICH provides support for
many functional units. This driver provides needed support for

View File

@ -8,7 +8,8 @@
* Configuration Registers.
*
* This driver is derived from lpc_sch.
*
* Copyright (c) 2017, 2021-2022 Intel Corporation
* Copyright (c) 2011 Extreme Engineering Solution, Inc.
* Author: Aaron Sierra <asierra@xes-inc.com>
*
@ -42,9 +43,11 @@
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/pci.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/mfd/core.h>
#include <linux/mfd/lpc_ich.h>
#include <linux/platform_data/itco_wdt.h>
#include <linux/platform_data/x86/p2sb.h>
#define ACPIBASE 0x40
#define ACPIBASE_GPE_OFF 0x28
@ -71,8 +74,6 @@
#define BCR 0xdc
#define BCR_WPD BIT(0)
#define SPIBASE_APL_SZ 4096
#define GPIOBASE_ICH0 0x58
#define GPIOCTRL_ICH0 0x5C
#define GPIOBASE_ICH6 0x48
@ -143,6 +144,73 @@ static struct mfd_cell lpc_ich_gpio_cell = {
.ignore_resource_conflicts = true,
};
#define APL_GPIO_NORTH 0
#define APL_GPIO_NORTHWEST 1
#define APL_GPIO_WEST 2
#define APL_GPIO_SOUTHWEST 3
#define APL_GPIO_NR_DEVICES 4
/* Offset data for Apollo Lake GPIO controllers */
static resource_size_t apl_gpio_offsets[APL_GPIO_NR_DEVICES] = {
[APL_GPIO_NORTH] = 0xc50000,
[APL_GPIO_NORTHWEST] = 0xc40000,
[APL_GPIO_WEST] = 0xc70000,
[APL_GPIO_SOUTHWEST] = 0xc00000,
};
#define APL_GPIO_RESOURCE_SIZE 0x1000
#define APL_GPIO_IRQ 14
static struct resource apl_gpio_resources[APL_GPIO_NR_DEVICES][2] = {
[APL_GPIO_NORTH] = {
DEFINE_RES_MEM(0, 0),
DEFINE_RES_IRQ(APL_GPIO_IRQ),
},
[APL_GPIO_NORTHWEST] = {
DEFINE_RES_MEM(0, 0),
DEFINE_RES_IRQ(APL_GPIO_IRQ),
},
[APL_GPIO_WEST] = {
DEFINE_RES_MEM(0, 0),
DEFINE_RES_IRQ(APL_GPIO_IRQ),
},
[APL_GPIO_SOUTHWEST] = {
DEFINE_RES_MEM(0, 0),
DEFINE_RES_IRQ(APL_GPIO_IRQ),
},
};
static const struct mfd_cell apl_gpio_devices[APL_GPIO_NR_DEVICES] = {
[APL_GPIO_NORTH] = {
.name = "apollolake-pinctrl",
.id = APL_GPIO_NORTH,
.num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_NORTH]),
.resources = apl_gpio_resources[APL_GPIO_NORTH],
.ignore_resource_conflicts = true,
},
[APL_GPIO_NORTHWEST] = {
.name = "apollolake-pinctrl",
.id = APL_GPIO_NORTHWEST,
.num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_NORTHWEST]),
.resources = apl_gpio_resources[APL_GPIO_NORTHWEST],
.ignore_resource_conflicts = true,
},
[APL_GPIO_WEST] = {
.name = "apollolake-pinctrl",
.id = APL_GPIO_WEST,
.num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_WEST]),
.resources = apl_gpio_resources[APL_GPIO_WEST],
.ignore_resource_conflicts = true,
},
[APL_GPIO_SOUTHWEST] = {
.name = "apollolake-pinctrl",
.id = APL_GPIO_SOUTHWEST,
.num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_SOUTHWEST]),
.resources = apl_gpio_resources[APL_GPIO_SOUTHWEST],
.ignore_resource_conflicts = true,
},
};
static struct mfd_cell lpc_ich_spi_cell = {
.name = "intel-spi",
@ -1086,6 +1154,34 @@ wdt_done:
return ret;
}
static int lpc_ich_init_pinctrl(struct pci_dev *dev)
{
struct resource base;
unsigned int i;
int ret;
/* Check, if GPIO has been exported as an ACPI device */
if (acpi_dev_present("INT3452", NULL, -1))
return -EEXIST;
ret = p2sb_bar(dev->bus, 0, &base);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(apl_gpio_devices); i++) {
struct resource *mem = &apl_gpio_resources[i][0];
resource_size_t offset = apl_gpio_offsets[i];
/* Fill MEM resource */
mem->start = base.start + offset;
mem->end = base.start + offset + APL_GPIO_RESOURCE_SIZE - 1;
mem->flags = base.flags;
}
return mfd_add_devices(&dev->dev, 0, apl_gpio_devices,
ARRAY_SIZE(apl_gpio_devices), NULL, 0, NULL);
}
static bool lpc_ich_byt_set_writeable(void __iomem *base, void *data)
{
u32 val;
@ -1100,35 +1196,32 @@ static bool lpc_ich_byt_set_writeable(void __iomem *base, void *data)
return val & BYT_BCR_WPD;
}
static bool lpc_ich_lpt_set_writeable(void __iomem *base, void *data)
static bool lpc_ich_set_writeable(struct pci_bus *bus, unsigned int devfn)
{
struct pci_dev *pdev = data;
u32 bcr;
pci_read_config_dword(pdev, BCR, &bcr);
pci_bus_read_config_dword(bus, devfn, BCR, &bcr);
if (!(bcr & BCR_WPD)) {
bcr |= BCR_WPD;
pci_write_config_dword(pdev, BCR, bcr);
pci_read_config_dword(pdev, BCR, &bcr);
pci_bus_write_config_dword(bus, devfn, BCR, bcr);
pci_bus_read_config_dword(bus, devfn, BCR, &bcr);
}
return bcr & BCR_WPD;
}
static bool lpc_ich_lpt_set_writeable(void __iomem *base, void *data)
{
struct pci_dev *pdev = data;
return lpc_ich_set_writeable(pdev->bus, pdev->devfn);
}
static bool lpc_ich_bxt_set_writeable(void __iomem *base, void *data)
{
unsigned int spi = PCI_DEVFN(13, 2);
struct pci_bus *bus = data;
u32 bcr;
struct pci_dev *pdev = data;
pci_bus_read_config_dword(bus, spi, BCR, &bcr);
if (!(bcr & BCR_WPD)) {
bcr |= BCR_WPD;
pci_bus_write_config_dword(bus, spi, BCR, bcr);
pci_bus_read_config_dword(bus, spi, BCR, &bcr);
}
return bcr & BCR_WPD;
return lpc_ich_set_writeable(pdev->bus, PCI_DEVFN(13, 2));
}
static int lpc_ich_init_spi(struct pci_dev *dev)
@ -1137,6 +1230,7 @@ static int lpc_ich_init_spi(struct pci_dev *dev)
struct resource *res = &intel_spi_res[0];
struct intel_spi_boardinfo *info;
u32 spi_base, rcba;
int ret;
info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
@ -1167,30 +1261,19 @@ static int lpc_ich_init_spi(struct pci_dev *dev)
}
break;
case INTEL_SPI_BXT: {
unsigned int p2sb = PCI_DEVFN(13, 0);
unsigned int spi = PCI_DEVFN(13, 2);
struct pci_bus *bus = dev->bus;
case INTEL_SPI_BXT:
/*
* The P2SB is hidden by BIOS and we need to unhide it in
* order to read BAR of the SPI flash device. Once that is
* done we hide it again.
*/
pci_bus_write_config_byte(bus, p2sb, 0xe1, 0x0);
pci_bus_read_config_dword(bus, spi, PCI_BASE_ADDRESS_0,
&spi_base);
if (spi_base != ~0) {
res->start = spi_base & 0xfffffff0;
res->end = res->start + SPIBASE_APL_SZ - 1;
ret = p2sb_bar(dev->bus, PCI_DEVFN(13, 2), res);
if (ret)
return ret;
info->set_writeable = lpc_ich_bxt_set_writeable;
info->data = bus;
}
pci_bus_write_config_byte(bus, p2sb, 0xe1, 0x1);
info->set_writeable = lpc_ich_bxt_set_writeable;
info->data = dev;
break;
}
default:
return -EINVAL;
@ -1249,6 +1332,12 @@ static int lpc_ich_probe(struct pci_dev *dev,
cell_added = true;
}
if (priv->chipset == LPC_APL) {
ret = lpc_ich_init_pinctrl(dev);
if (!ret)
cell_added = true;
}
if (lpc_chipset_info[priv->chipset].spi_type) {
ret = lpc_ich_init_spi(dev);
if (!ret)

View File

@ -1641,16 +1641,14 @@ EXPORT_SYMBOL_GPL(intel_pinctrl_probe_by_uid);
const struct intel_pinctrl_soc_data *intel_pinctrl_get_soc_data(struct platform_device *pdev)
{
const struct intel_pinctrl_soc_data * const *table;
const struct intel_pinctrl_soc_data *data = NULL;
const struct intel_pinctrl_soc_data **table;
struct acpi_device *adev;
unsigned int i;
adev = ACPI_COMPANION(&pdev->dev);
if (adev) {
const void *match = device_get_match_data(&pdev->dev);
table = device_get_match_data(&pdev->dev);
if (table) {
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
unsigned int i;
table = (const struct intel_pinctrl_soc_data **)match;
for (i = 0; table[i]; i++) {
if (!strcmp(adev->pnp.unique_id, table[i]->uid)) {
data = table[i];
@ -1664,7 +1662,7 @@ const struct intel_pinctrl_soc_data *intel_pinctrl_get_soc_data(struct platform_
if (!id)
return ERR_PTR(-ENODEV);
table = (const struct intel_pinctrl_soc_data **)id->driver_data;
table = (const struct intel_pinctrl_soc_data * const *)id->driver_data;
data = table[pdev->id];
}

View File

@ -1,7 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
if X86
source "drivers/platform/x86/Kconfig"
endif
if MIPS
source "drivers/platform/mips/Kconfig"
endif
@ -15,3 +12,5 @@ source "drivers/platform/mellanox/Kconfig"
source "drivers/platform/olpc/Kconfig"
source "drivers/platform/surface/Kconfig"
source "drivers/platform/x86/Kconfig"

View File

@ -31,6 +31,7 @@
* @group: sysfs attribute group;
* @groups: list of sysfs attribute group for hwmon registration;
* @regsize: size of a register value;
* @io_lock: user access locking;
*/
struct mlxreg_io_priv_data {
struct platform_device *pdev;
@ -41,6 +42,7 @@ struct mlxreg_io_priv_data {
struct attribute_group group;
const struct attribute_group *groups[2];
int regsize;
struct mutex io_lock; /* Protects user access. */
};
static int
@ -116,14 +118,19 @@ mlxreg_io_attr_show(struct device *dev, struct device_attribute *attr,
u32 regval = 0;
int ret;
mutex_lock(&priv->io_lock);
ret = mlxreg_io_get_reg(priv->pdata->regmap, data, 0, true,
priv->regsize, &regval);
if (ret)
goto access_error;
mutex_unlock(&priv->io_lock);
return sprintf(buf, "%u\n", regval);
access_error:
mutex_unlock(&priv->io_lock);
return ret;
}
@ -145,6 +152,8 @@ mlxreg_io_attr_store(struct device *dev, struct device_attribute *attr,
if (ret)
return ret;
mutex_lock(&priv->io_lock);
ret = mlxreg_io_get_reg(priv->pdata->regmap, data, input_val, false,
priv->regsize, &regval);
if (ret)
@ -154,9 +163,12 @@ mlxreg_io_attr_store(struct device *dev, struct device_attribute *attr,
if (ret)
goto access_error;
mutex_unlock(&priv->io_lock);
return len;
access_error:
mutex_unlock(&priv->io_lock);
dev_err(&priv->pdev->dev, "Bus access error\n");
return ret;
}
@ -246,16 +258,27 @@ static int mlxreg_io_probe(struct platform_device *pdev)
return PTR_ERR(priv->hwmon);
}
mutex_init(&priv->io_lock);
dev_set_drvdata(&pdev->dev, priv);
return 0;
}
static int mlxreg_io_remove(struct platform_device *pdev)
{
struct mlxreg_io_priv_data *priv = dev_get_drvdata(&pdev->dev);
mutex_destroy(&priv->io_lock);
return 0;
}
static struct platform_driver mlxreg_io_driver = {
.driver = {
.name = "mlxreg-io",
},
.probe = mlxreg_io_probe,
.remove = mlxreg_io_remove,
};
module_platform_driver(mlxreg_io_driver);

View File

@ -716,8 +716,12 @@ mlxreg_lc_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap,
switch (regval) {
case MLXREG_LC_SN4800_C16:
err = mlxreg_lc_sn4800_c16_config_init(mlxreg_lc, regmap, data);
if (err)
if (err) {
dev_err(dev, "Failed to config client %s at bus %d at addr 0x%02x\n",
data->hpdev.brdinfo->type, data->hpdev.nr,
data->hpdev.brdinfo->addr);
return err;
}
break;
default:
return -ENODEV;
@ -730,8 +734,11 @@ mlxreg_lc_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap,
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))
if (IS_ERR(mlxreg_lc->mux)) {
dev_err(dev, "Failed to create mux infra for client %s at bus %d at addr 0x%02x\n",
data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr);
return PTR_ERR(mlxreg_lc->mux);
}
/* Register IO access driver. */
if (mlxreg_lc->io_data) {
@ -740,6 +747,9 @@ mlxreg_lc_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap,
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)) {
dev_err(dev, "Failed to create regio for client %s at bus %d at addr 0x%02x\n",
data->hpdev.brdinfo->type, data->hpdev.nr,
data->hpdev.brdinfo->addr);
err = PTR_ERR(mlxreg_lc->io_regs);
goto fail_register_io;
}
@ -753,6 +763,9 @@ mlxreg_lc_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap,
mlxreg_lc->led_data,
sizeof(*mlxreg_lc->led_data));
if (IS_ERR(mlxreg_lc->led)) {
dev_err(dev, "Failed to create LED objects for client %s at bus %d at addr 0x%02x\n",
data->hpdev.brdinfo->type, data->hpdev.nr,
data->hpdev.brdinfo->addr);
err = PTR_ERR(mlxreg_lc->led);
goto fail_register_led;
}
@ -809,7 +822,8 @@ static int mlxreg_lc_probe(struct platform_device *pdev)
if (!data->hpdev.adapter) {
dev_err(&pdev->dev, "Failed to get adapter for bus %d\n",
data->hpdev.nr);
return -EFAULT;
err = -EFAULT;
goto i2c_get_adapter_fail;
}
/* Create device at the top of line card I2C tree.*/
@ -818,32 +832,40 @@ static int mlxreg_lc_probe(struct platform_device *pdev)
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);
err = PTR_ERR(data->hpdev.client);
goto i2c_new_device_fail;
}
regmap = devm_regmap_init_i2c(data->hpdev.client,
&mlxreg_lc_regmap_conf);
if (IS_ERR(regmap)) {
dev_err(&pdev->dev, "Failed to create regmap for client %s at bus %d at addr 0x%02x\n",
data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr);
err = PTR_ERR(regmap);
goto mlxreg_lc_probe_fail;
goto devm_regmap_init_i2c_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;
if (err) {
dev_err(&pdev->dev, "Failed to set default regmap %d for client %s at bus %d at addr 0x%02x\n",
i, data->hpdev.brdinfo->type, data->hpdev.nr,
data->hpdev.brdinfo->addr);
goto regmap_write_fail;
}
}
/* Sync registers with hardware. */
regcache_mark_dirty(regmap);
err = regcache_sync(regmap);
if (err)
goto mlxreg_lc_probe_fail;
if (err) {
dev_err(&pdev->dev, "Failed to sync regmap for client %s at bus %d at addr 0x%02x\n",
data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr);
err = PTR_ERR(regmap);
goto regcache_sync_fail;
}
par_pdata = data->hpdev.brdinfo->platform_data;
mlxreg_lc->par_regmap = par_pdata->regmap;
@ -854,12 +876,27 @@ static int mlxreg_lc_probe(struct platform_device *pdev)
/* Configure line card. */
err = mlxreg_lc_config_init(mlxreg_lc, regmap, data);
if (err)
goto mlxreg_lc_probe_fail;
goto mlxreg_lc_config_init_fail;
return err;
mlxreg_lc_probe_fail:
mlxreg_lc_config_init_fail:
regcache_sync_fail:
regmap_write_fail:
devm_regmap_init_i2c_fail:
if (data->hpdev.client) {
i2c_unregister_device(data->hpdev.client);
data->hpdev.client = NULL;
}
i2c_new_device_fail:
i2c_put_adapter(data->hpdev.adapter);
data->hpdev.adapter = NULL;
i2c_get_adapter_fail:
/* Clear event notification callback and handle. */
if (data->notifier) {
data->notifier->user_handler = NULL;
data->notifier->handle = NULL;
}
return err;
}
@ -868,11 +905,18 @@ 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;
}
/*
* Probing and removing are invoked by hotplug events raised upon line card insertion and
* removing. If probing procedure fails all data is cleared. However, hotplug event still
* will be raised on line card removing and activate removing procedure. In this case there
* is nothing to remove.
*/
if (!data->notifier || !data->notifier->handle)
return 0;
/* Clear event notification callback and handle. */
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,

View File

@ -264,7 +264,7 @@ static ssize_t ec_dbgfs_cmd_write(struct file *file, const char __user *buf,
int i, m;
unsigned char ec_cmd[EC_MAX_CMD_ARGS];
unsigned int ec_cmd_int[EC_MAX_CMD_ARGS];
char cmdbuf[64];
char cmdbuf[64] = "";
int ec_cmd_bytes;
mutex_lock(&ec_dbgfs_lock);

View File

@ -72,18 +72,45 @@ config SURFACE_AGGREGATOR_CDEV
The provided interface is intended for debugging and development only,
and should not be used otherwise.
config SURFACE_AGGREGATOR_HUB
tristate "Surface System Aggregator Module Subsystem Device Hubs"
depends on SURFACE_AGGREGATOR
depends on SURFACE_AGGREGATOR_BUS
help
Device-hub drivers for Surface System Aggregator Module (SSAM) subsystem
devices.
Provides subsystem hub drivers which manage client devices on various
SSAM subsystems. In some subsystems, notably the BAS subsystem managing
devices contained in the base of the Surface Book 3 and the KIP subsystem
managing type-cover devices in the Surface Pro 8 and Surface Pro X,
devices can be (hot-)removed. Hub devices and drivers are required to
manage these subdevices.
Devices managed via these hubs are:
- Battery/AC devices (Surface Book 3).
- HID input devices (7th-generation and later models with detachable
input devices).
Select M (recommended) or Y here if you want support for the above
mentioned devices on the corresponding Surface models. Without this
module, the respective devices mentioned above will not be instantiated
and thus any functionality provided by them will be missing, even when
drivers for these devices are present. This module only provides the
respective subsystem hubs. Both drivers and device specification (e.g.
via the Surface Aggregator Registry) for these devices still need to be
selected via other options.
config SURFACE_AGGREGATOR_REGISTRY
tristate "Surface System Aggregator Module Device Registry"
depends on SURFACE_AGGREGATOR
depends on SURFACE_AGGREGATOR_BUS
help
Device-registry and device-hubs for Surface System Aggregator Module
(SSAM) devices.
Device-registry for Surface System Aggregator Module (SSAM) devices.
Provides a module and driver which act as a device-registry for SSAM
client devices that cannot be detected automatically, e.g. via ACPI.
Such devices are instead provided via this registry and attached via
device hubs, also provided in this module.
Such devices are instead provided and managed via this registry.
Devices provided via this registry are:
- Platform profile (performance-/cooling-mode) device (5th- and later
@ -99,6 +126,29 @@ config SURFACE_AGGREGATOR_REGISTRY
the respective client devices. Drivers for these devices still need to
be selected via the other options.
config SURFACE_AGGREGATOR_TABLET_SWITCH
tristate "Surface Aggregator Generic Tablet-Mode Switch Driver"
depends on SURFACE_AGGREGATOR
depends on SURFACE_AGGREGATOR_BUS
depends on INPUT
help
Provides a tablet-mode switch input device on Microsoft Surface models
using the KIP subsystem for detachable keyboards (e.g. keyboard covers)
or the POS subsystem for device/screen posture changes.
The KIP subsystem is used on newer Surface generations to handle
detachable input peripherals, specifically the keyboard cover (containing
keyboard and touchpad) on the Surface Pro 8 and Surface Pro X. The POS
subsystem is used for device posture change notifications on the Surface
Laptop Studio. This module provides a driver to let user-space know when
the device should be considered in tablet-mode due to the keyboard cover
being detached or folded back (essentially signaling when the keyboard is
not available for input). It does so by creating a tablet-mode switch
input device, sending the standard SW_TABLET_MODE event on mode change.
Select M or Y here, if you want to provide tablet-mode switch input
events on the Surface Pro 8, Surface Pro X, and Surface Laptop Studio.
config SURFACE_DTX
tristate "Surface DTX (Detachment System) Driver"
depends on SURFACE_AGGREGATOR

View File

@ -9,7 +9,9 @@ obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o
obj-$(CONFIG_SURFACE_ACPI_NOTIFY) += surface_acpi_notify.o
obj-$(CONFIG_SURFACE_AGGREGATOR) += aggregator/
obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV) += surface_aggregator_cdev.o
obj-$(CONFIG_SURFACE_AGGREGATOR_HUB) += surface_aggregator_hub.o
obj-$(CONFIG_SURFACE_AGGREGATOR_REGISTRY) += surface_aggregator_registry.o
obj-$(CONFIG_SURFACE_AGGREGATOR_TABLET_SWITCH) += surface_aggregator_tabletsw.o
obj-$(CONFIG_SURFACE_DTX) += surface_dtx.o
obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o
obj-$(CONFIG_SURFACE_HOTPLUG) += surface_hotplug.o

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
# Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
menuconfig SURFACE_AGGREGATOR
tristate "Microsoft Surface System Aggregator Module Subsystem and Drivers"

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
# Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
# For include/trace/define_trace.h to include trace.h
CFLAGS_core.o = -I$(src)

View File

@ -2,10 +2,11 @@
/*
* Surface System Aggregator Module bus and device integration.
*
* Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#include <linux/device.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/surface_aggregator/controller.h>
@ -14,6 +15,9 @@
#include "bus.h"
#include "controller.h"
/* -- Device and bus functions. --------------------------------------------- */
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@ -46,6 +50,7 @@ static void ssam_device_release(struct device *dev)
struct ssam_device *sdev = to_ssam_device(dev);
ssam_controller_put(sdev->ctrl);
fwnode_handle_put(sdev->dev.fwnode);
kfree(sdev);
}
@ -363,6 +368,134 @@ void ssam_device_driver_unregister(struct ssam_device_driver *sdrv)
}
EXPORT_SYMBOL_GPL(ssam_device_driver_unregister);
/* -- Bus registration. ----------------------------------------------------- */
/**
* ssam_bus_register() - Register and set-up the SSAM client device bus.
*/
int ssam_bus_register(void)
{
return bus_register(&ssam_bus_type);
}
/**
* ssam_bus_unregister() - Unregister the SSAM client device bus.
*/
void ssam_bus_unregister(void)
{
return bus_unregister(&ssam_bus_type);
}
/* -- Helpers for controller and hub devices. ------------------------------- */
static int ssam_device_uid_from_string(const char *str, struct ssam_device_uid *uid)
{
u8 d, tc, tid, iid, fn;
int n;
n = sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx", &d, &tc, &tid, &iid, &fn);
if (n != 5)
return -EINVAL;
uid->domain = d;
uid->category = tc;
uid->target = tid;
uid->instance = iid;
uid->function = fn;
return 0;
}
static int ssam_get_uid_for_node(struct fwnode_handle *node, struct ssam_device_uid *uid)
{
const char *str = fwnode_get_name(node);
/*
* To simplify definitions of firmware nodes, we set the device name
* based on the UID of the device, prefixed with "ssam:".
*/
if (strncmp(str, "ssam:", strlen("ssam:")) != 0)
return -ENODEV;
str += strlen("ssam:");
return ssam_device_uid_from_string(str, uid);
}
static int ssam_add_client_device(struct device *parent, struct ssam_controller *ctrl,
struct fwnode_handle *node)
{
struct ssam_device_uid uid;
struct ssam_device *sdev;
int status;
status = ssam_get_uid_for_node(node, &uid);
if (status)
return status;
sdev = ssam_device_alloc(ctrl, uid);
if (!sdev)
return -ENOMEM;
sdev->dev.parent = parent;
sdev->dev.fwnode = fwnode_handle_get(node);
status = ssam_device_add(sdev);
if (status)
ssam_device_put(sdev);
return status;
}
/**
* __ssam_register_clients() - Register client devices defined under the
* given firmware node as children of the given device.
* @parent: The parent device under which clients should be registered.
* @ctrl: The controller with which client should be registered.
* @node: The firmware node holding definitions of the devices to be added.
*
* Register all clients that have been defined as children of the given root
* firmware node as children of the given parent device. The respective child
* firmware nodes will be associated with the correspondingly created child
* devices.
*
* The given controller will be used to instantiate the new devices. See
* ssam_device_add() for details.
*
* Note that, generally, the use of either ssam_device_register_clients() or
* ssam_register_clients() should be preferred as they directly use the
* firmware node and/or controller associated with the given device. This
* function is only intended for use when different device specifications (e.g.
* ACPI and firmware nodes) need to be combined (as is done in the platform hub
* of the device registry).
*
* Return: Returns zero on success, nonzero on failure.
*/
int __ssam_register_clients(struct device *parent, struct ssam_controller *ctrl,
struct fwnode_handle *node)
{
struct fwnode_handle *child;
int status;
fwnode_for_each_child_node(node, child) {
/*
* Try to add the device specified in the firmware node. If
* this fails with -ENODEV, the node does not specify any SSAM
* device, so ignore it and continue with the next one.
*/
status = ssam_add_client_device(parent, ctrl, child);
if (status && status != -ENODEV)
goto err;
}
return 0;
err:
ssam_remove_clients(parent);
return status;
}
EXPORT_SYMBOL_GPL(__ssam_register_clients);
static int ssam_remove_device(struct device *dev, void *_data)
{
struct ssam_device *sdev = to_ssam_device(dev);
@ -387,19 +520,3 @@ void ssam_remove_clients(struct device *dev)
device_for_each_child_reverse(dev, NULL, ssam_remove_device);
}
EXPORT_SYMBOL_GPL(ssam_remove_clients);
/**
* ssam_bus_register() - Register and set-up the SSAM client device bus.
*/
int ssam_bus_register(void)
{
return bus_register(&ssam_bus_type);
}
/**
* ssam_bus_unregister() - Unregister the SSAM client device bus.
*/
void ssam_bus_unregister(void)
{
return bus_unregister(&ssam_bus_type);
}

View File

@ -2,7 +2,7 @@
/*
* Surface System Aggregator Module bus and device integration.
*
* Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#ifndef _SURFACE_AGGREGATOR_BUS_H

View File

@ -2,7 +2,7 @@
/*
* Main SSAM/SSH controller structure and functionality.
*
* Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#include <linux/acpi.h>
@ -2199,16 +2199,26 @@ static int ssam_nf_refcount_enable(struct ssam_controller *ctrl,
}
/**
* ssam_nf_refcount_disable_free() - Disable event for reference count entry if it is
* no longer in use and free the corresponding entry.
* ssam_nf_refcount_disable_free() - Disable event for reference count entry if
* it is no longer in use and free the corresponding entry.
* @ctrl: The controller to disable the event on.
* @entry: The reference count entry for the event to be disabled.
* @flags: The flags used for enabling the event on the EC.
* @ec: Flag specifying if the event should actually be disabled on the EC.
*
* If the reference count equals zero, i.e. the event is no longer requested by
* any client, the event will be disabled and the corresponding reference count
* entry freed. The reference count entry must not be used any more after a
* call to this function.
* If ``ec`` equals ``true`` and the reference count equals zero (i.e. the
* event is no longer requested by any client), the specified event will be
* disabled on the EC via the corresponding request.
*
* If ``ec`` equals ``false``, no request will be sent to the EC and the event
* can be considered in a detached state (i.e. no longer used but still
* enabled). Disabling an event via this method may be required for
* hot-removable devices, where event disable requests may time out after the
* device has been physically removed.
*
* In both cases, if the reference count equals zero, the corresponding
* reference count entry will be freed. The reference count entry must not be
* used any more after a call to this function.
*
* Also checks if the flags used for disabling the event match the flags used
* for enabling the event and warns if they do not (regardless of reference
@ -2223,7 +2233,7 @@ static int ssam_nf_refcount_enable(struct ssam_controller *ctrl,
* returns the status of the event-enable EC command.
*/
static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl,
struct ssam_nf_refcount_entry *entry, u8 flags)
struct ssam_nf_refcount_entry *entry, u8 flags, bool ec)
{
const struct ssam_event_registry reg = entry->key.reg;
const struct ssam_event_id id = entry->key.id;
@ -2232,8 +2242,9 @@ static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl,
lockdep_assert_held(&nf->lock);
ssam_dbg(ctrl, "disabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
reg.target_category, id.target_category, id.instance, entry->refcount);
ssam_dbg(ctrl, "%s event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
ec ? "disabling" : "detaching", reg.target_category, id.target_category,
id.instance, entry->refcount);
if (entry->flags != flags) {
ssam_warn(ctrl,
@ -2242,7 +2253,7 @@ static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl,
id.instance);
}
if (entry->refcount == 0) {
if (ec && entry->refcount == 0) {
status = ssam_ssh_event_disable(ctrl, reg, id, flags);
kfree(entry);
}
@ -2322,20 +2333,26 @@ int ssam_notifier_register(struct ssam_controller *ctrl, struct ssam_event_notif
EXPORT_SYMBOL_GPL(ssam_notifier_register);
/**
* ssam_notifier_unregister() - Unregister an event notifier.
* @ctrl: The controller the notifier has been registered on.
* @n: The event notifier to unregister.
* __ssam_notifier_unregister() - Unregister an event notifier.
* @ctrl: The controller the notifier has been registered on.
* @n: The event notifier to unregister.
* @disable: Whether to disable the corresponding event on the EC.
*
* Unregister an event notifier. Decrement the usage counter of the associated
* SAM event if the notifier is not marked as an observer. If the usage counter
* reaches zero, the event will be disabled.
* reaches zero and ``disable`` equals ``true``, the event will be disabled.
*
* Useful for hot-removable devices, where communication may fail once the
* device has been physically removed. In that case, specifying ``disable`` as
* ``false`` avoids communication with the EC.
*
* Return: Returns zero on success, %-ENOENT if the given notifier block has
* not been registered on the controller. If the given notifier block was the
* last one associated with its specific event, returns the status of the
* event-disable EC-command.
*/
int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_notifier *n)
int __ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_notifier *n,
bool disable)
{
u16 rqid = ssh_tc_to_rqid(n->event.id.target_category);
struct ssam_nf_refcount_entry *entry;
@ -2373,7 +2390,7 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_not
goto remove;
}
status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags);
status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags, disable);
}
remove:
@ -2383,7 +2400,7 @@ remove:
return status;
}
EXPORT_SYMBOL_GPL(ssam_notifier_unregister);
EXPORT_SYMBOL_GPL(__ssam_notifier_unregister);
/**
* ssam_controller_event_enable() - Enable the specified event.
@ -2477,7 +2494,7 @@ int ssam_controller_event_disable(struct ssam_controller *ctrl,
return -ENOENT;
}
status = ssam_nf_refcount_disable_free(ctrl, entry, flags);
status = ssam_nf_refcount_disable_free(ctrl, entry, flags, true);
mutex_unlock(&nf->lock);
return status;

View File

@ -2,7 +2,7 @@
/*
* Main SSAM/SSH controller structure and functionality.
*
* Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#ifndef _SURFACE_AGGREGATOR_CONTROLLER_H

View File

@ -7,7 +7,7 @@
* Handles communication via requests as well as enabling, disabling, and
* relaying of events.
*
* Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#include <linux/acpi.h>

View File

@ -2,7 +2,7 @@
/*
* SSH message builder functions.
*
* Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#ifndef _SURFACE_AGGREGATOR_SSH_MSGB_H

View File

@ -2,7 +2,7 @@
/*
* SSH packet transport layer.
*
* Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#include <asm/unaligned.h>

View File

@ -2,7 +2,7 @@
/*
* SSH packet transport layer.
*
* Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#ifndef _SURFACE_AGGREGATOR_SSH_PACKET_LAYER_H

View File

@ -2,7 +2,7 @@
/*
* SSH message parser.
*
* Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#include <asm/unaligned.h>

View File

@ -2,7 +2,7 @@
/*
* SSH message parser.
*
* Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#ifndef _SURFACE_AGGREGATOR_SSH_PARSER_H

View File

@ -2,7 +2,7 @@
/*
* SSH request transport layer.
*
* Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#include <asm/unaligned.h>

View File

@ -2,7 +2,7 @@
/*
* SSH request transport layer.
*
* Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#ifndef _SURFACE_AGGREGATOR_SSH_REQUEST_LAYER_H

View File

@ -2,7 +2,7 @@
/*
* Trace points for SSAM/SSH.
*
* Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2020-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#undef TRACE_SYSTEM
@ -76,7 +76,7 @@ TRACE_DEFINE_ENUM(SSAM_SSH_TC_HID);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_TCH);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_BKL);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_TAM);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_ACC);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_ACC0);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_UFI);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_USC);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_PEN);
@ -85,6 +85,11 @@ TRACE_DEFINE_ENUM(SSAM_SSH_TC_AUD);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_SMC);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_KPD);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_REG);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_SPT);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_SYS);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_ACC1);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_SHB);
TRACE_DEFINE_ENUM(SSAM_SSH_TC_POS);
#define SSAM_PTR_UID_LEN 9
#define SSAM_U8_FIELD_NOT_APPLICABLE ((u16)-1)
@ -229,40 +234,45 @@ static inline u32 ssam_trace_get_request_tc(const struct ssh_packet *p)
#define ssam_show_ssh_tc(rqid) \
__print_symbolic(rqid, \
{ SSAM_SSH_TC_NOT_APPLICABLE, "N/A" }, \
{ SSAM_SSH_TC_SAM, "SAM" }, \
{ SSAM_SSH_TC_BAT, "BAT" }, \
{ SSAM_SSH_TC_TMP, "TMP" }, \
{ SSAM_SSH_TC_PMC, "PMC" }, \
{ SSAM_SSH_TC_FAN, "FAN" }, \
{ SSAM_SSH_TC_PoM, "PoM" }, \
{ SSAM_SSH_TC_DBG, "DBG" }, \
{ SSAM_SSH_TC_KBD, "KBD" }, \
{ SSAM_SSH_TC_FWU, "FWU" }, \
{ SSAM_SSH_TC_UNI, "UNI" }, \
{ SSAM_SSH_TC_LPC, "LPC" }, \
{ SSAM_SSH_TC_TCL, "TCL" }, \
{ SSAM_SSH_TC_SFL, "SFL" }, \
{ SSAM_SSH_TC_KIP, "KIP" }, \
{ SSAM_SSH_TC_EXT, "EXT" }, \
{ SSAM_SSH_TC_BLD, "BLD" }, \
{ SSAM_SSH_TC_BAS, "BAS" }, \
{ SSAM_SSH_TC_SEN, "SEN" }, \
{ SSAM_SSH_TC_SRQ, "SRQ" }, \
{ SSAM_SSH_TC_MCU, "MCU" }, \
{ SSAM_SSH_TC_HID, "HID" }, \
{ SSAM_SSH_TC_TCH, "TCH" }, \
{ SSAM_SSH_TC_BKL, "BKL" }, \
{ SSAM_SSH_TC_TAM, "TAM" }, \
{ SSAM_SSH_TC_ACC, "ACC" }, \
{ SSAM_SSH_TC_UFI, "UFI" }, \
{ SSAM_SSH_TC_USC, "USC" }, \
{ SSAM_SSH_TC_PEN, "PEN" }, \
{ SSAM_SSH_TC_VID, "VID" }, \
{ SSAM_SSH_TC_AUD, "AUD" }, \
{ SSAM_SSH_TC_SMC, "SMC" }, \
{ SSAM_SSH_TC_KPD, "KPD" }, \
{ SSAM_SSH_TC_REG, "REG" } \
{ SSAM_SSH_TC_NOT_APPLICABLE, "N/A" }, \
{ SSAM_SSH_TC_SAM, "SAM" }, \
{ SSAM_SSH_TC_BAT, "BAT" }, \
{ SSAM_SSH_TC_TMP, "TMP" }, \
{ SSAM_SSH_TC_PMC, "PMC" }, \
{ SSAM_SSH_TC_FAN, "FAN" }, \
{ SSAM_SSH_TC_PoM, "PoM" }, \
{ SSAM_SSH_TC_DBG, "DBG" }, \
{ SSAM_SSH_TC_KBD, "KBD" }, \
{ SSAM_SSH_TC_FWU, "FWU" }, \
{ SSAM_SSH_TC_UNI, "UNI" }, \
{ SSAM_SSH_TC_LPC, "LPC" }, \
{ SSAM_SSH_TC_TCL, "TCL" }, \
{ SSAM_SSH_TC_SFL, "SFL" }, \
{ SSAM_SSH_TC_KIP, "KIP" }, \
{ SSAM_SSH_TC_EXT, "EXT" }, \
{ SSAM_SSH_TC_BLD, "BLD" }, \
{ SSAM_SSH_TC_BAS, "BAS" }, \
{ SSAM_SSH_TC_SEN, "SEN" }, \
{ SSAM_SSH_TC_SRQ, "SRQ" }, \
{ SSAM_SSH_TC_MCU, "MCU" }, \
{ SSAM_SSH_TC_HID, "HID" }, \
{ SSAM_SSH_TC_TCH, "TCH" }, \
{ SSAM_SSH_TC_BKL, "BKL" }, \
{ SSAM_SSH_TC_TAM, "TAM" }, \
{ SSAM_SSH_TC_ACC0, "ACC0" }, \
{ SSAM_SSH_TC_UFI, "UFI" }, \
{ SSAM_SSH_TC_USC, "USC" }, \
{ SSAM_SSH_TC_PEN, "PEN" }, \
{ SSAM_SSH_TC_VID, "VID" }, \
{ SSAM_SSH_TC_AUD, "AUD" }, \
{ SSAM_SSH_TC_SMC, "SMC" }, \
{ SSAM_SSH_TC_KPD, "KPD" }, \
{ SSAM_SSH_TC_REG, "REG" }, \
{ SSAM_SSH_TC_SPT, "SPT" }, \
{ SSAM_SSH_TC_SYS, "SYS" }, \
{ SSAM_SSH_TC_ACC1, "ACC1" }, \
{ SSAM_SSH_TC_SHB, "SMB" }, \
{ SSAM_SSH_TC_POS, "POS" } \
)
DECLARE_EVENT_CLASS(ssam_frame_class,

View File

@ -8,7 +8,7 @@
* notifications sent from ACPI via the SAN interface by providing them to any
* registered external driver.
*
* Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#include <asm/unaligned.h>
@ -37,6 +37,7 @@ struct san_data {
#define to_san_data(ptr, member) \
container_of(ptr, struct san_data, member)
static struct workqueue_struct *san_wq;
/* -- dGPU notifier interface. ---------------------------------------------- */
@ -356,7 +357,7 @@ static u32 san_evt_bat_nf(struct ssam_event_notifier *nf,
memcpy(&work->event, event, sizeof(struct ssam_event) + event->length);
schedule_delayed_work(&work->work, delay);
queue_delayed_work(san_wq, &work->work, delay);
return SSAM_NOTIF_HANDLED;
}
@ -861,7 +862,7 @@ static int san_remove(struct platform_device *pdev)
* We have unregistered our event sources. Now we need to ensure that
* all delayed works they may have spawned are run to completion.
*/
flush_scheduled_work();
flush_workqueue(san_wq);
return 0;
}
@ -881,7 +882,27 @@ static struct platform_driver surface_acpi_notify = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
module_platform_driver(surface_acpi_notify);
static int __init san_init(void)
{
int ret;
san_wq = alloc_workqueue("san_wq", 0, 0);
if (!san_wq)
return -ENOMEM;
ret = platform_driver_register(&surface_acpi_notify);
if (ret)
destroy_workqueue(san_wq);
return ret;
}
module_init(san_init);
static void __exit san_exit(void)
{
platform_driver_unregister(&surface_acpi_notify);
destroy_workqueue(san_wq);
}
module_exit(san_exit);
MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
MODULE_DESCRIPTION("Surface ACPI Notify driver for Surface System Aggregator Module");

View File

@ -3,7 +3,7 @@
* Provides user-space access to the SSAM EC via the /dev/surface/aggregator
* misc device. Intended for debugging and development.
*
* Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2020-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#include <linux/fs.h>

View File

@ -0,0 +1,371 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for Surface System Aggregator Module (SSAM) subsystem device hubs.
*
* Provides a driver for SSAM subsystems device hubs. This driver performs
* instantiation of the devices managed by said hubs and takes care of
* (hot-)removal.
*
* Copyright (C) 2020-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#include <linux/kernel.h>
#include <linux/limits.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/surface_aggregator/device.h>
/* -- SSAM generic subsystem hub driver framework. -------------------------- */
enum ssam_hub_state {
SSAM_HUB_UNINITIALIZED, /* Only set during initialization. */
SSAM_HUB_CONNECTED,
SSAM_HUB_DISCONNECTED,
};
enum ssam_hub_flags {
SSAM_HUB_HOT_REMOVED,
};
struct ssam_hub;
struct ssam_hub_ops {
int (*get_state)(struct ssam_hub *hub, enum ssam_hub_state *state);
};
struct ssam_hub {
struct ssam_device *sdev;
enum ssam_hub_state state;
unsigned long flags;
struct delayed_work update_work;
unsigned long connect_delay;
struct ssam_event_notifier notif;
struct ssam_hub_ops ops;
};
struct ssam_hub_desc {
struct {
struct ssam_event_registry reg;
struct ssam_event_id id;
enum ssam_event_mask mask;
} event;
struct {
u32 (*notify)(struct ssam_event_notifier *nf, const struct ssam_event *event);
int (*get_state)(struct ssam_hub *hub, enum ssam_hub_state *state);
} ops;
unsigned long connect_delay_ms;
};
static void ssam_hub_update_workfn(struct work_struct *work)
{
struct ssam_hub *hub = container_of(work, struct ssam_hub, update_work.work);
enum ssam_hub_state state;
int status = 0;
status = hub->ops.get_state(hub, &state);
if (status)
return;
/*
* There is a small possibility that hub devices were hot-removed and
* re-added before we were able to remove them here. In that case, both
* the state returned by get_state() and the state of the hub will
* equal SSAM_HUB_CONNECTED and we would bail early below, which would
* leave child devices without proper (re-)initialization and the
* hot-remove flag set.
*
* Therefore, we check whether devices have been hot-removed via an
* additional flag on the hub and, in this case, override the returned
* hub state. In case of a missed disconnect (i.e. get_state returned
* "connected"), we further need to re-schedule this work (with the
* appropriate delay) as the actual connect work submission might have
* been merged with this one.
*
* This then leads to one of two cases: Either we submit an unnecessary
* work item (which will get ignored via either the queue or the state
* checks) or, in the unlikely case that the work is actually required,
* double the normal connect delay.
*/
if (test_and_clear_bit(SSAM_HUB_HOT_REMOVED, &hub->flags)) {
if (state == SSAM_HUB_CONNECTED)
schedule_delayed_work(&hub->update_work, hub->connect_delay);
state = SSAM_HUB_DISCONNECTED;
}
if (hub->state == state)
return;
hub->state = state;
if (hub->state == SSAM_HUB_CONNECTED)
status = ssam_device_register_clients(hub->sdev);
else
ssam_remove_clients(&hub->sdev->dev);
if (status)
dev_err(&hub->sdev->dev, "failed to update hub child devices: %d\n", status);
}
static int ssam_hub_mark_hot_removed(struct device *dev, void *_data)
{
struct ssam_device *sdev = to_ssam_device(dev);
if (is_ssam_device(dev))
ssam_device_mark_hot_removed(sdev);
return 0;
}
static void ssam_hub_update(struct ssam_hub *hub, bool connected)
{
unsigned long delay;
/* Mark devices as hot-removed before we remove any. */
if (!connected) {
set_bit(SSAM_HUB_HOT_REMOVED, &hub->flags);
device_for_each_child_reverse(&hub->sdev->dev, NULL, ssam_hub_mark_hot_removed);
}
/*
* Delay update when the base/keyboard cover is being connected to give
* devices/EC some time to set up.
*/
delay = connected ? hub->connect_delay : 0;
schedule_delayed_work(&hub->update_work, delay);
}
static int __maybe_unused ssam_hub_resume(struct device *dev)
{
struct ssam_hub *hub = dev_get_drvdata(dev);
schedule_delayed_work(&hub->update_work, 0);
return 0;
}
static SIMPLE_DEV_PM_OPS(ssam_hub_pm_ops, NULL, ssam_hub_resume);
static int ssam_hub_probe(struct ssam_device *sdev)
{
const struct ssam_hub_desc *desc;
struct ssam_hub *hub;
int status;
desc = ssam_device_get_match_data(sdev);
if (!desc) {
WARN(1, "no driver match data specified");
return -EINVAL;
}
hub = devm_kzalloc(&sdev->dev, sizeof(*hub), GFP_KERNEL);
if (!hub)
return -ENOMEM;
hub->sdev = sdev;
hub->state = SSAM_HUB_UNINITIALIZED;
hub->notif.base.priority = INT_MAX; /* This notifier should run first. */
hub->notif.base.fn = desc->ops.notify;
hub->notif.event.reg = desc->event.reg;
hub->notif.event.id = desc->event.id;
hub->notif.event.mask = desc->event.mask;
hub->notif.event.flags = SSAM_EVENT_SEQUENCED;
hub->connect_delay = msecs_to_jiffies(desc->connect_delay_ms);
hub->ops.get_state = desc->ops.get_state;
INIT_DELAYED_WORK(&hub->update_work, ssam_hub_update_workfn);
ssam_device_set_drvdata(sdev, hub);
status = ssam_device_notifier_register(sdev, &hub->notif);
if (status)
return status;
schedule_delayed_work(&hub->update_work, 0);
return 0;
}
static void ssam_hub_remove(struct ssam_device *sdev)
{
struct ssam_hub *hub = ssam_device_get_drvdata(sdev);
ssam_device_notifier_unregister(sdev, &hub->notif);
cancel_delayed_work_sync(&hub->update_work);
ssam_remove_clients(&sdev->dev);
}
/* -- SSAM base-subsystem hub driver. --------------------------------------- */
/*
* Some devices (especially battery) may need a bit of time to be fully usable
* after being (re-)connected. This delay has been determined via
* experimentation.
*/
#define SSAM_BASE_UPDATE_CONNECT_DELAY 2500
SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_query_opmode, u8, {
.target_category = SSAM_SSH_TC_BAS,
.target_id = 0x01,
.command_id = 0x0d,
.instance_id = 0x00,
});
#define SSAM_BAS_OPMODE_TABLET 0x00
#define SSAM_EVENT_BAS_CID_CONNECTION 0x0c
static int ssam_base_hub_query_state(struct ssam_hub *hub, enum ssam_hub_state *state)
{
u8 opmode;
int status;
status = ssam_retry(ssam_bas_query_opmode, hub->sdev->ctrl, &opmode);
if (status < 0) {
dev_err(&hub->sdev->dev, "failed to query base state: %d\n", status);
return status;
}
if (opmode != SSAM_BAS_OPMODE_TABLET)
*state = SSAM_HUB_CONNECTED;
else
*state = SSAM_HUB_DISCONNECTED;
return 0;
}
static u32 ssam_base_hub_notif(struct ssam_event_notifier *nf, const struct ssam_event *event)
{
struct ssam_hub *hub = container_of(nf, struct ssam_hub, notif);
if (event->command_id != SSAM_EVENT_BAS_CID_CONNECTION)
return 0;
if (event->length < 1) {
dev_err(&hub->sdev->dev, "unexpected payload size: %u\n", event->length);
return 0;
}
ssam_hub_update(hub, event->data[0]);
/*
* Do not return SSAM_NOTIF_HANDLED: The event should be picked up and
* consumed by the detachment system driver. We're just a (more or less)
* silent observer.
*/
return 0;
}
static const struct ssam_hub_desc base_hub = {
.event = {
.reg = SSAM_EVENT_REGISTRY_SAM,
.id = {
.target_category = SSAM_SSH_TC_BAS,
.instance = 0,
},
.mask = SSAM_EVENT_MASK_NONE,
},
.ops = {
.notify = ssam_base_hub_notif,
.get_state = ssam_base_hub_query_state,
},
.connect_delay_ms = SSAM_BASE_UPDATE_CONNECT_DELAY,
};
/* -- SSAM KIP-subsystem hub driver. ---------------------------------------- */
/*
* Some devices may need a bit of time to be fully usable after being
* (re-)connected. This delay has been determined via experimentation.
*/
#define SSAM_KIP_UPDATE_CONNECT_DELAY 250
#define SSAM_EVENT_KIP_CID_CONNECTION 0x2c
SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_query_state, u8, {
.target_category = SSAM_SSH_TC_KIP,
.target_id = 0x01,
.command_id = 0x2c,
.instance_id = 0x00,
});
static int ssam_kip_hub_query_state(struct ssam_hub *hub, enum ssam_hub_state *state)
{
int status;
u8 connected;
status = ssam_retry(__ssam_kip_query_state, hub->sdev->ctrl, &connected);
if (status < 0) {
dev_err(&hub->sdev->dev, "failed to query KIP connection state: %d\n", status);
return status;
}
*state = connected ? SSAM_HUB_CONNECTED : SSAM_HUB_DISCONNECTED;
return 0;
}
static u32 ssam_kip_hub_notif(struct ssam_event_notifier *nf, const struct ssam_event *event)
{
struct ssam_hub *hub = container_of(nf, struct ssam_hub, notif);
if (event->command_id != SSAM_EVENT_KIP_CID_CONNECTION)
return 0; /* Return "unhandled". */
if (event->length < 1) {
dev_err(&hub->sdev->dev, "unexpected payload size: %u\n", event->length);
return 0;
}
ssam_hub_update(hub, event->data[0]);
return SSAM_NOTIF_HANDLED;
}
static const struct ssam_hub_desc kip_hub = {
.event = {
.reg = SSAM_EVENT_REGISTRY_SAM,
.id = {
.target_category = SSAM_SSH_TC_KIP,
.instance = 0,
},
.mask = SSAM_EVENT_MASK_TARGET,
},
.ops = {
.notify = ssam_kip_hub_notif,
.get_state = ssam_kip_hub_query_state,
},
.connect_delay_ms = SSAM_KIP_UPDATE_CONNECT_DELAY,
};
/* -- Driver registration. -------------------------------------------------- */
static const struct ssam_device_id ssam_hub_match[] = {
{ SSAM_VDEV(HUB, 0x01, SSAM_SSH_TC_KIP, 0x00), (unsigned long)&kip_hub },
{ SSAM_VDEV(HUB, 0x02, SSAM_SSH_TC_BAS, 0x00), (unsigned long)&base_hub },
{ }
};
MODULE_DEVICE_TABLE(ssam, ssam_hub_match);
static struct ssam_device_driver ssam_subsystem_hub_driver = {
.probe = ssam_hub_probe,
.remove = ssam_hub_remove,
.match_table = ssam_hub_match,
.driver = {
.name = "surface_aggregator_subsystem_hub",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &ssam_hub_pm_ops,
},
};
module_ssam_device_driver(ssam_subsystem_hub_driver);
MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
MODULE_DESCRIPTION("Subsystem device hub driver for Surface System Aggregator Module");
MODULE_LICENSE("GPL");

View File

@ -6,19 +6,16 @@
* cannot be auto-detected. Provides device-hubs and performs instantiation
* for these devices.
*
* Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2020-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/limits.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/surface_aggregator/controller.h>
#include <linux/surface_aggregator/device.h>
@ -41,9 +38,15 @@ static const struct software_node ssam_node_root = {
.name = "ssam_platform_hub",
};
/* KIP device hub (connects keyboard cover devices on Surface Pro 8). */
static const struct software_node ssam_node_hub_kip = {
.name = "ssam:00:00:01:0e:00",
.parent = &ssam_node_root,
};
/* Base device hub (devices attached to Surface Book 3 base). */
static const struct software_node ssam_node_hub_base = {
.name = "ssam:00:00:02:00:00",
.name = "ssam:00:00:02:11:00",
.parent = &ssam_node_root,
};
@ -71,6 +74,12 @@ static const struct software_node ssam_node_tmp_pprof = {
.parent = &ssam_node_root,
};
/* Tablet-mode switch via KIP subsystem. */
static const struct software_node ssam_node_kip_tablet_switch = {
.name = "ssam:01:0e:01:00:01",
.parent = &ssam_node_root,
};
/* DTX / detachment-system device (Surface Book 3). */
static const struct software_node ssam_node_bas_dtx = {
.name = "ssam:01:11:01:00:00",
@ -155,6 +164,36 @@ static const struct software_node ssam_node_hid_base_iid6 = {
.parent = &ssam_node_hub_base,
};
/* HID keyboard (KIP hub). */
static const struct software_node ssam_node_hid_kip_keyboard = {
.name = "ssam:01:15:02:01:00",
.parent = &ssam_node_hub_kip,
};
/* HID pen stash (KIP hub; pen taken / stashed away evens). */
static const struct software_node ssam_node_hid_kip_penstash = {
.name = "ssam:01:15:02:02:00",
.parent = &ssam_node_hub_kip,
};
/* HID touchpad (KIP hub). */
static const struct software_node ssam_node_hid_kip_touchpad = {
.name = "ssam:01:15:02:03:00",
.parent = &ssam_node_hub_kip,
};
/* HID device instance 5 (KIP hub, unknown HID device). */
static const struct software_node ssam_node_hid_kip_iid5 = {
.name = "ssam:01:15:02:05:00",
.parent = &ssam_node_hub_kip,
};
/* Tablet-mode switch via POS subsystem. */
static const struct software_node ssam_node_pos_tablet_switch = {
.name = "ssam:01:26:01:00:01",
.parent = &ssam_node_root,
};
/*
* Devices for 5th- and 6th-generations models:
* - Surface Book 2,
@ -201,6 +240,7 @@ static const struct software_node *ssam_node_group_sls[] = {
&ssam_node_bat_ac,
&ssam_node_bat_main,
&ssam_node_tmp_pprof,
&ssam_node_pos_tablet_switch,
&ssam_node_hid_tid1_keyboard,
&ssam_node_hid_tid1_penstash,
&ssam_node_hid_tid1_touchpad,
@ -230,290 +270,19 @@ static const struct software_node *ssam_node_group_sp7[] = {
static const struct software_node *ssam_node_group_sp8[] = {
&ssam_node_root,
&ssam_node_hub_kip,
&ssam_node_bat_ac,
&ssam_node_bat_main,
&ssam_node_tmp_pprof,
/* TODO: Add support for keyboard cover. */
&ssam_node_kip_tablet_switch,
&ssam_node_hid_kip_keyboard,
&ssam_node_hid_kip_penstash,
&ssam_node_hid_kip_touchpad,
&ssam_node_hid_kip_iid5,
NULL,
};
/* -- Device registry helper functions. ------------------------------------- */
static int ssam_uid_from_string(const char *str, struct ssam_device_uid *uid)
{
u8 d, tc, tid, iid, fn;
int n;
n = sscanf(str, "ssam:%hhx:%hhx:%hhx:%hhx:%hhx", &d, &tc, &tid, &iid, &fn);
if (n != 5)
return -EINVAL;
uid->domain = d;
uid->category = tc;
uid->target = tid;
uid->instance = iid;
uid->function = fn;
return 0;
}
static int ssam_hub_add_device(struct device *parent, struct ssam_controller *ctrl,
struct fwnode_handle *node)
{
struct ssam_device_uid uid;
struct ssam_device *sdev;
int status;
status = ssam_uid_from_string(fwnode_get_name(node), &uid);
if (status)
return status;
sdev = ssam_device_alloc(ctrl, uid);
if (!sdev)
return -ENOMEM;
sdev->dev.parent = parent;
sdev->dev.fwnode = node;
status = ssam_device_add(sdev);
if (status)
ssam_device_put(sdev);
return status;
}
static int ssam_hub_register_clients(struct device *parent, struct ssam_controller *ctrl,
struct fwnode_handle *node)
{
struct fwnode_handle *child;
int status;
fwnode_for_each_child_node(node, child) {
/*
* Try to add the device specified in the firmware node. If
* this fails with -EINVAL, the node does not specify any SSAM
* device, so ignore it and continue with the next one.
*/
status = ssam_hub_add_device(parent, ctrl, child);
if (status && status != -EINVAL)
goto err;
}
return 0;
err:
ssam_remove_clients(parent);
return status;
}
/* -- SSAM base-hub driver. ------------------------------------------------- */
/*
* Some devices (especially battery) may need a bit of time to be fully usable
* after being (re-)connected. This delay has been determined via
* experimentation.
*/
#define SSAM_BASE_UPDATE_CONNECT_DELAY msecs_to_jiffies(2500)
enum ssam_base_hub_state {
SSAM_BASE_HUB_UNINITIALIZED,
SSAM_BASE_HUB_CONNECTED,
SSAM_BASE_HUB_DISCONNECTED,
};
struct ssam_base_hub {
struct ssam_device *sdev;
enum ssam_base_hub_state state;
struct delayed_work update_work;
struct ssam_event_notifier notif;
};
SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_query_opmode, u8, {
.target_category = SSAM_SSH_TC_BAS,
.target_id = 0x01,
.command_id = 0x0d,
.instance_id = 0x00,
});
#define SSAM_BAS_OPMODE_TABLET 0x00
#define SSAM_EVENT_BAS_CID_CONNECTION 0x0c
static int ssam_base_hub_query_state(struct ssam_base_hub *hub, enum ssam_base_hub_state *state)
{
u8 opmode;
int status;
status = ssam_retry(ssam_bas_query_opmode, hub->sdev->ctrl, &opmode);
if (status < 0) {
dev_err(&hub->sdev->dev, "failed to query base state: %d\n", status);
return status;
}
if (opmode != SSAM_BAS_OPMODE_TABLET)
*state = SSAM_BASE_HUB_CONNECTED;
else
*state = SSAM_BASE_HUB_DISCONNECTED;
return 0;
}
static ssize_t ssam_base_hub_state_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct ssam_base_hub *hub = dev_get_drvdata(dev);
bool connected = hub->state == SSAM_BASE_HUB_CONNECTED;
return sysfs_emit(buf, "%d\n", connected);
}
static struct device_attribute ssam_base_hub_attr_state =
__ATTR(state, 0444, ssam_base_hub_state_show, NULL);
static struct attribute *ssam_base_hub_attrs[] = {
&ssam_base_hub_attr_state.attr,
NULL,
};
static const struct attribute_group ssam_base_hub_group = {
.attrs = ssam_base_hub_attrs,
};
static void ssam_base_hub_update_workfn(struct work_struct *work)
{
struct ssam_base_hub *hub = container_of(work, struct ssam_base_hub, update_work.work);
struct fwnode_handle *node = dev_fwnode(&hub->sdev->dev);
enum ssam_base_hub_state state;
int status = 0;
status = ssam_base_hub_query_state(hub, &state);
if (status)
return;
if (hub->state == state)
return;
hub->state = state;
if (hub->state == SSAM_BASE_HUB_CONNECTED)
status = ssam_hub_register_clients(&hub->sdev->dev, hub->sdev->ctrl, node);
else
ssam_remove_clients(&hub->sdev->dev);
if (status)
dev_err(&hub->sdev->dev, "failed to update base-hub devices: %d\n", status);
}
static u32 ssam_base_hub_notif(struct ssam_event_notifier *nf, const struct ssam_event *event)
{
struct ssam_base_hub *hub = container_of(nf, struct ssam_base_hub, notif);
unsigned long delay;
if (event->command_id != SSAM_EVENT_BAS_CID_CONNECTION)
return 0;
if (event->length < 1) {
dev_err(&hub->sdev->dev, "unexpected payload size: %u\n", event->length);
return 0;
}
/*
* Delay update when the base is being connected to give devices/EC
* some time to set up.
*/
delay = event->data[0] ? SSAM_BASE_UPDATE_CONNECT_DELAY : 0;
schedule_delayed_work(&hub->update_work, delay);
/*
* Do not return SSAM_NOTIF_HANDLED: The event should be picked up and
* consumed by the detachment system driver. We're just a (more or less)
* silent observer.
*/
return 0;
}
static int __maybe_unused ssam_base_hub_resume(struct device *dev)
{
struct ssam_base_hub *hub = dev_get_drvdata(dev);
schedule_delayed_work(&hub->update_work, 0);
return 0;
}
static SIMPLE_DEV_PM_OPS(ssam_base_hub_pm_ops, NULL, ssam_base_hub_resume);
static int ssam_base_hub_probe(struct ssam_device *sdev)
{
struct ssam_base_hub *hub;
int status;
hub = devm_kzalloc(&sdev->dev, sizeof(*hub), GFP_KERNEL);
if (!hub)
return -ENOMEM;
hub->sdev = sdev;
hub->state = SSAM_BASE_HUB_UNINITIALIZED;
hub->notif.base.priority = INT_MAX; /* This notifier should run first. */
hub->notif.base.fn = ssam_base_hub_notif;
hub->notif.event.reg = SSAM_EVENT_REGISTRY_SAM;
hub->notif.event.id.target_category = SSAM_SSH_TC_BAS,
hub->notif.event.id.instance = 0,
hub->notif.event.mask = SSAM_EVENT_MASK_NONE;
hub->notif.event.flags = SSAM_EVENT_SEQUENCED;
INIT_DELAYED_WORK(&hub->update_work, ssam_base_hub_update_workfn);
ssam_device_set_drvdata(sdev, hub);
status = ssam_notifier_register(sdev->ctrl, &hub->notif);
if (status)
return status;
status = sysfs_create_group(&sdev->dev.kobj, &ssam_base_hub_group);
if (status)
goto err;
schedule_delayed_work(&hub->update_work, 0);
return 0;
err:
ssam_notifier_unregister(sdev->ctrl, &hub->notif);
cancel_delayed_work_sync(&hub->update_work);
ssam_remove_clients(&sdev->dev);
return status;
}
static void ssam_base_hub_remove(struct ssam_device *sdev)
{
struct ssam_base_hub *hub = ssam_device_get_drvdata(sdev);
sysfs_remove_group(&sdev->dev.kobj, &ssam_base_hub_group);
ssam_notifier_unregister(sdev->ctrl, &hub->notif);
cancel_delayed_work_sync(&hub->update_work);
ssam_remove_clients(&sdev->dev);
}
static const struct ssam_device_id ssam_base_hub_match[] = {
{ SSAM_VDEV(HUB, 0x02, SSAM_ANY_IID, 0x00) },
{ },
};
static struct ssam_device_driver ssam_base_hub_driver = {
.probe = ssam_base_hub_probe,
.remove = ssam_base_hub_remove,
.match_table = ssam_base_hub_match,
.driver = {
.name = "surface_aggregator_base_hub",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &ssam_base_hub_pm_ops,
},
};
/* -- SSAM platform/meta-hub driver. ---------------------------------------- */
static const struct acpi_device_id ssam_platform_hub_match[] = {
@ -597,7 +366,7 @@ static int ssam_platform_hub_probe(struct platform_device *pdev)
set_secondary_fwnode(&pdev->dev, root);
status = ssam_hub_register_clients(&pdev->dev, ctrl, root);
status = __ssam_register_clients(&pdev->dev, ctrl, root);
if (status) {
set_secondary_fwnode(&pdev->dev, NULL);
software_node_unregister_node_group(nodes);
@ -626,32 +395,7 @@ static struct platform_driver ssam_platform_hub_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
/* -- Module initialization. ------------------------------------------------ */
static int __init ssam_device_hub_init(void)
{
int status;
status = platform_driver_register(&ssam_platform_hub_driver);
if (status)
return status;
status = ssam_device_driver_register(&ssam_base_hub_driver);
if (status)
platform_driver_unregister(&ssam_platform_hub_driver);
return status;
}
module_init(ssam_device_hub_init);
static void __exit ssam_device_hub_exit(void)
{
ssam_device_driver_unregister(&ssam_base_hub_driver);
platform_driver_unregister(&ssam_platform_hub_driver);
}
module_exit(ssam_device_hub_exit);
module_platform_driver(ssam_platform_hub_driver);
MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
MODULE_DESCRIPTION("Device-registry for Surface System Aggregator Module");

View File

@ -0,0 +1,533 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Surface System Aggregator Module (SSAM) tablet mode switch driver.
*
* Copyright (C) 2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#include <asm/unaligned.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/surface_aggregator/controller.h>
#include <linux/surface_aggregator/device.h>
/* -- SSAM generic tablet switch driver framework. -------------------------- */
struct ssam_tablet_sw;
struct ssam_tablet_sw_ops {
int (*get_state)(struct ssam_tablet_sw *sw, u32 *state);
const char *(*state_name)(struct ssam_tablet_sw *sw, u32 state);
bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, u32 state);
};
struct ssam_tablet_sw {
struct ssam_device *sdev;
u32 state;
struct work_struct update_work;
struct input_dev *mode_switch;
struct ssam_tablet_sw_ops ops;
struct ssam_event_notifier notif;
};
struct ssam_tablet_sw_desc {
struct {
const char *name;
const char *phys;
} dev;
struct {
u32 (*notify)(struct ssam_event_notifier *nf, const struct ssam_event *event);
int (*get_state)(struct ssam_tablet_sw *sw, u32 *state);
const char *(*state_name)(struct ssam_tablet_sw *sw, u32 state);
bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, u32 state);
} ops;
struct {
struct ssam_event_registry reg;
struct ssam_event_id id;
enum ssam_event_mask mask;
u8 flags;
} event;
};
static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct ssam_tablet_sw *sw = dev_get_drvdata(dev);
const char *state = sw->ops.state_name(sw, sw->state);
return sysfs_emit(buf, "%s\n", state);
}
static DEVICE_ATTR_RO(state);
static struct attribute *ssam_tablet_sw_attrs[] = {
&dev_attr_state.attr,
NULL,
};
static const struct attribute_group ssam_tablet_sw_group = {
.attrs = ssam_tablet_sw_attrs,
};
static void ssam_tablet_sw_update_workfn(struct work_struct *work)
{
struct ssam_tablet_sw *sw = container_of(work, struct ssam_tablet_sw, update_work);
int tablet, status;
u32 state;
status = sw->ops.get_state(sw, &state);
if (status)
return;
if (sw->state == state)
return;
sw->state = state;
/* Send SW_TABLET_MODE event. */
tablet = sw->ops.state_is_tablet_mode(sw, state);
input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet);
input_sync(sw->mode_switch);
}
static int __maybe_unused ssam_tablet_sw_resume(struct device *dev)
{
struct ssam_tablet_sw *sw = dev_get_drvdata(dev);
schedule_work(&sw->update_work);
return 0;
}
static SIMPLE_DEV_PM_OPS(ssam_tablet_sw_pm_ops, NULL, ssam_tablet_sw_resume);
static int ssam_tablet_sw_probe(struct ssam_device *sdev)
{
const struct ssam_tablet_sw_desc *desc;
struct ssam_tablet_sw *sw;
int tablet, status;
desc = ssam_device_get_match_data(sdev);
if (!desc) {
WARN(1, "no driver match data specified");
return -EINVAL;
}
sw = devm_kzalloc(&sdev->dev, sizeof(*sw), GFP_KERNEL);
if (!sw)
return -ENOMEM;
sw->sdev = sdev;
sw->ops.get_state = desc->ops.get_state;
sw->ops.state_name = desc->ops.state_name;
sw->ops.state_is_tablet_mode = desc->ops.state_is_tablet_mode;
INIT_WORK(&sw->update_work, ssam_tablet_sw_update_workfn);
ssam_device_set_drvdata(sdev, sw);
/* Get initial state. */
status = sw->ops.get_state(sw, &sw->state);
if (status)
return status;
/* Set up tablet mode switch. */
sw->mode_switch = devm_input_allocate_device(&sdev->dev);
if (!sw->mode_switch)
return -ENOMEM;
sw->mode_switch->name = desc->dev.name;
sw->mode_switch->phys = desc->dev.phys;
sw->mode_switch->id.bustype = BUS_HOST;
sw->mode_switch->dev.parent = &sdev->dev;
tablet = sw->ops.state_is_tablet_mode(sw, sw->state);
input_set_capability(sw->mode_switch, EV_SW, SW_TABLET_MODE);
input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet);
status = input_register_device(sw->mode_switch);
if (status)
return status;
/* Set up notifier. */
sw->notif.base.priority = 0;
sw->notif.base.fn = desc->ops.notify;
sw->notif.event.reg = desc->event.reg;
sw->notif.event.id = desc->event.id;
sw->notif.event.mask = desc->event.mask;
sw->notif.event.flags = SSAM_EVENT_SEQUENCED;
status = ssam_device_notifier_register(sdev, &sw->notif);
if (status)
return status;
status = sysfs_create_group(&sdev->dev.kobj, &ssam_tablet_sw_group);
if (status)
goto err;
/* We might have missed events during setup, so check again. */
schedule_work(&sw->update_work);
return 0;
err:
ssam_device_notifier_unregister(sdev, &sw->notif);
cancel_work_sync(&sw->update_work);
return status;
}
static void ssam_tablet_sw_remove(struct ssam_device *sdev)
{
struct ssam_tablet_sw *sw = ssam_device_get_drvdata(sdev);
sysfs_remove_group(&sdev->dev.kobj, &ssam_tablet_sw_group);
ssam_device_notifier_unregister(sdev, &sw->notif);
cancel_work_sync(&sw->update_work);
}
/* -- SSAM KIP tablet switch implementation. -------------------------------- */
#define SSAM_EVENT_KIP_CID_COVER_STATE_CHANGED 0x1d
enum ssam_kip_cover_state {
SSAM_KIP_COVER_STATE_DISCONNECTED = 0x01,
SSAM_KIP_COVER_STATE_CLOSED = 0x02,
SSAM_KIP_COVER_STATE_LAPTOP = 0x03,
SSAM_KIP_COVER_STATE_FOLDED_CANVAS = 0x04,
SSAM_KIP_COVER_STATE_FOLDED_BACK = 0x05,
};
static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw, u32 state)
{
switch (state) {
case SSAM_KIP_COVER_STATE_DISCONNECTED:
return "disconnected";
case SSAM_KIP_COVER_STATE_CLOSED:
return "closed";
case SSAM_KIP_COVER_STATE_LAPTOP:
return "laptop";
case SSAM_KIP_COVER_STATE_FOLDED_CANVAS:
return "folded-canvas";
case SSAM_KIP_COVER_STATE_FOLDED_BACK:
return "folded-back";
default:
dev_warn(&sw->sdev->dev, "unknown KIP cover state: %u\n", state);
return "<unknown>";
}
}
static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 state)
{
switch (state) {
case SSAM_KIP_COVER_STATE_DISCONNECTED:
case SSAM_KIP_COVER_STATE_FOLDED_CANVAS:
case SSAM_KIP_COVER_STATE_FOLDED_BACK:
return true;
case SSAM_KIP_COVER_STATE_CLOSED:
case SSAM_KIP_COVER_STATE_LAPTOP:
return false;
default:
dev_warn(&sw->sdev->dev, "unknown KIP cover state: %d\n", sw->state);
return true;
}
}
SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_get_cover_state, u8, {
.target_category = SSAM_SSH_TC_KIP,
.target_id = 0x01,
.command_id = 0x1d,
.instance_id = 0x00,
});
static int ssam_kip_get_cover_state(struct ssam_tablet_sw *sw, u32 *state)
{
int status;
u8 raw;
status = ssam_retry(__ssam_kip_get_cover_state, sw->sdev->ctrl, &raw);
if (status < 0) {
dev_err(&sw->sdev->dev, "failed to query KIP lid state: %d\n", status);
return status;
}
*state = raw;
return 0;
}
static u32 ssam_kip_sw_notif(struct ssam_event_notifier *nf, const struct ssam_event *event)
{
struct ssam_tablet_sw *sw = container_of(nf, struct ssam_tablet_sw, notif);
if (event->command_id != SSAM_EVENT_KIP_CID_COVER_STATE_CHANGED)
return 0; /* Return "unhandled". */
if (event->length < 1)
dev_warn(&sw->sdev->dev, "unexpected payload size: %u\n", event->length);
schedule_work(&sw->update_work);
return SSAM_NOTIF_HANDLED;
}
static const struct ssam_tablet_sw_desc ssam_kip_sw_desc = {
.dev = {
.name = "Microsoft Surface KIP Tablet Mode Switch",
.phys = "ssam/01:0e:01:00:01/input0",
},
.ops = {
.notify = ssam_kip_sw_notif,
.get_state = ssam_kip_get_cover_state,
.state_name = ssam_kip_cover_state_name,
.state_is_tablet_mode = ssam_kip_cover_state_is_tablet_mode,
},
.event = {
.reg = SSAM_EVENT_REGISTRY_SAM,
.id = {
.target_category = SSAM_SSH_TC_KIP,
.instance = 0,
},
.mask = SSAM_EVENT_MASK_TARGET,
},
};
/* -- SSAM POS tablet switch implementation. -------------------------------- */
static bool tablet_mode_in_slate_state = true;
module_param(tablet_mode_in_slate_state, bool, 0644);
MODULE_PARM_DESC(tablet_mode_in_slate_state, "Enable tablet mode in slate device posture, default is 'true'");
#define SSAM_EVENT_POS_CID_POSTURE_CHANGED 0x03
#define SSAM_POS_MAX_SOURCES 4
enum ssam_pos_state {
SSAM_POS_POSTURE_LID_CLOSED = 0x00,
SSAM_POS_POSTURE_LAPTOP = 0x01,
SSAM_POS_POSTURE_SLATE = 0x02,
SSAM_POS_POSTURE_TABLET = 0x03,
};
struct ssam_sources_list {
__le32 count;
__le32 id[SSAM_POS_MAX_SOURCES];
} __packed;
static const char *ssam_pos_state_name(struct ssam_tablet_sw *sw, u32 state)
{
switch (state) {
case SSAM_POS_POSTURE_LID_CLOSED:
return "closed";
case SSAM_POS_POSTURE_LAPTOP:
return "laptop";
case SSAM_POS_POSTURE_SLATE:
return "slate";
case SSAM_POS_POSTURE_TABLET:
return "tablet";
default:
dev_warn(&sw->sdev->dev, "unknown device posture: %u\n", state);
return "<unknown>";
}
}
static bool ssam_pos_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 state)
{
switch (state) {
case SSAM_POS_POSTURE_LAPTOP:
case SSAM_POS_POSTURE_LID_CLOSED:
return false;
case SSAM_POS_POSTURE_SLATE:
return tablet_mode_in_slate_state;
case SSAM_POS_POSTURE_TABLET:
return true;
default:
dev_warn(&sw->sdev->dev, "unknown device posture: %u\n", state);
return true;
}
}
static int ssam_pos_get_sources_list(struct ssam_tablet_sw *sw, struct ssam_sources_list *sources)
{
struct ssam_request rqst;
struct ssam_response rsp;
int status;
rqst.target_category = SSAM_SSH_TC_POS;
rqst.target_id = 0x01;
rqst.command_id = 0x01;
rqst.instance_id = 0x00;
rqst.flags = SSAM_REQUEST_HAS_RESPONSE;
rqst.length = 0;
rqst.payload = NULL;
rsp.capacity = sizeof(*sources);
rsp.length = 0;
rsp.pointer = (u8 *)sources;
status = ssam_retry(ssam_request_sync_onstack, sw->sdev->ctrl, &rqst, &rsp, 0);
if (status)
return status;
/* We need at least the 'sources->count' field. */
if (rsp.length < sizeof(__le32)) {
dev_err(&sw->sdev->dev, "received source list response is too small\n");
return -EPROTO;
}
/* Make sure 'sources->count' matches with the response length. */
if (get_unaligned_le32(&sources->count) * sizeof(__le32) + sizeof(__le32) != rsp.length) {
dev_err(&sw->sdev->dev, "mismatch between number of sources and response size\n");
return -EPROTO;
}
return 0;
}
static int ssam_pos_get_source(struct ssam_tablet_sw *sw, u32 *source_id)
{
struct ssam_sources_list sources = {};
int status;
status = ssam_pos_get_sources_list(sw, &sources);
if (status)
return status;
if (get_unaligned_le32(&sources.count) == 0) {
dev_err(&sw->sdev->dev, "no posture sources found\n");
return -ENODEV;
}
/*
* We currently don't know what to do with more than one posture
* source. At the moment, only one source seems to be used/provided.
* The WARN_ON() here should hopefully let us know quickly once there
* is a device that provides multiple sources, at which point we can
* then try to figure out how to handle them.
*/
WARN_ON(get_unaligned_le32(&sources.count) > 1);
*source_id = get_unaligned_le32(&sources.id[0]);
return 0;
}
SSAM_DEFINE_SYNC_REQUEST_WR(__ssam_pos_get_posture_for_source, __le32, __le32, {
.target_category = SSAM_SSH_TC_POS,
.target_id = 0x01,
.command_id = 0x02,
.instance_id = 0x00,
});
static int ssam_pos_get_posture_for_source(struct ssam_tablet_sw *sw, u32 source_id, u32 *posture)
{
__le32 source_le = cpu_to_le32(source_id);
__le32 rspval_le = 0;
int status;
status = ssam_retry(__ssam_pos_get_posture_for_source, sw->sdev->ctrl,
&source_le, &rspval_le);
if (status)
return status;
*posture = le32_to_cpu(rspval_le);
return 0;
}
static int ssam_pos_get_posture(struct ssam_tablet_sw *sw, u32 *state)
{
u32 source_id;
int status;
status = ssam_pos_get_source(sw, &source_id);
if (status) {
dev_err(&sw->sdev->dev, "failed to get posture source ID: %d\n", status);
return status;
}
status = ssam_pos_get_posture_for_source(sw, source_id, state);
if (status) {
dev_err(&sw->sdev->dev, "failed to get posture value for source %u: %d\n",
source_id, status);
return status;
}
return 0;
}
static u32 ssam_pos_sw_notif(struct ssam_event_notifier *nf, const struct ssam_event *event)
{
struct ssam_tablet_sw *sw = container_of(nf, struct ssam_tablet_sw, notif);
if (event->command_id != SSAM_EVENT_POS_CID_POSTURE_CHANGED)
return 0; /* Return "unhandled". */
if (event->length != sizeof(__le32) * 3)
dev_warn(&sw->sdev->dev, "unexpected payload size: %u\n", event->length);
schedule_work(&sw->update_work);
return SSAM_NOTIF_HANDLED;
}
static const struct ssam_tablet_sw_desc ssam_pos_sw_desc = {
.dev = {
.name = "Microsoft Surface POS Tablet Mode Switch",
.phys = "ssam/01:26:01:00:01/input0",
},
.ops = {
.notify = ssam_pos_sw_notif,
.get_state = ssam_pos_get_posture,
.state_name = ssam_pos_state_name,
.state_is_tablet_mode = ssam_pos_state_is_tablet_mode,
},
.event = {
.reg = SSAM_EVENT_REGISTRY_SAM,
.id = {
.target_category = SSAM_SSH_TC_POS,
.instance = 0,
},
.mask = SSAM_EVENT_MASK_TARGET,
},
};
/* -- Driver registration. -------------------------------------------------- */
static const struct ssam_device_id ssam_tablet_sw_match[] = {
{ SSAM_SDEV(KIP, 0x01, 0x00, 0x01), (unsigned long)&ssam_kip_sw_desc },
{ SSAM_SDEV(POS, 0x01, 0x00, 0x01), (unsigned long)&ssam_pos_sw_desc },
{ },
};
MODULE_DEVICE_TABLE(ssam, ssam_tablet_sw_match);
static struct ssam_device_driver ssam_tablet_sw_driver = {
.probe = ssam_tablet_sw_probe,
.remove = ssam_tablet_sw_remove,
.match_table = ssam_tablet_sw_match,
.driver = {
.name = "surface_aggregator_tablet_mode_switch",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &ssam_tablet_sw_pm_ops,
},
};
module_ssam_device_driver(ssam_tablet_sw_driver);
MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
MODULE_DESCRIPTION("Tablet mode switch driver for Surface devices using the Surface Aggregator Module");
MODULE_LICENSE("GPL");

View File

@ -8,7 +8,7 @@
* acknowledge (to speed things up), abort (e.g. in case the dGPU is still in
* use), or request detachment via user-space.
*
* Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#include <linux/fs.h>

View File

@ -4,7 +4,7 @@
* properly configuring the respective GPEs. Required for wakeup via lid on
* newer Intel-based Microsoft Surface devices.
*
* Copyright (C) 2020 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2020-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@ -171,6 +171,18 @@ static const struct dmi_system_id dmi_lid_device_table[] = {
},
.driver_data = (void *)lid_device_props_l4D,
},
{
.ident = "Surface Laptop 4 (Intel 13\")",
.matches = {
/*
* We match for SKU here due to different variants: The
* AMD (15") version does not rely on GPEs.
*/
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_4_1950:1951"),
},
.driver_data = (void *)lid_device_props_l4B,
},
{
.ident = "Surface Laptop Studio",
.matches = {

View File

@ -10,7 +10,7 @@
* Event signaling is handled via ACPI, which will generate the appropriate
* device-check notifications to be picked up by the PCIe hot-plug driver.
*
* Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#include <linux/acpi.h>

View File

@ -3,7 +3,7 @@
* Surface Platform Profile / Performance Mode driver for Surface System
* Aggregator Module (thermal subsystem).
*
* Copyright (C) 2021 Maximilian Luz <luzmaximilian@gmail.com>
* Copyright (C) 2021-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
#include <asm/unaligned.h>

View File

@ -177,17 +177,15 @@ config ACER_WIRELESS
config ACER_WMI
tristate "Acer WMI Laptop Extras"
depends on ACPI
select LEDS_CLASS
select NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE
depends on SERIO_I8042
depends on INPUT
depends on RFKILL || RFKILL = n
depends on ACPI_WMI
select ACPI_VIDEO
select INPUT_SPARSEKMAP
# Acer WMI depends on ACPI_VIDEO when ACPI is enabled
select ACPI_VIDEO if ACPI
select LEDS_CLASS
select NEW_LEDS
help
This is a driver for newer Acer (and Wistron) laptops. It adds
wireless radio and bluetooth control, and on some laptops,
@ -196,32 +194,7 @@ config ACER_WMI
If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
here.
config AMD_PMC
tristate "AMD SoC PMC driver"
depends on ACPI && PCI && RTC_CLASS
help
The driver provides support for AMD Power Management Controller
primarily responsible for S2Idle transactions that are driven from
a platform firmware running on SMU. This driver also provides a debug
mechanism to investigate the S2Idle transactions and failures.
Say Y or M here if you have a notebook powered by AMD RYZEN CPU/APU.
If you choose to compile this driver as a module the module will be
called amd-pmc.
config AMD_HSMP
tristate "AMD HSMP Driver"
depends on AMD_NB && X86_64
help
The driver provides a way for user space tools to monitor and manage
system management functionality on EPYC server CPUs from AMD.
Host System Management Port (HSMP) interface is a mailbox interface
between the x86 core and the System Management Unit (SMU) firmware.
If you choose to compile this driver as a module the module will be
called amd_hsmp.
source "drivers/platform/x86/amd/Kconfig"
config ADV_SWBUTTON
tristate "Advantech ACPI Software Button Driver"
@ -300,6 +273,8 @@ config ASUS_WMI
select INPUT_SPARSEKMAP
select LEDS_CLASS
select NEW_LEDS
select LEDS_TRIGGERS
select LEDS_TRIGGER_AUDIO
select ACPI_PLATFORM_PROFILE
help
Say Y here if you have a WMI aware Asus laptop (like Eee PCs or new
@ -1164,7 +1139,14 @@ config WINMATE_FM07_KEYS
endif # X86_PLATFORM_DEVICES
config PMC_ATOM
def_bool y
depends on PCI
select COMMON_CLK
config P2SB
bool "Primary to Sideband (P2SB) bridge access support"
depends on PCI && X86
help
The Primary to Sideband (P2SB) bridge is an interface to some
PCI devices connected through it. In particular, SPI NOR controller
in Intel Apollo Lake SoC is one of such devices.
The main purpose of this library is to unhide P2SB device in case
firmware kept it hidden on some platforms in order to access devices
behind it.

View File

@ -23,8 +23,7 @@ obj-$(CONFIG_ACER_WIRELESS) += acer-wireless.o
obj-$(CONFIG_ACER_WMI) += acer-wmi.o
# AMD
obj-$(CONFIG_AMD_PMC) += amd-pmc.o
obj-$(CONFIG_AMD_HSMP) += amd_hsmp.o
obj-y += amd/
# Advantech
obj-$(CONFIG_ADV_SWBUTTON) += adv_swbutton.o
@ -120,13 +119,17 @@ obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o
# Intel uncore drivers
obj-$(CONFIG_INTEL_IPS) += intel_ips.o
# Intel miscellaneous drivers
intel_p2sb-y := p2sb.o
obj-$(CONFIG_P2SB) += intel_p2sb.o
# Intel PMIC / PMC / P-Unit devices
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
obj-$(CONFIG_INTEL_SCU_PCI) += intel_scu_pcidrv.o
obj-$(CONFIG_INTEL_SCU_PLATFORM) += intel_scu_pltdrv.o
obj-$(CONFIG_INTEL_SCU_WDT) += intel_scu_wdt.o
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
obj-$(CONFIG_PMC_ATOM) += pmc_atom.o
obj-$(CONFIG_X86_INTEL_LPSS) += pmc_atom.o
# Siemens Simatic Industrial PCs
obj-$(CONFIG_SIEMENS_SIMATIC_IPC) += simatic-ipc.o

View File

@ -1615,12 +1615,7 @@ static int read_brightness(struct backlight_device *bd)
static int update_bl_status(struct backlight_device *bd)
{
int intensity = bd->props.brightness;
if (bd->props.power != FB_BLANK_UNBLANK)
intensity = 0;
if (bd->props.fb_blank != FB_BLANK_UNBLANK)
intensity = 0;
int intensity = backlight_get_brightness(bd);
set_u32(intensity, ACER_CAP_BRIGHTNESS);

View File

@ -0,0 +1,31 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# AMD x86 Platform Specific Drivers
#
config AMD_PMC
tristate "AMD SoC PMC driver"
depends on ACPI && PCI && RTC_CLASS
help
The driver provides support for AMD Power Management Controller
primarily responsible for S2Idle transactions that are driven from
a platform firmware running on SMU. This driver also provides a debug
mechanism to investigate the S2Idle transactions and failures.
Say Y or M here if you have a notebook powered by AMD RYZEN CPU/APU.
If you choose to compile this driver as a module the module will be
called amd-pmc.
config AMD_HSMP
tristate "AMD HSMP Driver"
depends on AMD_NB && X86_64
help
The driver provides a way for user space tools to monitor and manage
system management functionality on EPYC server CPUs from AMD.
Host System Management Port (HSMP) interface is a mailbox interface
between the x86 core and the System Management Unit (SMU) firmware.
If you choose to compile this driver as a module the module will be
called amd_hsmp.

View File

@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for drivers/platform/x86/amd
# AMD x86 Platform-Specific Drivers
#
amd-pmc-y := pmc.o
obj-$(CONFIG_AMD_PMC) += amd-pmc.o
amd_hsmp-y := hsmp.o
obj-$(CONFIG_AMD_HSMP) += amd_hsmp.o

View File

@ -291,10 +291,7 @@ static int gmux_get_brightness(struct backlight_device *bd)
static int gmux_update_status(struct backlight_device *bd)
{
struct apple_gmux_data *gmux_data = bl_get_data(bd);
u32 brightness = bd->props.brightness;
if (bd->props.state & BL_CORE_SUSPENDED)
return 0;
u32 brightness = backlight_get_brightness(bd);
gmux_write32(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);

View File

@ -208,6 +208,7 @@ struct asus_wmi {
int kbd_led_wk;
struct led_classdev lightbar_led;
int lightbar_led_wk;
struct led_classdev micmute_led;
struct workqueue_struct *led_workqueue;
struct work_struct tpd_led_work;
struct work_struct wlan_led_work;
@ -1028,12 +1029,23 @@ static enum led_brightness lightbar_led_get(struct led_classdev *led_cdev)
return result & ASUS_WMI_DSTS_LIGHTBAR_MASK;
}
static int micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
int state = brightness != LED_OFF;
int err;
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_MICMUTE_LED, state, NULL);
return err < 0 ? err : 0;
}
static void asus_wmi_led_exit(struct asus_wmi *asus)
{
led_classdev_unregister(&asus->kbd_led);
led_classdev_unregister(&asus->tpd_led);
led_classdev_unregister(&asus->wlan_led);
led_classdev_unregister(&asus->lightbar_led);
led_classdev_unregister(&asus->micmute_led);
if (asus->led_workqueue)
destroy_workqueue(asus->led_workqueue);
@ -1105,6 +1117,19 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
&asus->lightbar_led);
}
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MICMUTE_LED)) {
asus->micmute_led.name = "asus::micmute";
asus->micmute_led.max_brightness = 1;
asus->micmute_led.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
asus->micmute_led.brightness_set_blocking = micmute_led_set;
asus->micmute_led.default_trigger = "audio-micmute";
rv = led_classdev_register(&asus->platform_device->dev,
&asus->micmute_led);
if (rv)
goto error;
}
error:
if (rv)
asus_wmi_led_exit(asus);

View File

@ -324,9 +324,7 @@ static int bl_update_status(struct backlight_device *b)
if (ret)
return ret;
set_backlight_state((b->props.power == FB_BLANK_UNBLANK)
&& !(b->props.state & BL_CORE_SUSPENDED)
&& !(b->props.state & BL_CORE_FBBLANK));
set_backlight_state(!backlight_is_blank(b));
return 0;
}

View File

@ -5,7 +5,6 @@
menuconfig X86_PLATFORM_DRIVERS_DELL
bool "Dell X86 Platform Specific Device Drivers"
depends on X86_PLATFORM_DEVICES
help
Say Y here to get to see options for device drivers for various
Dell x86 platforms, including vendor-specific laptop extension drivers.

View File

@ -20,25 +20,16 @@
#define PMT_XA_MAX INT_MAX
#define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX)
/*
* Early implementations of PMT on client platforms have some
* differences from the server platforms (which use the Out Of Band
* Management Services Module OOBMSM). This list tracks those
* platforms as needed to handle those differences. Newer client
* platforms are expected to be fully compatible with server.
*/
static const struct pci_device_id pmt_telem_early_client_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x467d) }, /* ADL */
{ PCI_VDEVICE(INTEL, 0x490e) }, /* DG1 */
{ PCI_VDEVICE(INTEL, 0x9a0d) }, /* TGL */
{ }
};
bool intel_pmt_is_early_client_hw(struct device *dev)
{
struct pci_dev *parent = to_pci_dev(dev->parent);
struct intel_vsec_device *ivdev = dev_to_ivdev(dev);
return !!pci_match_id(pmt_telem_early_client_pci_ids, parent);
/*
* Early implementations of PMT on client platforms have some
* differences from the server platforms (which use the Out Of Band
* Management Services Module OOBMSM).
*/
return !!(ivdev->info->quirks & VSEC_QUIRK_EARLY_HW);
}
EXPORT_SYMBOL_GPL(intel_pmt_is_early_client_hw);

View File

@ -23,12 +23,19 @@
#define TELEM_GUID_OFFSET 0x4
#define TELEM_BASE_OFFSET 0x8
#define TELEM_ACCESS(v) ((v) & GENMASK(3, 0))
#define TELEM_TYPE(v) (((v) & GENMASK(7, 4)) >> 4)
/* size is in bytes */
#define TELEM_SIZE(v) (((v) & GENMASK(27, 12)) >> 10)
/* Used by client hardware to identify a fixed telemetry entry*/
#define TELEM_CLIENT_FIXED_BLOCK_GUID 0x10000000
enum telem_type {
TELEM_TYPE_PUNIT = 0,
TELEM_TYPE_CRASHLOG,
TELEM_TYPE_PUNIT_FIXED,
};
struct pmt_telem_priv {
int num_entries;
struct intel_pmt_entry entry[];
@ -39,10 +46,15 @@ static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry,
{
u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET);
if (guid != TELEM_CLIENT_FIXED_BLOCK_GUID)
return false;
if (intel_pmt_is_early_client_hw(dev)) {
u32 type = TELEM_TYPE(readl(entry->disc_table));
return intel_pmt_is_early_client_hw(dev);
if ((type == TELEM_TYPE_PUNIT_FIXED) ||
(guid == TELEM_CLIENT_FIXED_BLOCK_GUID))
return true;
}
return false;
}
static int pmt_telem_header_decode(struct intel_pmt_entry *entry,

View File

@ -277,29 +277,38 @@ static int isst_if_get_platform_info(void __user *argp)
return 0;
}
#define ISST_MAX_BUS_NUMBER 2
struct isst_if_cpu_info {
/* For BUS 0 and BUS 1 only, which we need for PUNIT interface */
int bus_info[2];
struct pci_dev *pci_dev[2];
int bus_info[ISST_MAX_BUS_NUMBER];
struct pci_dev *pci_dev[ISST_MAX_BUS_NUMBER];
int punit_cpu_id;
int numa_node;
};
struct isst_if_pkg_info {
struct pci_dev *pci_dev[ISST_MAX_BUS_NUMBER];
};
static struct isst_if_cpu_info *isst_cpu_info;
static struct isst_if_pkg_info *isst_pkg_info;
#define ISST_MAX_PCI_DOMAINS 8
static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
{
struct pci_dev *matched_pci_dev = NULL;
struct pci_dev *pci_dev = NULL;
int no_matches = 0;
int no_matches = 0, pkg_id;
int i, bus_number;
if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids ||
cpu >= num_possible_cpus())
if (bus_no < 0 || bus_no >= ISST_MAX_BUS_NUMBER || cpu < 0 ||
cpu >= nr_cpu_ids || cpu >= num_possible_cpus())
return NULL;
pkg_id = topology_physical_package_id(cpu);
bus_number = isst_cpu_info[cpu].bus_info[bus_no];
if (bus_number < 0)
return NULL;
@ -324,6 +333,8 @@ static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn
}
if (node == isst_cpu_info[cpu].numa_node) {
isst_pkg_info[pkg_id].pci_dev[bus_no] = _pci_dev;
pci_dev = _pci_dev;
break;
}
@ -342,6 +353,10 @@ static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn
if (!pci_dev && no_matches == 1)
pci_dev = matched_pci_dev;
/* Return pci_dev pointer for any matched CPU in the package */
if (!pci_dev)
pci_dev = isst_pkg_info[pkg_id].pci_dev[bus_no];
return pci_dev;
}
@ -361,8 +376,8 @@ struct pci_dev *isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
{
struct pci_dev *pci_dev;
if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids ||
cpu >= num_possible_cpus())
if (bus_no < 0 || bus_no >= ISST_MAX_BUS_NUMBER || cpu < 0 ||
cpu >= nr_cpu_ids || cpu >= num_possible_cpus())
return NULL;
pci_dev = isst_cpu_info[cpu].pci_dev[bus_no];
@ -417,10 +432,19 @@ static int isst_if_cpu_info_init(void)
if (!isst_cpu_info)
return -ENOMEM;
isst_pkg_info = kcalloc(topology_max_packages(),
sizeof(*isst_pkg_info),
GFP_KERNEL);
if (!isst_pkg_info) {
kfree(isst_cpu_info);
return -ENOMEM;
}
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
"platform/x86/isst-if:online",
isst_if_cpu_online, NULL);
if (ret < 0) {
kfree(isst_pkg_info);
kfree(isst_cpu_info);
return ret;
}
@ -433,6 +457,7 @@ static int isst_if_cpu_info_init(void)
static void isst_if_cpu_info_exit(void)
{
cpuhp_remove_state(isst_if_online_id);
kfree(isst_pkg_info);
kfree(isst_cpu_info);
};

View File

@ -15,6 +15,7 @@
#include <linux/auxiliary_bus.h>
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/idr.h>
#include <linux/module.h>
@ -30,9 +31,13 @@
#define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0))
#define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3))
#define TABLE_OFFSET_SHIFT 3
#define PMT_XA_START 0
#define PMT_XA_MAX INT_MAX
#define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX)
static DEFINE_IDA(intel_vsec_ida);
static DEFINE_IDA(intel_vsec_sdsi_ida);
static DEFINE_XARRAY_ALLOC(auxdev_array);
/**
* struct intel_vsec_header - Common fields of Intel VSEC and DVSEC registers.
@ -54,12 +59,6 @@ struct intel_vsec_header {
u32 offset;
};
/* Platform specific data */
struct intel_vsec_platform_info {
struct intel_vsec_header **capabilities;
unsigned long quirks;
};
enum intel_vsec_id {
VSEC_ID_TELEMETRY = 2,
VSEC_ID_WATCHER = 3,
@ -138,7 +137,7 @@ static int intel_vsec_add_aux(struct pci_dev *pdev, struct intel_vsec_device *in
const char *name)
{
struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev;
int ret;
int ret, id;
ret = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL);
if (ret < 0) {
@ -165,14 +164,26 @@ static int intel_vsec_add_aux(struct pci_dev *pdev, struct intel_vsec_device *in
return ret;
}
return devm_add_action_or_reset(&pdev->dev, intel_vsec_remove_aux, auxdev);
ret = devm_add_action_or_reset(&pdev->dev, intel_vsec_remove_aux,
auxdev);
if (ret < 0)
return ret;
/* Add auxdev to list */
ret = xa_alloc(&auxdev_array, &id, intel_vsec_dev, PMT_XA_LIMIT,
GFP_KERNEL);
if (ret)
return ret;
return 0;
}
static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header,
unsigned long quirks)
struct intel_vsec_platform_info *info)
{
struct intel_vsec_device *intel_vsec_dev;
struct resource *res, *tmp;
unsigned long quirks = info->quirks;
int i;
if (!intel_vsec_allowed(header->id) || intel_vsec_disabled(header->id, quirks))
@ -216,7 +227,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he
intel_vsec_dev->pcidev = pdev;
intel_vsec_dev->resource = res;
intel_vsec_dev->num_resources = header->num_entries;
intel_vsec_dev->quirks = quirks;
intel_vsec_dev->info = info;
if (header->id == VSEC_ID_SDSI)
intel_vsec_dev->ida = &intel_vsec_sdsi_ida;
@ -226,14 +237,15 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he
return intel_vsec_add_aux(pdev, intel_vsec_dev, intel_vsec_name(header->id));
}
static bool intel_vsec_walk_header(struct pci_dev *pdev, unsigned long quirks,
struct intel_vsec_header **header)
static bool intel_vsec_walk_header(struct pci_dev *pdev,
struct intel_vsec_platform_info *info)
{
struct intel_vsec_header **header = info->capabilities;
bool have_devices = false;
int ret;
for ( ; *header; header++) {
ret = intel_vsec_add_dev(pdev, *header, quirks);
ret = intel_vsec_add_dev(pdev, *header, info);
if (ret)
dev_info(&pdev->dev, "Could not add device for DVSEC id %d\n",
(*header)->id);
@ -244,7 +256,8 @@ static bool intel_vsec_walk_header(struct pci_dev *pdev, unsigned long quirks,
return have_devices;
}
static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, unsigned long quirks)
static bool intel_vsec_walk_dvsec(struct pci_dev *pdev,
struct intel_vsec_platform_info *info)
{
bool have_devices = false;
int pos = 0;
@ -283,7 +296,7 @@ static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, unsigned long quirks)
pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr);
header.id = PCI_DVSEC_HEADER2_ID(hdr);
ret = intel_vsec_add_dev(pdev, &header, quirks);
ret = intel_vsec_add_dev(pdev, &header, info);
if (ret)
continue;
@ -293,7 +306,8 @@ static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, unsigned long quirks)
return have_devices;
}
static bool intel_vsec_walk_vsec(struct pci_dev *pdev, unsigned long quirks)
static bool intel_vsec_walk_vsec(struct pci_dev *pdev,
struct intel_vsec_platform_info *info)
{
bool have_devices = false;
int pos = 0;
@ -327,7 +341,7 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev, unsigned long quirks)
header.tbir = INTEL_DVSEC_TABLE_BAR(table);
header.offset = INTEL_DVSEC_TABLE_OFFSET(table);
ret = intel_vsec_add_dev(pdev, &header, quirks);
ret = intel_vsec_add_dev(pdev, &header, info);
if (ret)
continue;
@ -341,25 +355,25 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id
{
struct intel_vsec_platform_info *info;
bool have_devices = false;
unsigned long quirks = 0;
int ret;
ret = pcim_enable_device(pdev);
if (ret)
return ret;
pci_save_state(pdev);
info = (struct intel_vsec_platform_info *)id->driver_data;
if (info)
quirks = info->quirks;
if (!info)
return -EINVAL;
if (intel_vsec_walk_dvsec(pdev, quirks))
if (intel_vsec_walk_dvsec(pdev, info))
have_devices = true;
if (intel_vsec_walk_vsec(pdev, quirks))
if (intel_vsec_walk_vsec(pdev, info))
have_devices = true;
if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) &&
intel_vsec_walk_header(pdev, quirks, info->capabilities))
intel_vsec_walk_header(pdev, info))
have_devices = true;
if (!have_devices)
@ -370,7 +384,8 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id
/* TGL info */
static const struct intel_vsec_platform_info tgl_info = {
.quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG | VSEC_QUIRK_TABLE_SHIFT,
.quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG |
VSEC_QUIRK_TABLE_SHIFT | VSEC_QUIRK_EARLY_HW,
};
/* DG1 info */
@ -390,26 +405,89 @@ static struct intel_vsec_header *dg1_capabilities[] = {
static const struct intel_vsec_platform_info dg1_info = {
.capabilities = dg1_capabilities,
.quirks = VSEC_QUIRK_NO_DVSEC,
.quirks = VSEC_QUIRK_NO_DVSEC | VSEC_QUIRK_EARLY_HW,
};
#define PCI_DEVICE_ID_INTEL_VSEC_ADL 0x467d
#define PCI_DEVICE_ID_INTEL_VSEC_DG1 0x490e
#define PCI_DEVICE_ID_INTEL_VSEC_OOBMSM 0x09a7
#define PCI_DEVICE_ID_INTEL_VSEC_RPL 0xa77d
#define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d
static const struct pci_device_id intel_vsec_pci_ids[] = {
{ PCI_DEVICE_DATA(INTEL, VSEC_ADL, &tgl_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, NULL) },
{ PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &(struct intel_vsec_platform_info) {}) },
{ PCI_DEVICE_DATA(INTEL, VSEC_RPL, &tgl_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) },
{ }
};
MODULE_DEVICE_TABLE(pci, intel_vsec_pci_ids);
static pci_ers_result_t intel_vsec_pci_error_detected(struct pci_dev *pdev,
pci_channel_state_t state)
{
pci_ers_result_t status = PCI_ERS_RESULT_NEED_RESET;
dev_info(&pdev->dev, "PCI error detected, state %d", state);
if (state == pci_channel_io_perm_failure)
status = PCI_ERS_RESULT_DISCONNECT;
else
pci_disable_device(pdev);
return status;
}
static pci_ers_result_t intel_vsec_pci_slot_reset(struct pci_dev *pdev)
{
struct intel_vsec_device *intel_vsec_dev;
pci_ers_result_t status = PCI_ERS_RESULT_DISCONNECT;
const struct pci_device_id *pci_dev_id;
unsigned long index;
dev_info(&pdev->dev, "Resetting PCI slot\n");
msleep(2000);
if (pci_enable_device(pdev)) {
dev_info(&pdev->dev,
"Failed to re-enable PCI device after reset.\n");
goto out;
}
status = PCI_ERS_RESULT_RECOVERED;
xa_for_each(&auxdev_array, index, intel_vsec_dev) {
/* check if pdev doesn't match */
if (pdev != intel_vsec_dev->pcidev)
continue;
devm_release_action(&pdev->dev, intel_vsec_remove_aux,
&intel_vsec_dev->auxdev);
}
pci_disable_device(pdev);
pci_restore_state(pdev);
pci_dev_id = pci_match_id(intel_vsec_pci_ids, pdev);
intel_vsec_pci_probe(pdev, pci_dev_id);
out:
return status;
}
static void intel_vsec_pci_resume(struct pci_dev *pdev)
{
dev_info(&pdev->dev, "Done resuming PCI device\n");
}
static const struct pci_error_handlers intel_vsec_pci_err_handlers = {
.error_detected = intel_vsec_pci_error_detected,
.slot_reset = intel_vsec_pci_slot_reset,
.resume = intel_vsec_pci_resume,
};
static struct pci_driver intel_vsec_pci_driver = {
.name = "intel_vsec",
.id_table = intel_vsec_pci_ids,
.probe = intel_vsec_pci_probe,
.err_handler = &intel_vsec_pci_err_handlers,
};
module_pci_driver(intel_vsec_pci_driver);

View File

@ -20,6 +20,15 @@ enum intel_vsec_quirks {
/* DVSEC not present (provided in driver data) */
VSEC_QUIRK_NO_DVSEC = BIT(3),
/* Platforms requiring quirk in the auxiliary driver */
VSEC_QUIRK_EARLY_HW = BIT(4),
};
/* Platform specific data */
struct intel_vsec_platform_info {
struct intel_vsec_header **capabilities;
unsigned long quirks;
};
struct intel_vsec_device {
@ -27,7 +36,7 @@ struct intel_vsec_device {
struct pci_dev *pcidev;
struct resource *resource;
struct ida *ida;
unsigned long quirks;
struct intel_vsec_platform_info *info;
int num_resources;
};

View File

@ -34,6 +34,7 @@
#define MLXPLAT_CPLD_LPC_REG_CPLD3_PN1_OFFSET 0x09
#define MLXPLAT_CPLD_LPC_REG_CPLD4_PN_OFFSET 0x0a
#define MLXPLAT_CPLD_LPC_REG_CPLD4_PN1_OFFSET 0x0b
#define MLXPLAT_CPLD_LPC_REG_RESET_GP2_OFFSET 0x19
#define MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET 0x1c
#define MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET 0x1d
#define MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET 0x1e
@ -66,9 +67,15 @@
#define MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET 0x43
#define MLXPLAT_CPLD_LPC_REG_AGGRCX_OFFSET 0x44
#define MLXPLAT_CPLD_LPC_REG_AGGRCX_MASK_OFFSET 0x45
#define MLXPLAT_CPLD_LPC_REG_GWP_OFFSET 0x4a
#define MLXPLAT_CPLD_LPC_REG_GWP_EVENT_OFFSET 0x4b
#define MLXPLAT_CPLD_LPC_REG_GWP_MASK_OFFSET 0x4c
#define MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET 0x50
#define MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET 0x51
#define MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET 0x52
#define MLXPLAT_CPLD_LPC_REG_ASIC2_HEALTH_OFFSET 0x53
#define MLXPLAT_CPLD_LPC_REG_ASIC2_EVENT_OFFSET 0x54
#define MLXPLAT_CPLD_LPC_REG_ASIC2_MASK_OFFSET 0x55
#define MLXPLAT_CPLD_LPC_REG_AGGRLC_OFFSET 0x56
#define MLXPLAT_CPLD_LPC_REG_AGGRLC_MASK_OFFSET 0x57
#define MLXPLAT_CPLD_LPC_REG_PSU_OFFSET 0x58
@ -143,6 +150,7 @@
#define MLXPLAT_CPLD_LPC_REG_SLOT_QTY_OFFSET 0xfa
#define MLXPLAT_CPLD_LPC_REG_CONFIG1_OFFSET 0xfb
#define MLXPLAT_CPLD_LPC_REG_CONFIG2_OFFSET 0xfc
#define MLXPLAT_CPLD_LPC_REG_CONFIG3_OFFSET 0xfd
#define MLXPLAT_CPLD_LPC_IO_RANGE 0x100
#define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb
#define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda
@ -193,6 +201,7 @@
MLXPLAT_CPLD_AGGR_MASK_LC_ACT | \
MLXPLAT_CPLD_AGGR_MASK_LC_SDWN)
#define MLXPLAT_CPLD_LOW_AGGR_MASK_LOW 0xc1
#define MLXPLAT_CPLD_LOW_AGGR_MASK_ASIC2 BIT(2)
#define MLXPLAT_CPLD_LOW_AGGR_MASK_I2C BIT(6)
#define MLXPLAT_CPLD_PSU_MASK GENMASK(1, 0)
#define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0)
@ -204,6 +213,7 @@
#define MLXPLAT_CPLD_LED_LO_NIBBLE_MASK GENMASK(7, 4)
#define MLXPLAT_CPLD_LED_HI_NIBBLE_MASK GENMASK(3, 0)
#define MLXPLAT_CPLD_VOLTREG_UPD_MASK GENMASK(5, 4)
#define MLXPLAT_CPLD_GWP_MASK GENMASK(0, 0)
#define MLXPLAT_CPLD_I2C_CAP_BIT 0x04
#define MLXPLAT_CPLD_I2C_CAP_MASK GENMASK(5, MLXPLAT_CPLD_I2C_CAP_BIT)
@ -588,6 +598,15 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_asic_items_data[] = {
},
};
static struct mlxreg_core_data mlxplat_mlxcpld_default_asic2_items_data[] = {
{
.label = "asic2",
.reg = MLXPLAT_CPLD_LPC_REG_ASIC2_HEALTH_OFFSET,
.mask = MLXPLAT_CPLD_ASIC_MASK,
.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
},
};
static struct mlxreg_core_item mlxplat_mlxcpld_default_items[] = {
{
.data = mlxplat_mlxcpld_default_psu_items_data,
@ -1151,6 +1170,15 @@ static struct mlxreg_core_item mlxplat_mlxcpld_ext_items[] = {
.inversed = 0,
.health = true,
},
{
.data = mlxplat_mlxcpld_default_asic2_items_data,
.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
.reg = MLXPLAT_CPLD_LPC_REG_ASIC2_HEALTH_OFFSET,
.mask = MLXPLAT_CPLD_ASIC_MASK,
.count = ARRAY_SIZE(mlxplat_mlxcpld_default_asic2_items_data),
.inversed = 0,
.health = true,
}
};
static
@ -1160,7 +1188,7 @@ struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_ext_data = {
.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
.mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF | MLXPLAT_CPLD_AGGR_MASK_COMEX,
.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW | MLXPLAT_CPLD_LOW_AGGR_MASK_ASIC2,
};
static struct mlxreg_core_data mlxplat_mlxcpld_modular_pwr_items_data[] = {
@ -2004,6 +2032,38 @@ struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_modular_data = {
.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
};
/* Platform hotplug for NVLink blade systems family data */
static struct mlxreg_core_data mlxplat_mlxcpld_global_wp_items_data[] = {
{
.label = "global_wp_grant",
.reg = MLXPLAT_CPLD_LPC_REG_GWP_OFFSET,
.mask = MLXPLAT_CPLD_GWP_MASK,
.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
},
};
static struct mlxreg_core_item mlxplat_mlxcpld_nvlink_blade_items[] = {
{
.data = mlxplat_mlxcpld_global_wp_items_data,
.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
.reg = MLXPLAT_CPLD_LPC_REG_GWP_OFFSET,
.mask = MLXPLAT_CPLD_GWP_MASK,
.count = ARRAY_SIZE(mlxplat_mlxcpld_global_wp_items_data),
.inversed = 0,
.health = false,
},
};
static
struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_nvlink_blade_data = {
.items = mlxplat_mlxcpld_nvlink_blade_items,
.counter = ARRAY_SIZE(mlxplat_mlxcpld_nvlink_blade_items),
.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
.mask = MLXPLAT_CPLD_AGGR_MASK_COMEX,
.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
};
/* Platform led default data */
static struct mlxreg_core_data mlxplat_mlxcpld_default_led_data[] = {
{
@ -2102,6 +2162,25 @@ static struct mlxreg_core_platform_data mlxplat_default_led_wc_data = {
.counter = ARRAY_SIZE(mlxplat_mlxcpld_default_led_wc_data),
};
/* Platform led default data for water cooling Ethernet switch blade */
static struct mlxreg_core_data mlxplat_mlxcpld_default_led_eth_wc_blade_data[] = {
{
.label = "status:green",
.reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
.mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
},
{
.label = "status:red",
.reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
.mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK
},
};
static struct mlxreg_core_platform_data mlxplat_default_led_eth_wc_blade_data = {
.data = mlxplat_mlxcpld_default_led_eth_wc_blade_data,
.counter = ARRAY_SIZE(mlxplat_mlxcpld_default_led_eth_wc_blade_data),
};
/* Platform led MSN21xx system family data */
static struct mlxreg_core_data mlxplat_mlxcpld_msn21xx_led_data[] = {
{
@ -2856,6 +2935,18 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = {
.bit = GENMASK(7, 0),
.mode = 0444,
},
{
.label = "asic_reset",
.reg = MLXPLAT_CPLD_LPC_REG_RESET_GP2_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(3),
.mode = 0200,
},
{
.label = "asic2_reset",
.reg = MLXPLAT_CPLD_LPC_REG_RESET_GP2_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(2),
.mode = 0200,
},
{
.label = "reset_long_pb",
.reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
@ -2995,6 +3086,13 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = {
.bit = 1,
.mode = 0444,
},
{
.label = "asic2_health",
.reg = MLXPLAT_CPLD_LPC_REG_ASIC2_HEALTH_OFFSET,
.mask = MLXPLAT_CPLD_ASIC_MASK,
.bit = 1,
.mode = 0444,
},
{
.label = "fan_dir",
.reg = MLXPLAT_CPLD_LPC_REG_FAN_DIRECTION,
@ -3056,6 +3154,12 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = {
.bit = GENMASK(7, 0),
.mode = 0444,
},
{
.label = "config3",
.reg = MLXPLAT_CPLD_LPC_REG_CONFIG3_OFFSET,
.bit = GENMASK(7, 0),
.mode = 0444,
},
{
.label = "ufm_version",
.reg = MLXPLAT_CPLD_LPC_REG_UFM_VERSION_OFFSET,
@ -3534,6 +3638,12 @@ static struct mlxreg_core_data mlxplat_mlxcpld_modular_regs_io_data[] = {
.bit = GENMASK(7, 0),
.mode = 0444,
},
{
.label = "config3",
.reg = MLXPLAT_CPLD_LPC_REG_CONFIG3_OFFSET,
.bit = GENMASK(7, 0),
.mode = 0444,
},
{
.label = "ufm_version",
.reg = MLXPLAT_CPLD_LPC_REG_UFM_VERSION_OFFSET,
@ -3547,6 +3657,209 @@ static struct mlxreg_core_platform_data mlxplat_modular_regs_io_data = {
.counter = ARRAY_SIZE(mlxplat_mlxcpld_modular_regs_io_data),
};
/* Platform register access for NVLink blade systems family data */
static struct mlxreg_core_data mlxplat_mlxcpld_nvlink_blade_regs_io_data[] = {
{
.label = "cpld1_version",
.reg = MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFFSET,
.bit = GENMASK(7, 0),
.mode = 0444,
},
{
.label = "cpld1_pn",
.reg = MLXPLAT_CPLD_LPC_REG_CPLD1_PN_OFFSET,
.bit = GENMASK(15, 0),
.mode = 0444,
.regnum = 2,
},
{
.label = "cpld1_version_min",
.reg = MLXPLAT_CPLD_LPC_REG_CPLD1_MVER_OFFSET,
.bit = GENMASK(7, 0),
.mode = 0444,
},
{
.label = "reset_aux_pwr_or_ref",
.reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(2),
.mode = 0444,
},
{
.label = "reset_from_comex",
.reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(4),
.mode = 0444,
},
{
.label = "reset_comex_pwr_fail",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(3),
.mode = 0444,
},
{
.label = "reset_platform",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(4),
.mode = 0444,
},
{
.label = "reset_soc",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(5),
.mode = 0444,
},
{
.label = "reset_comex_wd",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(6),
.mode = 0444,
},
{
.label = "reset_voltmon_upgrade_fail",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(0),
.mode = 0444,
},
{
.label = "reset_system",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(1),
.mode = 0444,
},
{
.label = "reset_sw_pwr_off",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(2),
.mode = 0444,
},
{
.label = "reset_comex_thermal",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(3),
.mode = 0444,
},
{
.label = "reset_reload_bios",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(5),
.mode = 0444,
},
{
.label = "reset_ac_pwr_fail",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(6),
.mode = 0444,
},
{
.label = "pwr_cycle",
.reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(2),
.mode = 0200,
},
{
.label = "pwr_down",
.reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(3),
.mode = 0200,
},
{
.label = "global_wp_request",
.reg = MLXPLAT_CPLD_LPC_REG_GP2_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(0),
.mode = 0644,
},
{
.label = "jtag_enable",
.reg = MLXPLAT_CPLD_LPC_REG_GP2_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(4),
.mode = 0644,
},
{
.label = "comm_chnl_ready",
.reg = MLXPLAT_CPLD_LPC_REG_GP2_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(6),
.mode = 0200,
},
{
.label = "bios_safe_mode",
.reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(4),
.mode = 0444,
},
{
.label = "bios_active_image",
.reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(5),
.mode = 0444,
},
{
.label = "bios_auth_fail",
.reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(6),
.mode = 0444,
},
{
.label = "bios_upgrade_fail",
.reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(7),
.mode = 0444,
},
{
.label = "voltreg_update_status",
.reg = MLXPLAT_CPLD_LPC_REG_GP0_RO_OFFSET,
.mask = MLXPLAT_CPLD_VOLTREG_UPD_MASK,
.bit = 5,
.mode = 0444,
},
{
.label = "vpd_wp",
.reg = MLXPLAT_CPLD_LPC_REG_GP0_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(3),
.mode = 0644,
},
{
.label = "pcie_asic_reset_dis",
.reg = MLXPLAT_CPLD_LPC_REG_GP0_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(4),
.mode = 0644,
},
{
.label = "global_wp_response",
.reg = MLXPLAT_CPLD_LPC_REG_GWP_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(0),
.mode = 0444,
},
{
.label = "config1",
.reg = MLXPLAT_CPLD_LPC_REG_CONFIG1_OFFSET,
.bit = GENMASK(7, 0),
.mode = 0444,
},
{
.label = "config2",
.reg = MLXPLAT_CPLD_LPC_REG_CONFIG2_OFFSET,
.bit = GENMASK(7, 0),
.mode = 0444,
},
{
.label = "config3",
.reg = MLXPLAT_CPLD_LPC_REG_CONFIG3_OFFSET,
.bit = GENMASK(7, 0),
.mode = 0444,
},
{
.label = "ufm_version",
.reg = MLXPLAT_CPLD_LPC_REG_UFM_VERSION_OFFSET,
.bit = GENMASK(7, 0),
.mode = 0444,
},
};
static struct mlxreg_core_platform_data mlxplat_nvlink_blade_regs_io_data = {
.data = mlxplat_mlxcpld_nvlink_blade_regs_io_data,
.counter = ARRAY_SIZE(mlxplat_mlxcpld_nvlink_blade_regs_io_data),
};
/* Platform FAN default */
static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_data[] = {
{
@ -3932,8 +4245,12 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRCX_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_GWP_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_GWP_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC2_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC2_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
@ -4023,9 +4340,15 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRCX_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRCX_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_GWP_OFFSET:
case MLXPLAT_CPLD_LPC_REG_GWP_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_GWP_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC2_HEALTH_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC2_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC2_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PSU_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
@ -4100,6 +4423,7 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_SLOT_QTY_OFFSET:
case MLXPLAT_CPLD_LPC_REG_CONFIG1_OFFSET:
case MLXPLAT_CPLD_LPC_REG_CONFIG2_OFFSET:
case MLXPLAT_CPLD_LPC_REG_CONFIG3_OFFSET:
case MLXPLAT_CPLD_LPC_REG_UFM_VERSION_OFFSET:
return true;
}
@ -4150,9 +4474,15 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRCX_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRCX_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_GWP_OFFSET:
case MLXPLAT_CPLD_LPC_REG_GWP_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_GWP_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC2_HEALTH_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC2_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC2_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PSU_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
@ -4221,6 +4551,7 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_SLOT_QTY_OFFSET:
case MLXPLAT_CPLD_LPC_REG_CONFIG1_OFFSET:
case MLXPLAT_CPLD_LPC_REG_CONFIG2_OFFSET:
case MLXPLAT_CPLD_LPC_REG_CONFIG3_OFFSET:
case MLXPLAT_CPLD_LPC_REG_UFM_VERSION_OFFSET:
return true;
}
@ -4417,6 +4748,31 @@ static int __init mlxplat_dmi_default_wc_matched(const struct dmi_system_id *dmi
return 1;
}
static int __init mlxplat_dmi_default_eth_wc_blade_matched(const struct dmi_system_id *dmi)
{
int i;
mlxplat_max_adap_num = MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM;
mlxplat_mux_num = ARRAY_SIZE(mlxplat_default_mux_data);
mlxplat_mux_data = mlxplat_default_mux_data;
for (i = 0; i < mlxplat_mux_num; i++) {
mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
mlxplat_mux_data[i].n_values =
ARRAY_SIZE(mlxplat_msn21xx_channels);
}
mlxplat_hotplug = &mlxplat_mlxcpld_default_wc_data;
mlxplat_hotplug->deferred_nr =
mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
mlxplat_led = &mlxplat_default_led_eth_wc_blade_data;
mlxplat_regs_io = &mlxplat_default_ng_regs_io_data;
for (i = 0; i < ARRAY_SIZE(mlxplat_mlxcpld_wd_set_type2); i++)
mlxplat_wd_data[i] = &mlxplat_mlxcpld_wd_set_type2[i];
mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data;
mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_ng;
return 1;
}
static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
{
int i;
@ -4579,6 +4935,28 @@ static int __init mlxplat_dmi_modular_matched(const struct dmi_system_id *dmi)
return 1;
}
static int __init mlxplat_dmi_nvlink_blade_matched(const struct dmi_system_id *dmi)
{
int i;
mlxplat_max_adap_num = MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM;
mlxplat_mux_num = ARRAY_SIZE(mlxplat_default_mux_data);
mlxplat_mux_data = mlxplat_default_mux_data;
mlxplat_hotplug = &mlxplat_mlxcpld_nvlink_blade_data;
mlxplat_hotplug->deferred_nr =
mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
for (i = 0; i < mlxplat_mux_num; i++) {
mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
mlxplat_mux_data[i].n_values =
ARRAY_SIZE(mlxplat_msn21xx_channels);
}
mlxplat_regs_io = &mlxplat_nvlink_blade_regs_io_data;
mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data;
mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_ng400;
return 1;
}
static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
{
.callback = mlxplat_dmi_default_wc_matched,
@ -4611,6 +4989,13 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0004"),
},
},
{
.callback = mlxplat_dmi_default_eth_wc_blade_matched,
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0005"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI139"),
},
},
{
.callback = mlxplat_dmi_qmb7xx_matched,
.matches = {
@ -4641,6 +5026,12 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0011"),
},
},
{
.callback = mlxplat_dmi_nvlink_blade_matched,
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0015"),
},
},
{
.callback = mlxplat_dmi_msn274x_matched,
.matches = {
@ -4830,22 +5221,20 @@ static int __init mlxplat_init(void)
nr = (nr == mlxplat_max_adap_num) ? -1 : nr;
if (mlxplat_i2c)
mlxplat_i2c->regmap = priv->regmap;
priv->pdev_i2c = platform_device_register_resndata(
&mlxplat_dev->dev, "i2c_mlxcpld",
nr, mlxplat_mlxcpld_resources,
ARRAY_SIZE(mlxplat_mlxcpld_resources),
mlxplat_i2c, sizeof(*mlxplat_i2c));
priv->pdev_i2c = platform_device_register_resndata(&mlxplat_dev->dev, "i2c_mlxcpld",
nr, mlxplat_mlxcpld_resources,
ARRAY_SIZE(mlxplat_mlxcpld_resources),
mlxplat_i2c, sizeof(*mlxplat_i2c));
if (IS_ERR(priv->pdev_i2c)) {
err = PTR_ERR(priv->pdev_i2c);
goto fail_alloc;
}
for (i = 0; i < mlxplat_mux_num; i++) {
priv->pdev_mux[i] = platform_device_register_resndata(
&priv->pdev_i2c->dev,
"i2c-mux-reg", i, NULL,
0, &mlxplat_mux_data[i],
sizeof(mlxplat_mux_data[i]));
priv->pdev_mux[i] = platform_device_register_resndata(&priv->pdev_i2c->dev,
"i2c-mux-reg", i, NULL, 0,
&mlxplat_mux_data[i],
sizeof(mlxplat_mux_data[i]));
if (IS_ERR(priv->pdev_mux[i])) {
err = PTR_ERR(priv->pdev_mux[i]);
goto fail_platform_mux_register;
@ -4853,16 +5242,18 @@ static int __init mlxplat_init(void)
}
/* Add hotplug driver */
mlxplat_hotplug->regmap = priv->regmap;
priv->pdev_hotplug = platform_device_register_resndata(
&mlxplat_dev->dev, "mlxreg-hotplug",
PLATFORM_DEVID_NONE,
mlxplat_mlxcpld_resources,
ARRAY_SIZE(mlxplat_mlxcpld_resources),
mlxplat_hotplug, sizeof(*mlxplat_hotplug));
if (IS_ERR(priv->pdev_hotplug)) {
err = PTR_ERR(priv->pdev_hotplug);
goto fail_platform_mux_register;
if (mlxplat_hotplug) {
mlxplat_hotplug->regmap = priv->regmap;
priv->pdev_hotplug =
platform_device_register_resndata(&mlxplat_dev->dev,
"mlxreg-hotplug", PLATFORM_DEVID_NONE,
mlxplat_mlxcpld_resources,
ARRAY_SIZE(mlxplat_mlxcpld_resources),
mlxplat_hotplug, sizeof(*mlxplat_hotplug));
if (IS_ERR(priv->pdev_hotplug)) {
err = PTR_ERR(priv->pdev_hotplug);
goto fail_platform_mux_register;
}
}
/* Set default registers. */
@ -4875,24 +5266,26 @@ static int __init mlxplat_init(void)
}
/* Add LED driver. */
mlxplat_led->regmap = priv->regmap;
priv->pdev_led = platform_device_register_resndata(
&mlxplat_dev->dev, "leds-mlxreg",
PLATFORM_DEVID_NONE, NULL, 0,
mlxplat_led, sizeof(*mlxplat_led));
if (IS_ERR(priv->pdev_led)) {
err = PTR_ERR(priv->pdev_led);
goto fail_platform_hotplug_register;
if (mlxplat_led) {
mlxplat_led->regmap = priv->regmap;
priv->pdev_led =
platform_device_register_resndata(&mlxplat_dev->dev, "leds-mlxreg",
PLATFORM_DEVID_NONE, NULL, 0, mlxplat_led,
sizeof(*mlxplat_led));
if (IS_ERR(priv->pdev_led)) {
err = PTR_ERR(priv->pdev_led);
goto fail_platform_hotplug_register;
}
}
/* Add registers io access driver. */
if (mlxplat_regs_io) {
mlxplat_regs_io->regmap = priv->regmap;
priv->pdev_io_regs = platform_device_register_resndata(
&mlxplat_dev->dev, "mlxreg-io",
PLATFORM_DEVID_NONE, NULL, 0,
mlxplat_regs_io,
sizeof(*mlxplat_regs_io));
priv->pdev_io_regs = platform_device_register_resndata(&mlxplat_dev->dev,
"mlxreg-io",
PLATFORM_DEVID_NONE, NULL,
0, mlxplat_regs_io,
sizeof(*mlxplat_regs_io));
if (IS_ERR(priv->pdev_io_regs)) {
err = PTR_ERR(priv->pdev_io_regs);
goto fail_platform_led_register;
@ -4902,11 +5295,10 @@ static int __init mlxplat_init(void)
/* Add FAN driver. */
if (mlxplat_fan) {
mlxplat_fan->regmap = priv->regmap;
priv->pdev_fan = platform_device_register_resndata(
&mlxplat_dev->dev, "mlxreg-fan",
PLATFORM_DEVID_NONE, NULL, 0,
mlxplat_fan,
sizeof(*mlxplat_fan));
priv->pdev_fan = platform_device_register_resndata(&mlxplat_dev->dev, "mlxreg-fan",
PLATFORM_DEVID_NONE, NULL, 0,
mlxplat_fan,
sizeof(*mlxplat_fan));
if (IS_ERR(priv->pdev_fan)) {
err = PTR_ERR(priv->pdev_fan);
goto fail_platform_io_regs_register;
@ -4920,11 +5312,10 @@ static int __init mlxplat_init(void)
for (j = 0; j < MLXPLAT_CPLD_WD_MAX_DEVS; j++) {
if (mlxplat_wd_data[j]) {
mlxplat_wd_data[j]->regmap = priv->regmap;
priv->pdev_wd[j] = platform_device_register_resndata(
&mlxplat_dev->dev, "mlx-wdt",
j, NULL, 0,
mlxplat_wd_data[j],
sizeof(*mlxplat_wd_data[j]));
priv->pdev_wd[j] =
platform_device_register_resndata(&mlxplat_dev->dev, "mlx-wdt", j,
NULL, 0, mlxplat_wd_data[j],
sizeof(*mlxplat_wd_data[j]));
if (IS_ERR(priv->pdev_wd[j])) {
err = PTR_ERR(priv->pdev_wd[j]);
goto fail_platform_wd_register;
@ -4949,9 +5340,11 @@ fail_platform_io_regs_register:
if (mlxplat_regs_io)
platform_device_unregister(priv->pdev_io_regs);
fail_platform_led_register:
platform_device_unregister(priv->pdev_led);
if (mlxplat_led)
platform_device_unregister(priv->pdev_led);
fail_platform_hotplug_register:
platform_device_unregister(priv->pdev_hotplug);
if (mlxplat_hotplug)
platform_device_unregister(priv->pdev_hotplug);
fail_platform_mux_register:
while (--i >= 0)
platform_device_unregister(priv->pdev_mux[i]);
@ -4974,8 +5367,10 @@ static void __exit mlxplat_exit(void)
platform_device_unregister(priv->pdev_fan);
if (priv->pdev_io_regs)
platform_device_unregister(priv->pdev_io_regs);
platform_device_unregister(priv->pdev_led);
platform_device_unregister(priv->pdev_hotplug);
if (priv->pdev_led)
platform_device_unregister(priv->pdev_led);
if (priv->pdev_hotplug)
platform_device_unregister(priv->pdev_hotplug);
for (i = mlxplat_mux_num - 1; i >= 0 ; i--)
platform_device_unregister(priv->pdev_mux[i]);

133
drivers/platform/x86/p2sb.c Normal file
View File

@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Primary to Sideband (P2SB) bridge access support
*
* Copyright (c) 2017, 2021-2022 Intel Corporation.
*
* Authors: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
* Jonathan Yong <jonathan.yong@intel.com>
*/
#include <linux/bits.h>
#include <linux/export.h>
#include <linux/pci.h>
#include <linux/platform_data/x86/p2sb.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#define P2SBC 0xe0
#define P2SBC_HIDE BIT(8)
static const struct x86_cpu_id p2sb_cpu_ids[] = {
X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT, PCI_DEVFN(13, 0)),
X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_D, PCI_DEVFN(31, 1)),
X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT_D, PCI_DEVFN(31, 1)),
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, PCI_DEVFN(31, 1)),
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, PCI_DEVFN(31, 1)),
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, PCI_DEVFN(31, 1)),
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, PCI_DEVFN(31, 1)),
{}
};
static int p2sb_get_devfn(unsigned int *devfn)
{
const struct x86_cpu_id *id;
id = x86_match_cpu(p2sb_cpu_ids);
if (!id)
return -ENODEV;
*devfn = (unsigned int)id->driver_data;
return 0;
}
static int p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem)
{
/* Copy resource from the first BAR of the device in question */
*mem = pdev->resource[0];
return 0;
}
static int p2sb_scan_and_read(struct pci_bus *bus, unsigned int devfn, struct resource *mem)
{
struct pci_dev *pdev;
int ret;
pdev = pci_scan_single_device(bus, devfn);
if (!pdev)
return -ENODEV;
ret = p2sb_read_bar0(pdev, mem);
pci_stop_and_remove_bus_device(pdev);
return ret;
}
/**
* p2sb_bar - Get Primary to Sideband (P2SB) bridge device BAR
* @bus: PCI bus to communicate with
* @devfn: PCI slot and function to communicate with
* @mem: memory resource to be filled in
*
* The BIOS prevents the P2SB device from being enumerated by the PCI
* subsystem, so we need to unhide and hide it back to lookup the BAR.
*
* if @bus is NULL, the bus 0 in domain 0 will be used.
* If @devfn is 0, it will be replaced by devfn of the P2SB device.
*
* Caller must provide a valid pointer to @mem.
*
* Locking is handled by pci_rescan_remove_lock mutex.
*
* Return:
* 0 on success or appropriate errno value on error.
*/
int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem)
{
struct pci_dev *pdev_p2sb;
unsigned int devfn_p2sb;
u32 value = P2SBC_HIDE;
int ret;
/* Get devfn for P2SB device itself */
ret = p2sb_get_devfn(&devfn_p2sb);
if (ret)
return ret;
/* if @bus is NULL, use bus 0 in domain 0 */
bus = bus ?: pci_find_bus(0, 0);
/*
* Prevent concurrent PCI bus scan from seeing the P2SB device and
* removing via sysfs while it is temporarily exposed.
*/
pci_lock_rescan_remove();
/* Unhide the P2SB device, if needed */
pci_bus_read_config_dword(bus, devfn_p2sb, P2SBC, &value);
if (value & P2SBC_HIDE)
pci_bus_write_config_dword(bus, devfn_p2sb, P2SBC, 0);
pdev_p2sb = pci_scan_single_device(bus, devfn_p2sb);
if (devfn)
ret = p2sb_scan_and_read(bus, devfn, mem);
else
ret = p2sb_read_bar0(pdev_p2sb, mem);
pci_stop_and_remove_bus_device(pdev_p2sb);
/* Hide the P2SB device, if it was hidden */
if (value & P2SBC_HIDE)
pci_bus_write_config_dword(bus, devfn_p2sb, P2SBC, P2SBC_HIDE);
pci_unlock_rescan_remove();
if (ret)
return ret;
if (mem->flags == 0)
return -ENODEV;
return 0;
}
EXPORT_SYMBOL_GPL(p2sb_bar);

View File

@ -998,19 +998,23 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
pr_err("Couldn't retrieve BIOS data\n");
goto out_input;
}
/* initialize backlight */
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = pcc->sinf[SINF_AC_MAX_BRIGHT];
pcc->backlight = backlight_device_register("panasonic", NULL, pcc,
&pcc_backlight_ops, &props);
if (IS_ERR(pcc->backlight)) {
result = PTR_ERR(pcc->backlight);
goto out_input;
}
/* read the initial brightness setting from the hardware */
pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
/* initialize backlight */
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = pcc->sinf[SINF_AC_MAX_BRIGHT];
pcc->backlight = backlight_device_register("panasonic", NULL, pcc,
&pcc_backlight_ops, &props);
if (IS_ERR(pcc->backlight)) {
result = PTR_ERR(pcc->backlight);
goto out_input;
}
/* read the initial brightness setting from the hardware */
pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
}
/* Reset initial sticky key mode since the hardware register state is not consistent */
acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, 0);

View File

@ -389,21 +389,16 @@ static const struct dmi_system_id critclk_systems[] = {
},
},
{
/* pmc_plt_clk0 - 3 are used for the 4 ethernet controllers */
.ident = "Lex 3I380D",
/*
* Lex System / Lex Computech Co. makes a lot of Bay Trail
* based embedded boards which often come with multiple
* ethernet controllers using multiple pmc_plt_clks. See:
* https://www.lex.com.tw/products/embedded-ipc-board/
*/
.ident = "Lex BayTrail",
.callback = dmi_callback,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Lex BayTrail"),
DMI_MATCH(DMI_PRODUCT_NAME, "3I380D"),
},
},
{
/* pmc_plt_clk* - are used for ethernet controllers */
.ident = "Lex 2I385SW",
.callback = dmi_callback,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Lex BayTrail"),
DMI_MATCH(DMI_PRODUCT_NAME, "2I385SW"),
},
},
{

View File

@ -61,36 +61,35 @@ static int smi_get_irq(struct platform_device *pdev, struct acpi_device *adev,
default:
return 0;
}
if (ret < 0)
dev_err_probe(&pdev->dev, ret, "Error requesting irq at index %d: %d\n",
inst->irq_idx, ret);
return dev_err_probe(&pdev->dev, ret, "Error requesting irq at index %d\n",
inst->irq_idx);
return ret;
}
static void smi_devs_unregister(struct smi *smi)
{
while (smi->i2c_num > 0)
i2c_unregister_device(smi->i2c_devs[--smi->i2c_num]);
while (smi->i2c_num--)
i2c_unregister_device(smi->i2c_devs[smi->i2c_num]);
while (smi->spi_num > 0)
spi_unregister_device(smi->spi_devs[--smi->spi_num]);
while (smi->spi_num--)
spi_unregister_device(smi->spi_devs[smi->spi_num]);
}
/**
* smi_spi_probe - Instantiate multiple SPI devices from inst array
* @pdev: Platform device
* @adev: ACPI device
* @smi: Internal struct for Serial multi instantiate driver
* @inst_array: Array of instances to probe
*
* Returns the number of SPI devices instantiate, Zero if none is found or a negative error code.
*/
static int smi_spi_probe(struct platform_device *pdev, struct acpi_device *adev, struct smi *smi,
static int smi_spi_probe(struct platform_device *pdev, struct smi *smi,
const struct smi_instance *inst_array)
{
struct device *dev = &pdev->dev;
struct acpi_device *adev = ACPI_COMPANION(dev);
struct spi_controller *ctlr;
struct spi_device *spi_dev;
char name[50];
@ -99,8 +98,8 @@ static int smi_spi_probe(struct platform_device *pdev, struct acpi_device *adev,
ret = acpi_spi_count_resources(adev);
if (ret < 0)
return ret;
else if (!ret)
return -ENODEV;
if (!ret)
return -ENOENT;
count = ret;
@ -112,9 +111,8 @@ static int smi_spi_probe(struct platform_device *pdev, struct acpi_device *adev,
spi_dev = acpi_spi_device_alloc(NULL, adev, i);
if (IS_ERR(spi_dev)) {
ret = PTR_ERR(spi_dev);
dev_err_probe(dev, ret, "failed to allocate SPI device %s from ACPI: %d\n",
dev_name(&adev->dev), ret);
ret = dev_err_probe(dev, PTR_ERR(spi_dev), "failed to allocate SPI device %s from ACPI\n",
dev_name(&adev->dev));
goto error;
}
@ -135,9 +133,8 @@ static int smi_spi_probe(struct platform_device *pdev, struct acpi_device *adev,
ret = spi_add_device(spi_dev);
if (ret) {
dev_err_probe(&ctlr->dev, ret,
"failed to add SPI device %s from ACPI: %d\n",
dev_name(&adev->dev), ret);
dev_err_probe(&ctlr->dev, ret, "failed to add SPI device %s from ACPI\n",
dev_name(&adev->dev));
spi_dev_put(spi_dev);
goto error;
}
@ -166,25 +163,25 @@ error:
/**
* smi_i2c_probe - Instantiate multiple I2C devices from inst array
* @pdev: Platform device
* @adev: ACPI device
* @smi: Internal struct for Serial multi instantiate driver
* @inst_array: Array of instances to probe
*
* Returns the number of I2C devices instantiate, Zero if none is found or a negative error code.
*/
static int smi_i2c_probe(struct platform_device *pdev, struct acpi_device *adev, struct smi *smi,
static int smi_i2c_probe(struct platform_device *pdev, struct smi *smi,
const struct smi_instance *inst_array)
{
struct i2c_board_info board_info = {};
struct device *dev = &pdev->dev;
struct acpi_device *adev = ACPI_COMPANION(dev);
char name[32];
int i, ret, count;
ret = i2c_acpi_client_count(adev);
if (ret < 0)
return ret;
else if (!ret)
return -ENODEV;
if (!ret)
return -ENOENT;
count = ret;
@ -230,12 +227,8 @@ static int smi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct smi_node *node;
struct acpi_device *adev;
struct smi *smi;
adev = ACPI_COMPANION(dev);
if (!adev)
return -ENODEV;
int ret;
node = device_get_match_data(dev);
if (!node) {
@ -251,19 +244,25 @@ static int smi_probe(struct platform_device *pdev)
switch (node->bus_type) {
case SMI_I2C:
return smi_i2c_probe(pdev, adev, smi, node->instances);
return smi_i2c_probe(pdev, smi, node->instances);
case SMI_SPI:
return smi_spi_probe(pdev, adev, smi, node->instances);
return smi_spi_probe(pdev, smi, node->instances);
case SMI_AUTO_DETECT:
if (i2c_acpi_client_count(adev) > 0)
return smi_i2c_probe(pdev, adev, smi, node->instances);
else
return smi_spi_probe(pdev, adev, smi, node->instances);
/*
* For backwards-compatibility with the existing nodes I2C
* is checked first and if such entries are found ONLY I2C
* devices are created. Since some existing nodes that were
* already handled by this driver could also contain unrelated
* SpiSerialBus nodes that were previously ignored, and this
* preserves that behavior.
*/
ret = smi_i2c_probe(pdev, smi, node->instances);
if (ret != -ENOENT)
return ret;
return smi_spi_probe(pdev, smi, node->instances);
default:
return -EINVAL;
}
return 0; /* never reached */
}
static int smi_remove(struct platform_device *pdev)
@ -325,8 +324,8 @@ static const struct smi_node cs35l41_hda = {
static const struct acpi_device_id smi_acpi_ids[] = {
{ "BSG1160", (unsigned long)&bsg1160_data },
{ "BSG2150", (unsigned long)&bsg2150_data },
{ "INT3515", (unsigned long)&int3515_data },
{ "CSC3551", (unsigned long)&cs35l41_hda },
{ "INT3515", (unsigned long)&int3515_data },
/* Non-conforming _HID for Cirrus Logic already released */
{ "CLSA0100", (unsigned long)&cs35l41_hda },
{ }

View File

@ -51,6 +51,7 @@ static int register_platform_devices(u32 station_id)
{
u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
char *pdevname = KBUILD_MODNAME "_leds";
int i;
platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
@ -64,10 +65,12 @@ static int register_platform_devices(u32 station_id)
}
if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
if (ledmode == SIMATIC_IPC_DEVICE_127E)
pdevname = KBUILD_MODNAME "_leds_gpio";
platform_data.devmode = ledmode;
ipc_led_platform_device =
platform_device_register_data(NULL,
KBUILD_MODNAME "_leds", PLATFORM_DEVID_NONE,
pdevname, PLATFORM_DEVID_NONE,
&platform_data,
sizeof(struct simatic_ipc_platform));
if (IS_ERR(ipc_led_platform_device))
@ -101,44 +104,6 @@ static int register_platform_devices(u32 station_id)
return 0;
}
/* FIXME: this should eventually be done with generic P2SB discovery code
* the individual drivers for watchdogs and LEDs access memory that implements
* GPIO, but pinctrl will not come up because of missing ACPI entries
*
* While there is no conflict a cleaner solution would be to somehow bring up
* pinctrl even with these ACPI entries missing, and base the drivers on pinctrl.
* After which the following function could be dropped, together with the code
* poking the memory.
*/
/*
* Get membase address from PCI, used in leds and wdt module. Here we read
* the bar0. The final address calculation is done in the appropriate modules
*/
u32 simatic_ipc_get_membase0(unsigned int p2sb)
{
struct pci_bus *bus;
u32 bar0 = 0;
/*
* The GPIO memory is in bar0 of the hidden P2SB device.
* Unhide the device to have a quick look at it, before we hide it
* again.
* Also grab the pci rescan lock so that device does not get discovered
* and remapped while it is visible.
* This code is inspired by drivers/mfd/lpc_ich.c
*/
bus = pci_find_bus(0, 0);
pci_lock_rescan_remove();
pci_bus_write_config_byte(bus, p2sb, 0xE1, 0x0);
pci_bus_read_config_dword(bus, p2sb, PCI_BASE_ADDRESS_0, &bar0);
bar0 &= ~0xf;
pci_bus_write_config_byte(bus, p2sb, 0xE1, 0x1);
pci_unlock_rescan_remove();
return bar0;
}
EXPORT_SYMBOL(simatic_ipc_get_membase0);
static int __init simatic_ipc_init_module(void)
{
const struct dmi_system_id *match;

View File

@ -4341,7 +4341,7 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
{
struct acpi_resource_irq *p = &resource->data.irq;
struct sony_pic_irq *interrupt = NULL;
if (!p || !p->interrupt_count) {
if (!p->interrupt_count) {
/*
* IRQ descriptors may have no IRQ# bits set,
* particularly those those w/ _STA disabled
@ -4374,11 +4374,6 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
struct acpi_resource_io *io = &resource->data.io;
struct sony_pic_ioport *ioport =
list_first_entry(&dev->ioports, struct sony_pic_ioport, list);
if (!io) {
dprintk("Blank IO resource\n");
return AE_OK;
}
if (!ioport->io1.minimum) {
memcpy(&ioport->io1, io, sizeof(*io));
dprintk("IO1 at 0x%.4x (0x%.2x)\n", ioport->io1.minimum,

View File

@ -339,7 +339,7 @@ static ssize_t kb_led_color_show(
struct led_classdev *led;
struct system76_data *data;
led = (struct led_classdev *)dev->driver_data;
led = dev_get_drvdata(dev);
data = container_of(led, struct system76_data, kb_led);
return sysfs_emit(buf, "%06X\n", data->kb_color);
}
@ -356,7 +356,7 @@ static ssize_t kb_led_color_store(
unsigned int val;
int ret;
led = (struct led_classdev *)dev->driver_data;
led = dev_get_drvdata(dev);
data = container_of(led, struct system76_data, kb_led);
ret = kstrtouint(buf, 16, &val);
if (ret)

View File

@ -34,46 +34,51 @@
* thanks to Chris Wright <chrisw@osdl.org>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/nvram.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/sysfs.h>
#include <linux/acpi.h>
#include <linux/backlight.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/fb.h>
#include <linux/platform_device.h>
#include <linux/freezer.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/leds.h>
#include <linux/rfkill.h>
#include <linux/dmi.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/leds.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/nvram.h>
#include <linux/pci.h>
#include <linux/power_supply.h>
#include <linux/platform_device.h>
#include <linux/platform_profile.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/initval.h>
#include <linux/power_supply.h>
#include <linux/proc_fs.h>
#include <linux/rfkill.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/string_helpers.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/workqueue.h>
#include <acpi/battery.h>
#include <acpi/video.h>
#include <drm/drm_privacy_screen_driver.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/initval.h>
#include "dual_accel_detect.h"
/* ThinkPad CMOS commands */
@ -159,6 +164,7 @@ enum tpacpi_hkey_event_t {
TP_HKEY_EV_VOL_DOWN = 0x1016, /* Volume down or unmute */
TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */
TP_HKEY_EV_PRIVACYGUARD_TOGGLE = 0x130f, /* Toggle priv.guard on/off */
TP_HKEY_EV_AMT_TOGGLE = 0x131a, /* Toggle AMT on/off */
/* Reasons for waking up from S3/S4 */
TP_HKEY_EV_WKUP_S3_UNDOCK = 0x2304, /* undock requested, S3 */
@ -257,8 +263,6 @@ enum tpacpi_hkey_event_t {
#define TPACPI_DBG_BRGHT 0x0020
#define TPACPI_DBG_MIXER 0x0040
#define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off")
#define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
#define strlencmp(a, b) (strncmp((a), (b), strlen(b)))
@ -1312,9 +1316,7 @@ static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, struct seq_file *
return status;
}
seq_printf(m, "status:\t\t%s\n",
(status == TPACPI_RFK_RADIO_ON) ?
"enabled" : "disabled");
seq_printf(m, "status:\t\t%s\n", str_enabled_disabled(status == TPACPI_RFK_RADIO_ON));
seq_printf(m, "commands:\tenable, disable\n");
}
@ -1341,8 +1343,7 @@ static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf)
if (status != -1) {
tpacpi_disclose_usertask("procfs", "attempt to %s %s\n",
(status == TPACPI_RFK_RADIO_ON) ?
"enable" : "disable",
str_enable_disable(status == TPACPI_RFK_RADIO_ON),
tpacpi_rfkill_names[id]);
res = (tpacpi_rfkill_switches[id]->ops->set_status)(status);
tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]);
@ -3499,8 +3500,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) {
tp_features.hotkey_wlsw = 1;
radiosw_state = !!status;
pr_info("radio switch found; radios are %s\n",
enabled(status, 0));
pr_info("radio switch found; radios are %s\n", str_enabled_disabled(status & BIT(0)));
}
tabletsw_state = hotkey_init_tablet_mode();
@ -3735,6 +3735,7 @@ static bool hotkey_notify_extended_hotkey(const u32 hkey)
switch (hkey) {
case TP_HKEY_EV_PRIVACYGUARD_TOGGLE:
case TP_HKEY_EV_AMT_TOGGLE:
tpacpi_driver_event(hkey);
return true;
}
@ -4159,7 +4160,7 @@ static int hotkey_read(struct seq_file *m)
if (res)
return res;
seq_printf(m, "status:\t\t%s\n", enabled(status, 0));
seq_printf(m, "status:\t\t%s\n", str_enabled_disabled(status & BIT(0)));
if (hotkey_all_mask) {
seq_printf(m, "mask:\t\t0x%08x\n", hotkey_user_mask);
seq_printf(m, "commands:\tenable, disable, reset, <mask>\n");
@ -4292,9 +4293,8 @@ static int bluetooth_set_status(enum tpacpi_rfkill_state state)
{
int status;
vdbg_printk(TPACPI_DBG_RFKILL,
"will attempt to %s bluetooth\n",
(state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable");
vdbg_printk(TPACPI_DBG_RFKILL, "will attempt to %s bluetooth\n",
str_enable_disable(state == TPACPI_RFK_RADIO_ON));
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if (dbg_bluetoothemul) {
@ -4659,9 +4659,8 @@ static int wan_set_status(enum tpacpi_rfkill_state state)
{
int status;
vdbg_printk(TPACPI_DBG_RFKILL,
"will attempt to %s wwan\n",
(state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable");
vdbg_printk(TPACPI_DBG_RFKILL, "will attempt to %s wwan\n",
str_enable_disable(state == TPACPI_RFK_RADIO_ON));
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if (dbg_wwanemul) {
@ -4837,9 +4836,8 @@ static int uwb_set_status(enum tpacpi_rfkill_state state)
{
int status;
vdbg_printk(TPACPI_DBG_RFKILL,
"will attempt to %s UWB\n",
(state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable");
vdbg_printk(TPACPI_DBG_RFKILL, "will attempt to %s UWB\n",
str_enable_disable(state == TPACPI_RFK_RADIO_ON));
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if (dbg_uwbemul) {
@ -5193,11 +5191,11 @@ static int video_read(struct seq_file *m)
return autosw;
seq_printf(m, "status:\t\tsupported\n");
seq_printf(m, "lcd:\t\t%s\n", enabled(status, 0));
seq_printf(m, "crt:\t\t%s\n", enabled(status, 1));
seq_printf(m, "lcd:\t\t%s\n", str_enabled_disabled(status & BIT(0)));
seq_printf(m, "crt:\t\t%s\n", str_enabled_disabled(status & BIT(1)));
if (video_supported == TPACPI_VIDEO_NEW)
seq_printf(m, "dvi:\t\t%s\n", enabled(status, 3));
seq_printf(m, "auto:\t\t%s\n", enabled(autosw, 0));
seq_printf(m, "dvi:\t\t%s\n", str_enabled_disabled(status & BIT(3)));
seq_printf(m, "auto:\t\t%s\n", str_enabled_disabled(autosw & BIT(0)));
seq_printf(m, "commands:\tlcd_enable, lcd_disable\n");
seq_printf(m, "commands:\tcrt_enable, crt_disable\n");
if (video_supported == TPACPI_VIDEO_NEW)
@ -5628,7 +5626,7 @@ static int light_read(struct seq_file *m)
status = light_get_status();
if (status < 0)
return status;
seq_printf(m, "status:\t\t%s\n", onoff(status, 0));
seq_printf(m, "status:\t\t%s\n", str_on_off(status & BIT(0)));
seq_printf(m, "commands:\ton, off\n");
}
@ -6084,9 +6082,7 @@ static int __init led_init(struct ibm_init_struct *iibm)
return 0;
}
#define str_led_status(s) \
((s) == TPACPI_LED_OFF ? "off" : \
((s) == TPACPI_LED_ON ? "on" : "blinking"))
#define str_led_status(s) ((s) >= TPACPI_LED_BLINK ? "blinking" : str_on_off(s))
static int led_read(struct seq_file *m)
{
@ -6103,8 +6099,7 @@ static int led_read(struct seq_file *m)
status = led_get_status(i);
if (status < 0)
return -EIO;
seq_printf(m, "%d:\t\t%s\n",
i, str_led_status(status));
seq_printf(m, "%d:\t\t%s\n", i, str_led_status(status));
}
}
@ -6797,10 +6792,7 @@ static int brightness_set(unsigned int value)
static int brightness_update_status(struct backlight_device *bd)
{
unsigned int level =
(bd->props.fb_blank == FB_BLANK_UNBLANK &&
bd->props.power == FB_BLANK_UNBLANK) ?
bd->props.brightness : 0;
int level = backlight_get_brightness(bd);
dbg_printk(TPACPI_DBG_BRGHT,
"backlight: attempt to set level to %d\n",
@ -7830,8 +7822,7 @@ static int volume_read(struct seq_file *m)
seq_printf(m, "level:\t\t%d\n",
status & TP_EC_AUDIO_LVL_MSK);
seq_printf(m, "mute:\t\t%s\n",
onoff(status, TP_EC_AUDIO_MUTESW));
seq_printf(m, "mute:\t\t%s\n", str_on_off(status & BIT(TP_EC_AUDIO_MUTESW)));
if (volume_control_allowed) {
seq_printf(m, "commands:\tunmute, mute\n");
@ -9060,7 +9051,7 @@ static int fan_read(struct seq_file *m)
seq_printf(m, "status:\t\t%s\n"
"level:\t\t%d\n",
(status != 0) ? "enabled" : "disabled", status);
str_enabled_disabled(status), status);
break;
case TPACPI_FAN_RD_TPEC:
@ -9069,8 +9060,7 @@ static int fan_read(struct seq_file *m)
if (rc)
return rc;
seq_printf(m, "status:\t\t%s\n",
(status != 0) ? "enabled" : "disabled");
seq_printf(m, "status:\t\t%s\n", str_enabled_disabled(status));
rc = fan_get_speed(&speed);
if (rc < 0)
@ -10268,6 +10258,7 @@ static struct ibm_struct proxsensor_driver_data = {
#define DYTC_CMD_FUNC_CAP 3 /* To get DYTC capabilities */
#define DYTC_FC_MMC 27 /* MMC Mode supported */
#define DYTC_FC_PSC 29 /* PSC Mode supported */
#define DYTC_FC_AMT 31 /* AMT mode supported */
#define DYTC_GET_FUNCTION_BIT 8 /* Bits 8-11 - function setting */
#define DYTC_GET_MODE_BIT 12 /* Bits 12-15 - mode setting */
@ -10280,6 +10271,10 @@ static struct ibm_struct proxsensor_driver_data = {
#define DYTC_FUNCTION_CQL 1 /* Function = 1, lap mode */
#define DYTC_FUNCTION_MMC 11 /* Function = 11, MMC mode */
#define DYTC_FUNCTION_PSC 13 /* Function = 13, PSC mode */
#define DYTC_FUNCTION_AMT 15 /* Function = 15, AMT mode */
#define DYTC_MODE_AMT_ENABLE 0x1 /* Enable AMT (in balanced mode) */
#define DYTC_MODE_AMT_DISABLE 0xF /* Disable AMT (in other modes) */
#define DYTC_MODE_MMC_PERFORM 2 /* High power mode aka performance */
#define DYTC_MODE_MMC_LOWPOWER 3 /* Low power mode */
@ -10300,6 +10295,8 @@ static struct ibm_struct proxsensor_driver_data = {
#define DYTC_DISABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_MMC_BALANCE, 0)
#define DYTC_ENABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_MMC_BALANCE, 1)
static int dytc_control_amt(bool enable);
static bool dytc_amt_active;
static enum platform_profile_option dytc_current_profile;
static atomic_t dytc_ignore_event = ATOMIC_INIT(0);
@ -10382,6 +10379,30 @@ static int dytc_profile_get(struct platform_profile_handler *pprof,
return 0;
}
static int dytc_control_amt(bool enable)
{
int dummy;
int err;
int cmd;
if (!(dytc_capabilities & BIT(DYTC_FC_AMT))) {
pr_warn("Attempting to toggle AMT on a system that doesn't advertise support\n");
return -ENODEV;
}
if (enable)
cmd = DYTC_SET_COMMAND(DYTC_FUNCTION_AMT, DYTC_MODE_AMT_ENABLE, enable);
else
cmd = DYTC_SET_COMMAND(DYTC_FUNCTION_AMT, DYTC_MODE_AMT_DISABLE, enable);
pr_debug("%sabling AMT (cmd 0x%x)", enable ? "en":"dis", cmd);
err = dytc_command(cmd, &dummy);
if (err)
return err;
dytc_amt_active = enable;
return 0;
}
/*
* Helper function - check if we are in CQL mode and if we are
* - disable CQL,
@ -10464,6 +10485,9 @@ static int dytc_profile_set(struct platform_profile_handler *pprof,
err = dytc_command(DYTC_SET_COMMAND(DYTC_FUNCTION_PSC, perfmode, 1), &output);
if (err)
goto unlock;
/* system supports AMT, activate it when on balanced */
if (dytc_capabilities & BIT(DYTC_FC_AMT))
dytc_control_amt(profile == PLATFORM_PROFILE_BALANCED);
}
/* Success - update current profile */
dytc_current_profile = profile;
@ -10568,6 +10592,11 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
/* Ensure initial values are correct */
dytc_profile_refresh();
/* Set AMT correctly now we know current profile */
if ((dytc_capabilities & BIT(DYTC_FC_PSC)) &&
(dytc_capabilities & BIT(DYTC_FC_AMT)))
dytc_control_amt(dytc_current_profile == PLATFORM_PROFILE_BALANCED);
return 0;
}
@ -11011,6 +11040,15 @@ static void tpacpi_driver_event(const unsigned int hkey_event)
if (changed)
drm_privacy_screen_call_notifier_chain(lcdshadow_dev);
}
if (hkey_event == TP_HKEY_EV_AMT_TOGGLE) {
/* If we're enabling AMT we need to force balanced mode */
if (!dytc_amt_active)
/* This will also set AMT mode enabled */
dytc_profile_set(NULL, PLATFORM_PROFILE_BALANCED);
else
dytc_control_amt(!dytc_amt_active);
}
}
static void hotkey_driver_event(const unsigned int scancode)

View File

@ -802,7 +802,7 @@ static int spwr_battery_register(struct spwr_battery_device *bat)
if (IS_ERR(bat->psy))
return PTR_ERR(bat->psy);
return ssam_notifier_register(bat->sdev->ctrl, &bat->notif);
return ssam_device_notifier_register(bat->sdev, &bat->notif);
}
@ -837,7 +837,7 @@ static void surface_battery_remove(struct ssam_device *sdev)
{
struct spwr_battery_device *bat = ssam_device_get_drvdata(sdev);
ssam_notifier_unregister(sdev->ctrl, &bat->notif);
ssam_device_notifier_unregister(sdev, &bat->notif);
cancel_delayed_work_sync(&bat->update_work);
}

View File

@ -216,7 +216,7 @@ static int spwr_ac_register(struct spwr_ac_device *ac)
if (IS_ERR(ac->psy))
return PTR_ERR(ac->psy);
return ssam_notifier_register(ac->sdev->ctrl, &ac->notif);
return ssam_device_notifier_register(ac->sdev, &ac->notif);
}
@ -251,7 +251,7 @@ static void surface_ac_remove(struct ssam_device *sdev)
{
struct spwr_ac_device *ac = ssam_device_get_drvdata(sdev);
ssam_notifier_unregister(sdev->ctrl, &ac->notif);
ssam_device_notifier_unregister(sdev, &ac->notif);
}
static const struct spwr_psy_properties spwr_psy_props_adp1 = {

View File

@ -1647,6 +1647,7 @@ config SIEMENS_SIMATIC_IPC_WDT
tristate "Siemens Simatic IPC Watchdog"
depends on SIEMENS_SIMATIC_IPC
select WATCHDOG_CORE
select P2SB
help
This driver adds support for several watchdogs found in Industrial
PCs from Siemens.

View File

@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_data/x86/p2sb.h>
#include <linux/platform_data/x86/simatic-ipc-base.h>
#include <linux/platform_device.h>
#include <linux/sizes.h>
@ -54,9 +55,9 @@ static struct resource io_resource_trigger =
DEFINE_RES_IO_NAMED(WD_TRIGGER_IOADR, SZ_1,
KBUILD_MODNAME " WD_TRIGGER_IOADR");
/* the actual start will be discovered with pci, 0 is a placeholder */
/* the actual start will be discovered with p2sb, 0 is a placeholder */
static struct resource mem_resource =
DEFINE_RES_MEM_NAMED(0, SZ_4, "WD_RESET_BASE_ADR");
DEFINE_RES_MEM_NAMED(0, 0, "WD_RESET_BASE_ADR");
static u32 wd_timeout_table[] = {2, 4, 6, 8, 16, 32, 48, 64 };
static void __iomem *wd_reset_base_addr;
@ -150,6 +151,7 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev)
struct simatic_ipc_platform *plat = pdev->dev.platform_data;
struct device *dev = &pdev->dev;
struct resource *res;
int ret;
switch (plat->devmode) {
case SIMATIC_IPC_DEVICE_227E:
@ -190,15 +192,14 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev)
if (plat->devmode == SIMATIC_IPC_DEVICE_427E) {
res = &mem_resource;
/* get GPIO base from PCI */
res->start = simatic_ipc_get_membase0(PCI_DEVFN(0x1f, 1));
if (res->start == 0)
return -ENODEV;
ret = p2sb_bar(NULL, 0, res);
if (ret)
return ret;
/* do the final address calculation */
res->start = res->start + (GPIO_COMMUNITY0_PORT_ID << 16) +
PAD_CFG_DW0_GPP_A_23;
res->end += res->start;
res->end = res->start + SZ_4 - 1;
wd_reset_base_addr = devm_ioremap_resource(dev, res);
if (IS_ERR(wd_reset_base_addr))

View File

@ -49,6 +49,7 @@
#define ASUS_WMI_DEVID_LED4 0x00020014
#define ASUS_WMI_DEVID_LED5 0x00020015
#define ASUS_WMI_DEVID_LED6 0x00020016
#define ASUS_WMI_DEVID_MICMUTE_LED 0x00040017
/* Backlight and Brightness */
#define ASUS_WMI_DEVID_ALS_ENABLE 0x00050001 /* Ambient Light Sensor */

View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Primary to Sideband (P2SB) bridge access support
*/
#ifndef _PLATFORM_DATA_X86_P2SB_H
#define _PLATFORM_DATA_X86_P2SB_H
#include <linux/errno.h>
#include <linux/kconfig.h>
struct pci_bus;
struct resource;
#if IS_BUILTIN(CONFIG_P2SB)
int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem);
#else /* CONFIG_P2SB */
static inline int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem)
{
return -ENODEV;
}
#endif /* CONFIG_P2SB is not set */
#endif /* _PLATFORM_DATA_X86_P2SB_H */

View File

@ -47,7 +47,7 @@
#define PMC_S0I2_TMR 0x88
#define PMC_S0I3_TMR 0x8C
#define PMC_S0_TMR 0x90
/* Sleep state counter is in units of of 32us */
/* Sleep state counter is in units of 32us */
#define PMC_TMR_SHIFT 5
/* Power status of power islands */

View File

@ -24,6 +24,4 @@ struct simatic_ipc_platform {
u8 devmode;
};
u32 simatic_ipc_get_membase0(unsigned int p2sb);
#endif /* __PLATFORM_DATA_X86_SIMATIC_IPC_BASE_H */

View File

@ -469,6 +469,67 @@ struct ssam_request_spec_md {
return 0; \
}
/**
* SSAM_DEFINE_SYNC_REQUEST_WR() - Define synchronous SAM request function with
* both argument and return value.
* @name: Name of the generated function.
* @atype: Type of the request's argument.
* @rtype: Type of the request's return value.
* @spec: Specification (&struct ssam_request_spec) defining the request.
*
* Defines a function executing the synchronous SAM request specified by @spec,
* with the request taking an argument of type @atype and having a return value
* of type @rtype. The generated function takes care of setting up the request
* and response structs, buffer allocation, as well as execution of the request
* itself, returning once the request has been fully completed. The required
* transport buffer will be allocated on the stack.
*
* The generated function is defined as ``static int name(struct
* ssam_controller *ctrl, const atype *arg, rtype *ret)``, returning the status
* of the request, which is zero on success and negative on failure. The
* ``ctrl`` parameter is the controller via which the request is sent. The
* request argument is specified via the ``arg`` pointer. The request's return
* value is written to the memory pointed to by the ``ret`` parameter.
*
* Refer to ssam_request_sync_onstack() for more details on the behavior of
* the generated function.
*/
#define SSAM_DEFINE_SYNC_REQUEST_WR(name, atype, rtype, spec...) \
static int name(struct ssam_controller *ctrl, const atype *arg, rtype *ret) \
{ \
struct ssam_request_spec s = (struct ssam_request_spec)spec; \
struct ssam_request rqst; \
struct ssam_response rsp; \
int status; \
\
rqst.target_category = s.target_category; \
rqst.target_id = s.target_id; \
rqst.command_id = s.command_id; \
rqst.instance_id = s.instance_id; \
rqst.flags = s.flags | SSAM_REQUEST_HAS_RESPONSE; \
rqst.length = sizeof(atype); \
rqst.payload = (u8 *)arg; \
\
rsp.capacity = sizeof(rtype); \
rsp.length = 0; \
rsp.pointer = (u8 *)ret; \
\
status = ssam_request_sync_onstack(ctrl, &rqst, &rsp, sizeof(atype)); \
if (status) \
return status; \
\
if (rsp.length != sizeof(rtype)) { \
struct device *dev = ssam_controller_device(ctrl); \
dev_err(dev, \
"rqst: invalid response length, expected %zu, got %zu (tc: %#04x, cid: %#04x)", \
sizeof(rtype), rsp.length, rqst.target_category,\
rqst.command_id); \
return -EIO; \
} \
\
return 0; \
}
/**
* SSAM_DEFINE_SYNC_REQUEST_MD_N() - Define synchronous multi-device SAM
* request function with neither argument nor return value.
@ -613,6 +674,70 @@ struct ssam_request_spec_md {
return 0; \
}
/**
* SSAM_DEFINE_SYNC_REQUEST_MD_WR() - Define synchronous multi-device SAM
* request function with both argument and return value.
* @name: Name of the generated function.
* @atype: Type of the request's argument.
* @rtype: Type of the request's return value.
* @spec: Specification (&struct ssam_request_spec_md) defining the request.
*
* Defines a function executing the synchronous SAM request specified by @spec,
* with the request taking an argument of type @atype and having a return value
* of type @rtype. Device specifying parameters are not hard-coded, but instead
* must be provided to the function. The generated function takes care of
* setting up the request and response structs, buffer allocation, as well as
* execution of the request itself, returning once the request has been fully
* completed. The required transport buffer will be allocated on the stack.
*
* The generated function is defined as ``static int name(struct
* ssam_controller *ctrl, u8 tid, u8 iid, const atype *arg, rtype *ret)``,
* returning the status of the request, which is zero on success and negative
* on failure. The ``ctrl`` parameter is the controller via which the request
* is sent, ``tid`` the target ID for the request, and ``iid`` the instance ID.
* The request argument is specified via the ``arg`` pointer. The request's
* return value is written to the memory pointed to by the ``ret`` parameter.
*
* Refer to ssam_request_sync_onstack() for more details on the behavior of
* the generated function.
*/
#define SSAM_DEFINE_SYNC_REQUEST_MD_WR(name, atype, rtype, spec...) \
static int name(struct ssam_controller *ctrl, u8 tid, u8 iid, \
const atype *arg, rtype *ret) \
{ \
struct ssam_request_spec_md s = (struct ssam_request_spec_md)spec; \
struct ssam_request rqst; \
struct ssam_response rsp; \
int status; \
\
rqst.target_category = s.target_category; \
rqst.target_id = tid; \
rqst.command_id = s.command_id; \
rqst.instance_id = iid; \
rqst.flags = s.flags | SSAM_REQUEST_HAS_RESPONSE; \
rqst.length = sizeof(atype); \
rqst.payload = (u8 *)arg; \
\
rsp.capacity = sizeof(rtype); \
rsp.length = 0; \
rsp.pointer = (u8 *)ret; \
\
status = ssam_request_sync_onstack(ctrl, &rqst, &rsp, sizeof(atype)); \
if (status) \
return status; \
\
if (rsp.length != sizeof(rtype)) { \
struct device *dev = ssam_controller_device(ctrl); \
dev_err(dev, \
"rqst: invalid response length, expected %zu, got %zu (tc: %#04x, cid: %#04x)", \
sizeof(rtype), rsp.length, rqst.target_category,\
rqst.command_id); \
return -EIO; \
} \
\
return 0; \
}
/* -- Event notifier/callbacks. --------------------------------------------- */
@ -835,8 +960,28 @@ struct ssam_event_notifier {
int ssam_notifier_register(struct ssam_controller *ctrl,
struct ssam_event_notifier *n);
int ssam_notifier_unregister(struct ssam_controller *ctrl,
struct ssam_event_notifier *n);
int __ssam_notifier_unregister(struct ssam_controller *ctrl,
struct ssam_event_notifier *n, bool disable);
/**
* ssam_notifier_unregister() - Unregister an event notifier.
* @ctrl: The controller the notifier has been registered on.
* @n: The event notifier to unregister.
*
* Unregister an event notifier. Decrement the usage counter of the associated
* SAM event if the notifier is not marked as an observer. If the usage counter
* reaches zero, the event will be disabled.
*
* Return: Returns zero on success, %-ENOENT if the given notifier block has
* not been registered on the controller. If the given notifier block was the
* last one associated with its specific event, returns the status of the
* event-disable EC-command.
*/
static inline int ssam_notifier_unregister(struct ssam_controller *ctrl,
struct ssam_event_notifier *n)
{
return __ssam_notifier_unregister(ctrl, n, true);
}
int ssam_controller_event_enable(struct ssam_controller *ctrl,
struct ssam_event_registry reg,

View File

@ -15,6 +15,7 @@
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/types.h>
#include <linux/surface_aggregator/controller.h>
@ -148,17 +149,30 @@ struct ssam_device_uid {
#define SSAM_SDEV(cat, tid, iid, fun) \
SSAM_DEVICE(SSAM_DOMAIN_SERIALHUB, SSAM_SSH_TC_##cat, tid, iid, fun)
/*
* enum ssam_device_flags - Flags for SSAM client devices.
* @SSAM_DEVICE_HOT_REMOVED_BIT:
* The device has been hot-removed. Further communication with it may time
* out and should be avoided.
*/
enum ssam_device_flags {
SSAM_DEVICE_HOT_REMOVED_BIT = 0,
};
/**
* struct ssam_device - SSAM client device.
* @dev: Driver model representation of the device.
* @ctrl: SSAM controller managing this device.
* @uid: UID identifying the device.
* @dev: Driver model representation of the device.
* @ctrl: SSAM controller managing this device.
* @uid: UID identifying the device.
* @flags: Device state flags, see &enum ssam_device_flags.
*/
struct ssam_device {
struct device dev;
struct ssam_controller *ctrl;
struct ssam_device_uid uid;
unsigned long flags;
};
/**
@ -177,6 +191,8 @@ struct ssam_device_driver {
void (*remove)(struct ssam_device *sdev);
};
#ifdef CONFIG_SURFACE_AGGREGATOR_BUS
extern struct bus_type ssam_bus_type;
extern const struct device_type ssam_device_type;
@ -193,6 +209,15 @@ static inline bool is_ssam_device(struct device *d)
return d->type == &ssam_device_type;
}
#else /* CONFIG_SURFACE_AGGREGATOR_BUS */
static inline bool is_ssam_device(struct device *d)
{
return false;
}
#endif /* CONFIG_SURFACE_AGGREGATOR_BUS */
/**
* to_ssam_device() - Casts the given device to a SSAM client device.
* @d: The device to cast.
@ -240,6 +265,35 @@ struct ssam_device *ssam_device_alloc(struct ssam_controller *ctrl,
int ssam_device_add(struct ssam_device *sdev);
void ssam_device_remove(struct ssam_device *sdev);
/**
* ssam_device_mark_hot_removed() - Mark the given device as hot-removed.
* @sdev: The device to mark as hot-removed.
*
* Mark the device as having been hot-removed. This signals drivers using the
* device that communication with the device should be avoided and may lead to
* timeouts.
*/
static inline void ssam_device_mark_hot_removed(struct ssam_device *sdev)
{
dev_dbg(&sdev->dev, "marking device as hot-removed\n");
set_bit(SSAM_DEVICE_HOT_REMOVED_BIT, &sdev->flags);
}
/**
* ssam_device_is_hot_removed() - Check if the given device has been
* hot-removed.
* @sdev: The device to check.
*
* Checks if the given device has been marked as hot-removed. See
* ssam_device_mark_hot_removed() for more details.
*
* Return: Returns ``true`` if the device has been marked as hot-removed.
*/
static inline bool ssam_device_is_hot_removed(struct ssam_device *sdev)
{
return test_bit(SSAM_DEVICE_HOT_REMOVED_BIT, &sdev->flags);
}
/**
* ssam_device_get() - Increment reference count of SSAM client device.
* @sdev: The device to increment the reference count of.
@ -322,11 +376,62 @@ void ssam_device_driver_unregister(struct ssam_device_driver *d);
/* -- Helpers for controller and hub devices. ------------------------------- */
#ifdef CONFIG_SURFACE_AGGREGATOR_BUS
int __ssam_register_clients(struct device *parent, struct ssam_controller *ctrl,
struct fwnode_handle *node);
void ssam_remove_clients(struct device *dev);
#else /* CONFIG_SURFACE_AGGREGATOR_BUS */
static inline int __ssam_register_clients(struct device *parent, struct ssam_controller *ctrl,
struct fwnode_handle *node)
{
return 0;
}
static inline void ssam_remove_clients(struct device *dev) {}
#endif /* CONFIG_SURFACE_AGGREGATOR_BUS */
/**
* ssam_register_clients() - Register all client devices defined under the
* given parent device.
* @dev: The parent device under which clients should be registered.
* @ctrl: The controller with which client should be registered.
*
* Register all clients that have via firmware nodes been defined as children
* of the given (parent) device. The respective child firmware nodes will be
* associated with the correspondingly created child devices.
*
* The given controller will be used to instantiate the new devices. See
* ssam_device_add() for details.
*
* Return: Returns zero on success, nonzero on failure.
*/
static inline int ssam_register_clients(struct device *dev, struct ssam_controller *ctrl)
{
return __ssam_register_clients(dev, ctrl, dev_fwnode(dev));
}
/**
* ssam_device_register_clients() - Register all client devices defined under
* the given SSAM parent device.
* @sdev: The parent device under which clients should be registered.
*
* Register all clients that have via firmware nodes been defined as children
* of the given (parent) device. The respective child firmware nodes will be
* associated with the correspondingly created child devices.
*
* The controller used by the parent device will be used to instantiate the new
* devices. See ssam_device_add() for details.
*
* Return: Returns zero on success, nonzero on failure.
*/
static inline int ssam_device_register_clients(struct ssam_device *sdev)
{
return ssam_register_clients(&sdev->dev, sdev->ctrl);
}
/* -- Helpers for client-device requests. ----------------------------------- */
@ -430,4 +535,106 @@ static inline void ssam_remove_clients(struct device *dev) {}
sdev->uid.instance, ret); \
}
/**
* SSAM_DEFINE_SYNC_REQUEST_CL_WR() - Define synchronous client-device SAM
* request function with argument and return value.
* @name: Name of the generated function.
* @atype: Type of the request's argument.
* @rtype: Type of the request's return value.
* @spec: Specification (&struct ssam_request_spec_md) defining the request.
*
* Defines a function executing the synchronous SAM request specified by @spec,
* with the request taking an argument of type @atype and having a return value
* of type @rtype. Device specifying parameters are not hard-coded, but instead
* are provided via the client device, specifically its UID, supplied when
* calling this function. The generated function takes care of setting up the
* request struct, buffer allocation, as well as execution of the request
* itself, returning once the request has been fully completed. The required
* transport buffer will be allocated on the stack.
*
* The generated function is defined as ``static int name(struct ssam_device
* *sdev, const atype *arg, rtype *ret)``, returning the status of the request,
* which is zero on success and negative on failure. The ``sdev`` parameter
* specifies both the target device of the request and by association the
* controller via which the request is sent. The request's argument is
* specified via the ``arg`` pointer. The request's return value is written to
* the memory pointed to by the ``ret`` parameter.
*
* Refer to ssam_request_sync_onstack() for more details on the behavior of
* the generated function.
*/
#define SSAM_DEFINE_SYNC_REQUEST_CL_WR(name, atype, rtype, spec...) \
SSAM_DEFINE_SYNC_REQUEST_MD_WR(__raw_##name, atype, rtype, spec) \
static int name(struct ssam_device *sdev, const atype *arg, rtype *ret) \
{ \
return __raw_##name(sdev->ctrl, sdev->uid.target, \
sdev->uid.instance, arg, ret); \
}
/* -- Helpers for client-device notifiers. ---------------------------------- */
/**
* ssam_device_notifier_register() - Register an event notifier for the
* specified client device.
* @sdev: The device the notifier should be registered on.
* @n: The event notifier to register.
*
* Register an event notifier. Increment the usage counter of the associated
* SAM event if the notifier is not marked as an observer. If the event is not
* marked as an observer and is currently not enabled, it will be enabled
* during this call. If the notifier is marked as an observer, no attempt will
* be made at enabling any event and no reference count will be modified.
*
* Notifiers marked as observers do not need to be associated with one specific
* event, i.e. as long as no event matching is performed, only the event target
* category needs to be set.
*
* Return: Returns zero on success, %-ENOSPC if there have already been
* %INT_MAX notifiers for the event ID/type associated with the notifier block
* registered, %-ENOMEM if the corresponding event entry could not be
* allocated, %-ENODEV if the device is marked as hot-removed. If this is the
* first time that a notifier block is registered for the specific associated
* event, returns the status of the event-enable EC-command.
*/
static inline int ssam_device_notifier_register(struct ssam_device *sdev,
struct ssam_event_notifier *n)
{
/*
* Note that this check does not provide any guarantees whatsoever as
* hot-removal could happen at any point and we can't protect against
* it. Nevertheless, if we can detect hot-removal, bail early to avoid
* communication timeouts.
*/
if (ssam_device_is_hot_removed(sdev))
return -ENODEV;
return ssam_notifier_register(sdev->ctrl, n);
}
/**
* ssam_device_notifier_unregister() - Unregister an event notifier for the
* specified client device.
* @sdev: The device the notifier has been registered on.
* @n: The event notifier to unregister.
*
* Unregister an event notifier. Decrement the usage counter of the associated
* SAM event if the notifier is not marked as an observer. If the usage counter
* reaches zero, the event will be disabled.
*
* In case the device has been marked as hot-removed, the event will not be
* disabled on the EC, as in those cases any attempt at doing so may time out.
*
* Return: Returns zero on success, %-ENOENT if the given notifier block has
* not been registered on the controller. If the given notifier block was the
* last one associated with its specific event, returns the status of the
* event-disable EC-command.
*/
static inline int ssam_device_notifier_unregister(struct ssam_device *sdev,
struct ssam_event_notifier *n)
{
return __ssam_notifier_unregister(sdev->ctrl, n,
!ssam_device_is_hot_removed(sdev));
}
#endif /* _LINUX_SURFACE_AGGREGATOR_DEVICE_H */

View File

@ -201,7 +201,7 @@ static inline u16 ssh_crc(const u8 *buf, size_t len)
* exception of zero, which is not an event ID. Thus, this is also the
* absolute maximum number of event handlers that can be registered.
*/
#define SSH_NUM_EVENTS 34
#define SSH_NUM_EVENTS 38
/*
* SSH_NUM_TARGETS - The number of communication targets used in the protocol.
@ -292,40 +292,45 @@ struct ssam_span {
* Windows driver.
*/
enum ssam_ssh_tc {
/* Category 0x00 is invalid for EC use. */
SSAM_SSH_TC_SAM = 0x01, /* Generic system functionality, real-time clock. */
SSAM_SSH_TC_BAT = 0x02, /* Battery/power subsystem. */
SSAM_SSH_TC_TMP = 0x03, /* Thermal subsystem. */
SSAM_SSH_TC_PMC = 0x04,
SSAM_SSH_TC_FAN = 0x05,
SSAM_SSH_TC_PoM = 0x06,
SSAM_SSH_TC_DBG = 0x07,
SSAM_SSH_TC_KBD = 0x08, /* Legacy keyboard (Laptop 1/2). */
SSAM_SSH_TC_FWU = 0x09,
SSAM_SSH_TC_UNI = 0x0a,
SSAM_SSH_TC_LPC = 0x0b,
SSAM_SSH_TC_TCL = 0x0c,
SSAM_SSH_TC_SFL = 0x0d,
SSAM_SSH_TC_KIP = 0x0e,
SSAM_SSH_TC_EXT = 0x0f,
SSAM_SSH_TC_BLD = 0x10,
SSAM_SSH_TC_BAS = 0x11, /* Detachment system (Surface Book 2/3). */
SSAM_SSH_TC_SEN = 0x12,
SSAM_SSH_TC_SRQ = 0x13,
SSAM_SSH_TC_MCU = 0x14,
SSAM_SSH_TC_HID = 0x15, /* Generic HID input subsystem. */
SSAM_SSH_TC_TCH = 0x16,
SSAM_SSH_TC_BKL = 0x17,
SSAM_SSH_TC_TAM = 0x18,
SSAM_SSH_TC_ACC = 0x19,
SSAM_SSH_TC_UFI = 0x1a,
SSAM_SSH_TC_USC = 0x1b,
SSAM_SSH_TC_PEN = 0x1c,
SSAM_SSH_TC_VID = 0x1d,
SSAM_SSH_TC_AUD = 0x1e,
SSAM_SSH_TC_SMC = 0x1f,
SSAM_SSH_TC_KPD = 0x20,
SSAM_SSH_TC_REG = 0x21, /* Extended event registry. */
/* Category 0x00 is invalid for EC use. */
SSAM_SSH_TC_SAM = 0x01, /* Generic system functionality, real-time clock. */
SSAM_SSH_TC_BAT = 0x02, /* Battery/power subsystem. */
SSAM_SSH_TC_TMP = 0x03, /* Thermal subsystem. */
SSAM_SSH_TC_PMC = 0x04,
SSAM_SSH_TC_FAN = 0x05,
SSAM_SSH_TC_PoM = 0x06,
SSAM_SSH_TC_DBG = 0x07,
SSAM_SSH_TC_KBD = 0x08, /* Legacy keyboard (Laptop 1/2). */
SSAM_SSH_TC_FWU = 0x09,
SSAM_SSH_TC_UNI = 0x0a,
SSAM_SSH_TC_LPC = 0x0b,
SSAM_SSH_TC_TCL = 0x0c,
SSAM_SSH_TC_SFL = 0x0d,
SSAM_SSH_TC_KIP = 0x0e, /* Manages detachable peripherals (Pro X/8 keyboard cover) */
SSAM_SSH_TC_EXT = 0x0f,
SSAM_SSH_TC_BLD = 0x10,
SSAM_SSH_TC_BAS = 0x11, /* Detachment system (Surface Book 2/3). */
SSAM_SSH_TC_SEN = 0x12,
SSAM_SSH_TC_SRQ = 0x13,
SSAM_SSH_TC_MCU = 0x14,
SSAM_SSH_TC_HID = 0x15, /* Generic HID input subsystem. */
SSAM_SSH_TC_TCH = 0x16,
SSAM_SSH_TC_BKL = 0x17,
SSAM_SSH_TC_TAM = 0x18,
SSAM_SSH_TC_ACC0 = 0x19,
SSAM_SSH_TC_UFI = 0x1a,
SSAM_SSH_TC_USC = 0x1b,
SSAM_SSH_TC_PEN = 0x1c,
SSAM_SSH_TC_VID = 0x1d,
SSAM_SSH_TC_AUD = 0x1e,
SSAM_SSH_TC_SMC = 0x1f,
SSAM_SSH_TC_KPD = 0x20,
SSAM_SSH_TC_REG = 0x21, /* Extended event registry. */
SSAM_SSH_TC_SPT = 0x22,
SSAM_SSH_TC_SYS = 0x23,
SSAM_SSH_TC_ACC1 = 0x24,
SSAM_SSH_TC_SHB = 0x25,
SSAM_SSH_TC_POS = 0x26, /* For obtaining Laptop Studio screen position. */
};

View File

@ -144,7 +144,7 @@ static int family_handler(struct nl_msg *msg, void *arg)
continue;
res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]);
break;
};
}
return 0;
}

View File

@ -41,7 +41,7 @@ void process_level_change(int cpu)
time_t tm;
int ret;
if (pkg_id >= MAX_PACKAGE_COUNT || die_id > MAX_DIE_PER_PACKAGE) {
if (pkg_id >= MAX_PACKAGE_COUNT || die_id >= MAX_DIE_PER_PACKAGE) {
debug_printf("Invalid package/die info for cpu:%d\n", cpu);
return;
}