diff options
| author | Stephen Rothwell <sfr@canb.auug.org.au> | 2022-06-28 13:56:10 +1000 |
|---|---|---|
| committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2022-06-28 13:56:10 +1000 |
| commit | 3e35c72204c6c3828454103559e106dabe17f32d (patch) | |
| tree | cecb403d3432455da317f3f75d4bc9e73469a047 | |
| parent | f6d8eb2bde6d0659dd437a944bc62a3672204489 (diff) | |
| parent | f0da93cbaf7af868e34d611d555f4067b748ecdf (diff) | |
| download | linux-next-3e35c72204c6c3828454103559e106dabe17f32d.tar.gz linux-next-3e35c72204c6c3828454103559e106dabe17f32d.zip | |
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git
26 files changed, 799 insertions, 373 deletions
diff --git a/Documentation/driver-api/surface_aggregator/client.rst b/Documentation/driver-api/surface_aggregator/client.rst index e519d374c378..27f95abdbe99 100644 --- a/Documentation/driver-api/surface_aggregator/client.rst +++ b/Documentation/driver-api/surface_aggregator/client.rst @@ -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 diff --git a/MAINTAINERS b/MAINTAINERS index 31975d34c8a3..dfe7d0aa457f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -997,7 +997,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> @@ -1007,7 +1007,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> diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile index 1244c4e568ff..c2088b3c4081 100644 --- a/drivers/clk/x86/Makefile +++ b/drivers/clk/x86/Makefile @@ -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 diff --git a/drivers/hid/surface-hid/surface_hid_core.c b/drivers/hid/surface-hid/surface_hid_core.c index e46330b2e561..87637f813de2 100644 --- a/drivers/hid/surface-hid/surface_hid_core.c +++ b/drivers/hid/surface-hid/surface_hid_core.c @@ -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); diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig index 18fc6a08569e..b437847b6237 100644 --- a/drivers/platform/Kconfig +++ b/drivers/platform/Kconfig @@ -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" diff --git a/drivers/platform/surface/aggregator/controller.c b/drivers/platform/surface/aggregator/controller.c index b8c377b3f932..6de834b52b63 100644 --- a/drivers/platform/surface/aggregator/controller.c +++ b/drivers/platform/surface/aggregator/controller.c @@ -2199,16 +2199,26 @@ static int ssam_nf_refcount_enable(struct ssam_controller *ctrl, } /** - * ssam_nf_refcount_disable_free() - Disable event for reference count entry if it is - * no longer in use and free the corresponding entry. + * ssam_nf_refcount_disable_free() - Disable event for reference count entry if + * it is no longer in use and free the corresponding entry. * @ctrl: The controller to disable the event on. * @entry: The reference count entry for the event to be disabled. * @flags: The flags used for enabling the event on the EC. + * @ec: Flag specifying if the event should actually be disabled on the EC. * - * If the reference count equals zero, i.e. the event is no longer requested by - * any client, the event will be disabled and the corresponding reference count - * entry freed. The reference count entry must not be used any more after a - * call to this function. + * If ``ec`` equals ``true`` and the reference count equals zero (i.e. the + * event is no longer requested by any client), the specified event will be + * disabled on the EC via the corresponding request. + * + * If ``ec`` equals ``false``, no request will be sent to the EC and the event + * can be considered in a detached state (i.e. no longer used but still + * enabled). Disabling an event via this method may be required for + * hot-removable devices, where event disable requests may time out after the + * device has been physically removed. + * + * In both cases, if the reference count equals zero, the corresponding + * reference count entry will be freed. The reference count entry must not be + * used any more after a call to this function. * * Also checks if the flags used for disabling the event match the flags used * for enabling the event and warns if they do not (regardless of reference @@ -2223,7 +2233,7 @@ static int ssam_nf_refcount_enable(struct ssam_controller *ctrl, * returns the status of the event-enable EC command. */ static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl, - struct ssam_nf_refcount_entry *entry, u8 flags) + struct ssam_nf_refcount_entry *entry, u8 flags, bool ec) { const struct ssam_event_registry reg = entry->key.reg; const struct ssam_event_id id = entry->key.id; @@ -2232,8 +2242,9 @@ static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl, lockdep_assert_held(&nf->lock); - ssam_dbg(ctrl, "disabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n", - reg.target_category, id.target_category, id.instance, entry->refcount); + ssam_dbg(ctrl, "%s event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n", + ec ? "disabling" : "detaching", reg.target_category, id.target_category, + id.instance, entry->refcount); if (entry->flags != flags) { ssam_warn(ctrl, @@ -2242,7 +2253,7 @@ static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl, id.instance); } - if (entry->refcount == 0) { + if (ec && entry->refcount == 0) { status = ssam_ssh_event_disable(ctrl, reg, id, flags); kfree(entry); } @@ -2322,20 +2333,26 @@ int ssam_notifier_register(struct ssam_controller *ctrl, struct ssam_event_notif EXPORT_SYMBOL_GPL(ssam_notifier_register); /** - * ssam_notifier_unregister() - Unregister an event notifier. - * @ctrl: The controller the notifier has been registered on. - * @n: The event notifier to unregister. + * __ssam_notifier_unregister() - Unregister an event notifier. + * @ctrl: The controller the notifier has been registered on. + * @n: The event notifier to unregister. + * @disable: Whether to disable the corresponding event on the EC. * * Unregister an event notifier. Decrement the usage counter of the associated * SAM event if the notifier is not marked as an observer. If the usage counter - * reaches zero, the event will be disabled. + * reaches zero and ``disable`` equals ``true``, the event will be disabled. + * + * Useful for hot-removable devices, where communication may fail once the + * device has been physically removed. In that case, specifying ``disable`` as + * ``false`` avoids communication with the EC. * * Return: Returns zero on success, %-ENOENT if the given notifier block has * not been registered on the controller. If the given notifier block was the * last one associated with its specific event, returns the status of the * event-disable EC-command. */ -int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_notifier *n) +int __ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_notifier *n, + bool disable) { u16 rqid = ssh_tc_to_rqid(n->event.id.target_category); struct ssam_nf_refcount_entry *entry; @@ -2373,7 +2390,7 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_not goto remove; } - status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags); + status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags, disable); } remove: @@ -2383,7 +2400,7 @@ remove: return status; } -EXPORT_SYMBOL_GPL(ssam_notifier_unregister); +EXPORT_SYMBOL_GPL(__ssam_notifier_unregister); /** * ssam_controller_event_enable() - Enable the specified event. @@ -2477,7 +2494,7 @@ int ssam_controller_event_disable(struct ssam_controller *ctrl, return -ENOENT; } - status = ssam_nf_refcount_disable_free(ctrl, entry, flags); + status = ssam_nf_refcount_disable_free(ctrl, entry, flags, true); mutex_unlock(&nf->lock); return status; diff --git a/drivers/platform/surface/aggregator/trace.h b/drivers/platform/surface/aggregator/trace.h index de64cf169060..cc9e73fbc18e 100644 --- a/drivers/platform/surface/aggregator/trace.h +++ b/drivers/platform/surface/aggregator/trace.h @@ -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, diff --git a/drivers/platform/surface/surface_acpi_notify.c b/drivers/platform/surface/surface_acpi_notify.c index 7b758f8cc137..c0e12f0b9b79 100644 --- a/drivers/platform/surface/surface_acpi_notify.c +++ b/drivers/platform/surface/surface_acpi_notify.c @@ -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"); diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index ce2bd88feeaa..bf3303f1aa71 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -41,9 +41,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, }; @@ -155,6 +161,30 @@ 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, +}; + /* * Devices for 5th- and 6th-generations models: * - Surface Book 2, @@ -230,10 +260,15 @@ 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_hid_kip_keyboard, + &ssam_node_hid_kip_penstash, + &ssam_node_hid_kip_touchpad, + &ssam_node_hid_kip_iid5, + /* TODO: Add support for tablet mode switch. */ NULL, }; @@ -308,30 +343,159 @@ err: } -/* -- SSAM base-hub driver. ------------------------------------------------- */ +/* -- SSAM generic subsystem hub driver framework. -------------------------- */ -/* - * 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_hub_state { + SSAM_HUB_UNINITIALIZED, /* Only set during initialization. */ + SSAM_HUB_CONNECTED, + SSAM_HUB_DISCONNECTED, +}; -enum ssam_base_hub_state { - SSAM_BASE_HUB_UNINITIALIZED, - SSAM_BASE_HUB_CONNECTED, - SSAM_BASE_HUB_DISCONNECTED, +enum ssam_hub_flags { + SSAM_HUB_HOT_REMOVED, }; -struct ssam_base_hub { +struct ssam_hub { struct ssam_device *sdev; - enum ssam_base_hub_state state; + enum ssam_hub_state state; + unsigned long flags; + struct delayed_work update_work; + unsigned long connect_delay; struct ssam_event_notifier notif; + + int (*get_state)(struct ssam_hub *hub, enum ssam_hub_state *state); }; +static void ssam_hub_update_workfn(struct work_struct *work) +{ + struct ssam_hub *hub = container_of(work, struct ssam_hub, update_work.work); + struct fwnode_handle *node = dev_fwnode(&hub->sdev->dev); + enum ssam_hub_state state; + int status = 0; + + status = hub->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_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 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_setup(struct ssam_device *sdev, struct ssam_hub *hub) +{ + int status; + + hub->sdev = sdev; + hub->state = SSAM_HUB_UNINITIALIZED; + + 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-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) + SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_query_opmode, u8, { .target_category = SSAM_SSH_TC_BAS, .target_id = 0x01, @@ -342,7 +506,7 @@ SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_query_opmode, u8, { #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) +static int ssam_base_hub_query_state(struct ssam_hub *hub, enum ssam_hub_state *state) { u8 opmode; int status; @@ -354,62 +518,16 @@ static int ssam_base_hub_query_state(struct ssam_base_hub *hub, enum ssam_base_h } if (opmode != SSAM_BAS_OPMODE_TABLET) - *state = SSAM_BASE_HUB_CONNECTED; + *state = SSAM_HUB_CONNECTED; else - *state = SSAM_BASE_HUB_DISCONNECTED; + *state = SSAM_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; + struct ssam_hub *hub = container_of(nf, struct ssam_hub, notif); if (event->command_id != SSAM_EVENT_BAS_CID_CONNECTION) return 0; @@ -419,13 +537,7 @@ static u32 ssam_base_hub_notif(struct ssam_event_notifier *nf, const struct ssam 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); + ssam_hub_update(hub, event->data[0]); /* * Do not return SSAM_NOTIF_HANDLED: The event should be picked up and @@ -435,27 +547,14 @@ static u32 ssam_base_hub_notif(struct ssam_event_notifier *nf, const struct ssam 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; + struct ssam_hub *hub; 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; @@ -464,52 +563,112 @@ static int ssam_base_hub_probe(struct ssam_device *sdev) 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); + hub->connect_delay = SSAM_BASE_UPDATE_CONNECT_DELAY; + hub->get_state = ssam_base_hub_query_state; - ssam_device_set_drvdata(sdev, hub); + return ssam_hub_setup(sdev, hub); +} - status = ssam_notifier_register(sdev->ctrl, &hub->notif); - if (status) - return status; +static const struct ssam_device_id ssam_base_hub_match[] = { + { SSAM_VDEV(HUB, 0x02, SSAM_SSH_TC_BAS, 0x00) }, + { }, +}; - status = sysfs_create_group(&sdev->dev.kobj, &ssam_base_hub_group); - if (status) - goto err; +static struct ssam_device_driver ssam_base_hub_driver = { + .probe = ssam_base_hub_probe, + .remove = ssam_hub_remove, + .match_table = ssam_base_hub_match, + .driver = { + .name = "surface_aggregator_base_hub", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .pm = &ssam_hub_pm_ops, + }, +}; - schedule_delayed_work(&hub->update_work, 0); + +/* -- 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 msecs_to_jiffies(250) + +#define SSAM_EVENT_KIP_CID_CONNECTION 0x2c + +SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_get_connection_state, u8, { + .target_category = SSAM_SSH_TC_KIP, + .target_id = 0x01, + .command_id = 0x2c, + .instance_id = 0x00, +}); + +static int ssam_kip_get_connection_state(struct ssam_hub *hub, enum ssam_hub_state *state) +{ + int status; + u8 connected; + + status = ssam_retry(__ssam_kip_get_connection_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; +} -err: - ssam_notifier_unregister(sdev->ctrl, &hub->notif); - cancel_delayed_work_sync(&hub->update_work); - ssam_remove_clients(&sdev->dev); - return status; +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 void ssam_base_hub_remove(struct ssam_device *sdev) +static int ssam_kip_hub_probe(struct ssam_device *sdev) { - struct ssam_base_hub *hub = ssam_device_get_drvdata(sdev); + struct ssam_hub *hub; - sysfs_remove_group(&sdev->dev.kobj, &ssam_base_hub_group); + hub = devm_kzalloc(&sdev->dev, sizeof(*hub), GFP_KERNEL); + if (!hub) + return -ENOMEM; - ssam_notifier_unregister(sdev->ctrl, &hub->notif); - cancel_delayed_work_sync(&hub->update_work); - ssam_remove_clients(&sdev->dev); + hub->notif.base.priority = INT_MAX; /* This notifier should run first. */ + hub->notif.base.fn = ssam_kip_hub_notif; + hub->notif.event.reg = SSAM_EVENT_REGISTRY_SAM; + hub->notif.event.id.target_category = SSAM_SSH_TC_KIP, + hub->notif.event.id.instance = 0, + hub->notif.event.mask = SSAM_EVENT_MASK_TARGET; + hub->notif.event.flags = SSAM_EVENT_SEQUENCED; + + hub->connect_delay = SSAM_KIP_UPDATE_CONNECT_DELAY; + hub->get_state = ssam_kip_get_connection_state; + + return ssam_hub_setup(sdev, hub); } -static const struct ssam_device_id ssam_base_hub_match[] = { - { SSAM_VDEV(HUB, 0x02, SSAM_ANY_IID, 0x00) }, +static const struct ssam_device_id ssam_kip_hub_match[] = { + { SSAM_VDEV(HUB, 0x01, SSAM_SSH_TC_KIP, 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, +static struct ssam_device_driver ssam_kip_hub_driver = { + .probe = ssam_kip_hub_probe, + .remove = ssam_hub_remove, + .match_table = ssam_kip_hub_match, .driver = { - .name = "surface_aggregator_base_hub", + .name = "surface_kip_hub", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &ssam_base_hub_pm_ops, + .pm = &ssam_hub_pm_ops, }, }; @@ -636,18 +795,30 @@ static int __init ssam_device_hub_init(void) status = platform_driver_register(&ssam_platform_hub_driver); if (status) - return status; + goto err_platform; status = ssam_device_driver_register(&ssam_base_hub_driver); if (status) - platform_driver_unregister(&ssam_platform_hub_driver); + goto err_base; + status = ssam_device_driver_register(&ssam_kip_hub_driver); + if (status) + goto err_kip; + + return 0; + +err_kip: + ssam_device_driver_unregister(&ssam_base_hub_driver); +err_base: + platform_driver_unregister(&ssam_platform_hub_driver); +err_platform: return status; } module_init(ssam_device_hub_init); static void __exit ssam_device_hub_exit(void) { + ssam_device_driver_unregister(&ssam_kip_hub_driver); ssam_device_driver_unregister(&ssam_base_hub_driver); platform_driver_unregister(&ssam_platform_hub_driver); } diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index f08ad85683cb..f01f75589627 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -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" @@ -1161,8 +1134,3 @@ config WINMATE_FM07_KEYS that delivers key events when these buttons are pressed. endif # X86_PLATFORM_DEVICES - -config PMC_ATOM - def_bool y - depends on PCI - select COMMON_CLK diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 4a59f47a46e2..a0e417c34a9b 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -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 @@ -126,7 +125,7 @@ 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 diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 9c6943e401a6..e0230ea0cb7e 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -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); diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kconfig new file mode 100644 index 000000000000..c0d0a3c5170c --- /dev/null +++ b/drivers/platform/x86/amd/Kconfig @@ -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. diff --git a/drivers/platform/x86/amd/Makefile b/drivers/platform/x86/amd/Makefile new file mode 100644 index 000000000000..a03fbb08e808 --- /dev/null +++ b/drivers/platform/x86/amd/Makefile @@ -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 diff --git a/drivers/platform/x86/amd_hsmp.c b/drivers/platform/x86/amd/hsmp.c index a0c54b838c11..a0c54b838c11 100644 --- a/drivers/platform/x86/amd_hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c diff --git a/drivers/platform/x86/amd-pmc.c b/drivers/platform/x86/amd/pmc.c index f11d18beac18..f11d18beac18 100644 --- a/drivers/platform/x86/amd-pmc.c +++ b/drivers/platform/x86/amd/pmc.c diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 57553f9b4d1d..ffe98a18440b 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -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); diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index ab610376fdad..0942f50bd793 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -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; } diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig index fe224a54f24c..25421e061c47 100644 --- a/drivers/platform/x86/dell/Kconfig +++ b/drivers/platform/x86/dell/Kconfig @@ -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. diff --git a/drivers/platform/x86/system76_acpi.c b/drivers/platform/x86/system76_acpi.c index 7299ad08c838..958df41ad509 100644 --- a/drivers/platform/x86/system76_acpi.c +++ b/drivers/platform/x86/system76_acpi.c @@ -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) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index efb5bc1d1cf6..2175ad363e4f 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -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,22 +10295,18 @@ 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; -enum dytc_profile_funcmode { - DYTC_FUNCMODE_NONE = 0, - DYTC_FUNCMODE_MMC, - DYTC_FUNCMODE_PSC, -}; - -static enum dytc_profile_funcmode dytc_profile_available; static enum platform_profile_option dytc_current_profile; static atomic_t dytc_ignore_event = ATOMIC_INIT(0); static DEFINE_MUTEX(dytc_mutex); +static int dytc_capabilities; static bool dytc_mmc_get_available; static int convert_dytc_to_profile(int dytcmode, enum platform_profile_option *profile) { - if (dytc_profile_available == DYTC_FUNCMODE_MMC) { + if (dytc_capabilities & BIT(DYTC_FC_MMC)) { switch (dytcmode) { case DYTC_MODE_MMC_LOWPOWER: *profile = PLATFORM_PROFILE_LOW_POWER; @@ -10332,7 +10323,7 @@ static int convert_dytc_to_profile(int dytcmode, enum platform_profile_option *p } return 0; } - if (dytc_profile_available == DYTC_FUNCMODE_PSC) { + if (dytc_capabilities & BIT(DYTC_FC_PSC)) { switch (dytcmode) { case DYTC_MODE_PSC_LOWPOWER: *profile = PLATFORM_PROFILE_LOW_POWER; @@ -10354,21 +10345,21 @@ static int convert_profile_to_dytc(enum platform_profile_option profile, int *pe { switch (profile) { case PLATFORM_PROFILE_LOW_POWER: - if (dytc_profile_available == DYTC_FUNCMODE_MMC) + if (dytc_capabilities & BIT(DYTC_FC_MMC)) *perfmode = DYTC_MODE_MMC_LOWPOWER; - else if (dytc_profile_available == DYTC_FUNCMODE_PSC) + else if (dytc_capabilities & BIT(DYTC_FC_PSC)) *perfmode = DYTC_MODE_PSC_LOWPOWER; break; case PLATFORM_PROFILE_BALANCED: - if (dytc_profile_available == DYTC_FUNCMODE_MMC) + if (dytc_capabilities & BIT(DYTC_FC_MMC)) *perfmode = DYTC_MODE_MMC_BALANCE; - else if (dytc_profile_available == DYTC_FUNCMODE_PSC) + else if (dytc_capabilities & BIT(DYTC_FC_PSC)) *perfmode = DYTC_MODE_PSC_BALANCE; break; case PLATFORM_PROFILE_PERFORMANCE: - if (dytc_profile_available == DYTC_FUNCMODE_MMC) + if (dytc_capabilities & BIT(DYTC_FC_MMC)) *perfmode = DYTC_MODE_MMC_PERFORM; - else if (dytc_profile_available == DYTC_FUNCMODE_PSC) + else if (dytc_capabilities & BIT(DYTC_FC_PSC)) *perfmode = DYTC_MODE_PSC_PERFORM; break; default: /* Unknown profile */ @@ -10388,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, @@ -10447,7 +10462,7 @@ static int dytc_profile_set(struct platform_profile_handler *pprof, if (err) goto unlock; - if (dytc_profile_available == DYTC_FUNCMODE_MMC) { + if (dytc_capabilities & BIT(DYTC_FC_MMC)) { if (profile == PLATFORM_PROFILE_BALANCED) { /* * To get back to balanced mode we need to issue a reset command. @@ -10466,10 +10481,13 @@ static int dytc_profile_set(struct platform_profile_handler *pprof, goto unlock; } } - if (dytc_profile_available == DYTC_FUNCMODE_PSC) { + if (dytc_capabilities & BIT(DYTC_FC_PSC)) { 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; @@ -10485,12 +10503,12 @@ static void dytc_profile_refresh(void) int perfmode; mutex_lock(&dytc_mutex); - if (dytc_profile_available == DYTC_FUNCMODE_MMC) { + if (dytc_capabilities & BIT(DYTC_FC_MMC)) { if (dytc_mmc_get_available) err = dytc_command(DYTC_CMD_MMC_GET, &output); else err = dytc_cql_command(DYTC_CMD_GET, &output); - } else if (dytc_profile_available == DYTC_FUNCMODE_PSC) + } else if (dytc_capabilities & BIT(DYTC_FC_PSC)) err = dytc_command(DYTC_CMD_GET, &output); mutex_unlock(&dytc_mutex); @@ -10519,7 +10537,6 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) set_bit(PLATFORM_PROFILE_BALANCED, dytc_profile.choices); set_bit(PLATFORM_PROFILE_PERFORMANCE, dytc_profile.choices); - dytc_profile_available = DYTC_FUNCMODE_NONE; err = dytc_command(DYTC_CMD_QUERY, &output); if (err) return err; @@ -10532,13 +10549,12 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) return -ENODEV; /* Check what capabilities are supported */ - err = dytc_command(DYTC_CMD_FUNC_CAP, &output); + err = dytc_command(DYTC_CMD_FUNC_CAP, &dytc_capabilities); if (err) return err; - if (output & BIT(DYTC_FC_MMC)) { /* MMC MODE */ - dytc_profile_available = DYTC_FUNCMODE_MMC; - + if (dytc_capabilities & BIT(DYTC_FC_MMC)) { /* MMC MODE */ + pr_debug("MMC is supported\n"); /* * Check if MMC_GET functionality available * Version > 6 and return success from MMC_GET command @@ -10549,8 +10565,8 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) if (!err && ((output & DYTC_ERR_MASK) == DYTC_ERR_SUCCESS)) dytc_mmc_get_available = true; } - } else if (output & BIT(DYTC_FC_PSC)) { /* PSC MODE */ - dytc_profile_available = DYTC_FUNCMODE_PSC; + } else if (dytc_capabilities & BIT(DYTC_FC_PSC)) { /* PSC MODE */ + pr_debug("PSC is supported\n"); } else { dbg_printk(TPACPI_DBG_INIT, "No DYTC support available\n"); return -ENODEV; @@ -10571,12 +10587,16 @@ 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; } static void dytc_profile_exit(void) { - dytc_profile_available = DYTC_FUNCMODE_NONE; platform_profile_remove(); } @@ -11015,6 +11035,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) diff --git a/drivers/power/supply/surface_battery.c b/drivers/power/supply/surface_battery.c index 5ec2e6bb2465..540707882bb0 100644 --- a/drivers/power/supply/surface_battery.c +++ b/drivers/power/supply/surface_battery.c @@ -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); } diff --git a/drivers/power/supply/surface_charger.c b/drivers/power/supply/surface_charger.c index a060c36c7766..59182d55742d 100644 --- a/drivers/power/supply/surface_charger.c +++ b/drivers/power/supply/surface_charger.c @@ -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 = { diff --git a/include/linux/surface_aggregator/controller.h b/include/linux/surface_aggregator/controller.h index 74bfdffaf7b0..50a2b4926c06 100644 --- a/include/linux/surface_aggregator/controller.h +++ b/include/linux/surface_aggregator/controller.h @@ -835,8 +835,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, diff --git a/include/linux/surface_aggregator/device.h b/include/linux/surface_aggregator/device.h index cc257097eb05..c418f7f2732d 100644 --- a/include/linux/surface_aggregator/device.h +++ b/include/linux/surface_aggregator/device.h @@ -148,17 +148,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 +190,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 +208,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. @@ -241,6 +265,35 @@ 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. * @@ -430,4 +483,70 @@ static inline void ssam_remove_clients(struct device *dev) {} sdev->uid.instance, 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 */ diff --git a/include/linux/surface_aggregator/serial_hub.h b/include/linux/surface_aggregator/serial_hub.h index c3de43edcffa..45501b6e54e8 100644 --- a/include/linux/surface_aggregator/serial_hub.h +++ b/include/linux/surface_aggregator/serial_hub.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. */ }; |
