summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2022-06-28 13:56:10 +1000
committerStephen Rothwell <sfr@canb.auug.org.au>2022-06-28 13:56:10 +1000
commit3e35c72204c6c3828454103559e106dabe17f32d (patch)
treececb403d3432455da317f3f75d4bc9e73469a047
parentf6d8eb2bde6d0659dd437a944bc62a3672204489 (diff)
parentf0da93cbaf7af868e34d611d555f4067b748ecdf (diff)
downloadlinux-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
-rw-r--r--Documentation/driver-api/surface_aggregator/client.rst6
-rw-r--r--MAINTAINERS4
-rw-r--r--drivers/clk/x86/Makefile4
-rw-r--r--drivers/hid/surface-hid/surface_hid_core.c38
-rw-r--r--drivers/platform/Kconfig5
-rw-r--r--drivers/platform/surface/aggregator/controller.c53
-rw-r--r--drivers/platform/surface/aggregator/trace.h80
-rw-r--r--drivers/platform/surface/surface_acpi_notify.c27
-rw-r--r--drivers/platform/surface/surface_aggregator_registry.c403
-rw-r--r--drivers/platform/x86/Kconfig40
-rw-r--r--drivers/platform/x86/Makefile5
-rw-r--r--drivers/platform/x86/acer-wmi.c7
-rw-r--r--drivers/platform/x86/amd/Kconfig31
-rw-r--r--drivers/platform/x86/amd/Makefile10
-rw-r--r--drivers/platform/x86/amd/hsmp.c (renamed from drivers/platform/x86/amd_hsmp.c)0
-rw-r--r--drivers/platform/x86/amd/pmc.c (renamed from drivers/platform/x86/amd-pmc.c)0
-rw-r--r--drivers/platform/x86/apple-gmux.c5
-rw-r--r--drivers/platform/x86/compal-laptop.c4
-rw-r--r--drivers/platform/x86/dell/Kconfig1
-rw-r--r--drivers/platform/x86/system76_acpi.c4
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c213
-rw-r--r--drivers/power/supply/surface_battery.c4
-rw-r--r--drivers/power/supply/surface_charger.c4
-rw-r--r--include/linux/surface_aggregator/controller.h24
-rw-r--r--include/linux/surface_aggregator/device.h125
-rw-r--r--include/linux/surface_aggregator/serial_hub.h75
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. */
};