mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 10:45:49 +00:00
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:
commit
5f0848190c
@ -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.
|
||||
|
@ -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.
|
@ -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
|
||||
|
16
MAINTAINERS
16
MAINTAINERS
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
105
drivers/leds/simple/simatic-ipc-leds-gpio.c
Normal file
105
drivers/leds/simple/simatic-ipc-leds-gpio.c
Normal 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>");
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
|
||||
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)
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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, ®val);
|
||||
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, ®val);
|
||||
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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_not
|
||||
|
||||
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;
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
|
@ -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>
|
||||
|
371
drivers/platform/surface/surface_aggregator_hub.c
Normal file
371
drivers/platform/surface/surface_aggregator_hub.c
Normal 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");
|
@ -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");
|
||||
|
533
drivers/platform/surface/surface_aggregator_tabletsw.c
Normal file
533
drivers/platform/surface/surface_aggregator_tabletsw.c
Normal 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");
|
@ -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>
|
||||
|
@ -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 = {
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
31
drivers/platform/x86/amd/Kconfig
Normal file
31
drivers/platform/x86/amd/Kconfig
Normal 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.
|
10
drivers/platform/x86/amd/Makefile
Normal file
10
drivers/platform/x86/amd/Makefile
Normal 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
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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 @@ static int __init mlxplat_init(void)
|
||||
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
133
drivers/platform/x86/p2sb.c
Normal 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);
|
@ -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);
|
||||
|
@ -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"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -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 @@ static int smi_spi_probe(struct platform_device *pdev, struct acpi_device *adev,
|
||||
/**
|
||||
* 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 },
|
||||
{ }
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 = {
|
||||
|
@ -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.
|
||||
|
@ -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))
|
||||
|
@ -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 */
|
||||
|
28
include/linux/platform_data/x86/p2sb.h
Normal file
28
include/linux/platform_data/x86/p2sb.h
Normal 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 */
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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 */
|
||||
|
@ -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. */
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user