1fc00bc8aSMaximilian Luz // SPDX-License-Identifier: GPL-2.0+ 2fc00bc8aSMaximilian Luz /* 3fc00bc8aSMaximilian Luz * Driver for the Surface ACPI Notify (SAN) interface/shim. 4fc00bc8aSMaximilian Luz * 5fc00bc8aSMaximilian Luz * Translates communication from ACPI to Surface System Aggregator Module 6fc00bc8aSMaximilian Luz * (SSAM/SAM) requests and back, specifically SAM-over-SSH. Translates SSAM 7fc00bc8aSMaximilian Luz * events back to ACPI notifications. Allows handling of discrete GPU 8fc00bc8aSMaximilian Luz * notifications sent from ACPI via the SAN interface by providing them to any 9fc00bc8aSMaximilian Luz * registered external driver. 10fc00bc8aSMaximilian Luz * 11fc00bc8aSMaximilian Luz * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com> 12fc00bc8aSMaximilian Luz */ 13fc00bc8aSMaximilian Luz 14fc00bc8aSMaximilian Luz #include <asm/unaligned.h> 15fc00bc8aSMaximilian Luz #include <linux/acpi.h> 16fc00bc8aSMaximilian Luz #include <linux/delay.h> 17fc00bc8aSMaximilian Luz #include <linux/jiffies.h> 18fc00bc8aSMaximilian Luz #include <linux/kernel.h> 19fc00bc8aSMaximilian Luz #include <linux/module.h> 20fc00bc8aSMaximilian Luz #include <linux/notifier.h> 21fc00bc8aSMaximilian Luz #include <linux/platform_device.h> 22fc00bc8aSMaximilian Luz #include <linux/rwsem.h> 23fc00bc8aSMaximilian Luz 24fc00bc8aSMaximilian Luz #include <linux/surface_aggregator/controller.h> 25fc00bc8aSMaximilian Luz #include <linux/surface_acpi_notify.h> 26fc00bc8aSMaximilian Luz 27fc00bc8aSMaximilian Luz struct san_data { 28fc00bc8aSMaximilian Luz struct device *dev; 29fc00bc8aSMaximilian Luz struct ssam_controller *ctrl; 30fc00bc8aSMaximilian Luz 31fc00bc8aSMaximilian Luz struct acpi_connection_info info; 32fc00bc8aSMaximilian Luz 33fc00bc8aSMaximilian Luz struct ssam_event_notifier nf_bat; 34fc00bc8aSMaximilian Luz struct ssam_event_notifier nf_tmp; 35fc00bc8aSMaximilian Luz }; 36fc00bc8aSMaximilian Luz 37fc00bc8aSMaximilian Luz #define to_san_data(ptr, member) \ 38fc00bc8aSMaximilian Luz container_of(ptr, struct san_data, member) 39fc00bc8aSMaximilian Luz 40fc00bc8aSMaximilian Luz 41fc00bc8aSMaximilian Luz /* -- dGPU notifier interface. ---------------------------------------------- */ 42fc00bc8aSMaximilian Luz 43fc00bc8aSMaximilian Luz struct san_rqsg_if { 44fc00bc8aSMaximilian Luz struct rw_semaphore lock; 45fc00bc8aSMaximilian Luz struct device *dev; 46fc00bc8aSMaximilian Luz struct blocking_notifier_head nh; 47fc00bc8aSMaximilian Luz }; 48fc00bc8aSMaximilian Luz 49fc00bc8aSMaximilian Luz static struct san_rqsg_if san_rqsg_if = { 50fc00bc8aSMaximilian Luz .lock = __RWSEM_INITIALIZER(san_rqsg_if.lock), 51fc00bc8aSMaximilian Luz .dev = NULL, 52fc00bc8aSMaximilian Luz .nh = BLOCKING_NOTIFIER_INIT(san_rqsg_if.nh), 53fc00bc8aSMaximilian Luz }; 54fc00bc8aSMaximilian Luz 55fc00bc8aSMaximilian Luz static int san_set_rqsg_interface_device(struct device *dev) 56fc00bc8aSMaximilian Luz { 57fc00bc8aSMaximilian Luz int status = 0; 58fc00bc8aSMaximilian Luz 59fc00bc8aSMaximilian Luz down_write(&san_rqsg_if.lock); 60fc00bc8aSMaximilian Luz if (!san_rqsg_if.dev && dev) 61fc00bc8aSMaximilian Luz san_rqsg_if.dev = dev; 62fc00bc8aSMaximilian Luz else 63fc00bc8aSMaximilian Luz status = -EBUSY; 64fc00bc8aSMaximilian Luz up_write(&san_rqsg_if.lock); 65fc00bc8aSMaximilian Luz 66fc00bc8aSMaximilian Luz return status; 67fc00bc8aSMaximilian Luz } 68fc00bc8aSMaximilian Luz 69fc00bc8aSMaximilian Luz /** 70fc00bc8aSMaximilian Luz * san_client_link() - Link client as consumer to SAN device. 71fc00bc8aSMaximilian Luz * @client: The client to link. 72fc00bc8aSMaximilian Luz * 73fc00bc8aSMaximilian Luz * Sets up a device link between the provided client device as consumer and 74fc00bc8aSMaximilian Luz * the SAN device as provider. This function can be used to ensure that the 75fc00bc8aSMaximilian Luz * SAN interface has been set up and will be set up for as long as the driver 76fc00bc8aSMaximilian Luz * of the client device is bound. This guarantees that, during that time, all 77fc00bc8aSMaximilian Luz * dGPU events will be received by any registered notifier. 78fc00bc8aSMaximilian Luz * 79fc00bc8aSMaximilian Luz * The link will be automatically removed once the client device's driver is 80fc00bc8aSMaximilian Luz * unbound. 81fc00bc8aSMaximilian Luz * 82fc00bc8aSMaximilian Luz * Return: Returns zero on success, %-ENXIO if the SAN interface has not been 83fc00bc8aSMaximilian Luz * set up yet, and %-ENOMEM if device link creation failed. 84fc00bc8aSMaximilian Luz */ 85fc00bc8aSMaximilian Luz int san_client_link(struct device *client) 86fc00bc8aSMaximilian Luz { 87fc00bc8aSMaximilian Luz const u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER; 88fc00bc8aSMaximilian Luz struct device_link *link; 89fc00bc8aSMaximilian Luz 90fc00bc8aSMaximilian Luz down_read(&san_rqsg_if.lock); 91fc00bc8aSMaximilian Luz 92fc00bc8aSMaximilian Luz if (!san_rqsg_if.dev) { 93fc00bc8aSMaximilian Luz up_read(&san_rqsg_if.lock); 94fc00bc8aSMaximilian Luz return -ENXIO; 95fc00bc8aSMaximilian Luz } 96fc00bc8aSMaximilian Luz 97fc00bc8aSMaximilian Luz link = device_link_add(client, san_rqsg_if.dev, flags); 98fc00bc8aSMaximilian Luz if (!link) { 99fc00bc8aSMaximilian Luz up_read(&san_rqsg_if.lock); 100fc00bc8aSMaximilian Luz return -ENOMEM; 101fc00bc8aSMaximilian Luz } 102fc00bc8aSMaximilian Luz 103fc00bc8aSMaximilian Luz if (READ_ONCE(link->status) == DL_STATE_SUPPLIER_UNBIND) { 104fc00bc8aSMaximilian Luz up_read(&san_rqsg_if.lock); 105fc00bc8aSMaximilian Luz return -ENXIO; 106fc00bc8aSMaximilian Luz } 107fc00bc8aSMaximilian Luz 108fc00bc8aSMaximilian Luz up_read(&san_rqsg_if.lock); 109fc00bc8aSMaximilian Luz return 0; 110fc00bc8aSMaximilian Luz } 111fc00bc8aSMaximilian Luz EXPORT_SYMBOL_GPL(san_client_link); 112fc00bc8aSMaximilian Luz 113fc00bc8aSMaximilian Luz /** 114fc00bc8aSMaximilian Luz * san_dgpu_notifier_register() - Register a SAN dGPU notifier. 115fc00bc8aSMaximilian Luz * @nb: The notifier-block to register. 116fc00bc8aSMaximilian Luz * 117fc00bc8aSMaximilian Luz * Registers a SAN dGPU notifier, receiving any new SAN dGPU events sent from 118fc00bc8aSMaximilian Luz * ACPI. The registered notifier will be called with &struct san_dgpu_event 119fc00bc8aSMaximilian Luz * as notifier data and the command ID of that event as notifier action. 120fc00bc8aSMaximilian Luz */ 121fc00bc8aSMaximilian Luz int san_dgpu_notifier_register(struct notifier_block *nb) 122fc00bc8aSMaximilian Luz { 123fc00bc8aSMaximilian Luz return blocking_notifier_chain_register(&san_rqsg_if.nh, nb); 124fc00bc8aSMaximilian Luz } 125fc00bc8aSMaximilian Luz EXPORT_SYMBOL_GPL(san_dgpu_notifier_register); 126fc00bc8aSMaximilian Luz 127fc00bc8aSMaximilian Luz /** 128fc00bc8aSMaximilian Luz * san_dgpu_notifier_unregister() - Unregister a SAN dGPU notifier. 129fc00bc8aSMaximilian Luz * @nb: The notifier-block to unregister. 130fc00bc8aSMaximilian Luz */ 131fc00bc8aSMaximilian Luz int san_dgpu_notifier_unregister(struct notifier_block *nb) 132fc00bc8aSMaximilian Luz { 133fc00bc8aSMaximilian Luz return blocking_notifier_chain_unregister(&san_rqsg_if.nh, nb); 134fc00bc8aSMaximilian Luz } 135fc00bc8aSMaximilian Luz EXPORT_SYMBOL_GPL(san_dgpu_notifier_unregister); 136fc00bc8aSMaximilian Luz 137fc00bc8aSMaximilian Luz static int san_dgpu_notifier_call(struct san_dgpu_event *evt) 138fc00bc8aSMaximilian Luz { 139fc00bc8aSMaximilian Luz int ret; 140fc00bc8aSMaximilian Luz 141fc00bc8aSMaximilian Luz ret = blocking_notifier_call_chain(&san_rqsg_if.nh, evt->command, evt); 142fc00bc8aSMaximilian Luz return notifier_to_errno(ret); 143fc00bc8aSMaximilian Luz } 144fc00bc8aSMaximilian Luz 145fc00bc8aSMaximilian Luz 146fc00bc8aSMaximilian Luz /* -- ACPI _DSM event relay. ------------------------------------------------ */ 147fc00bc8aSMaximilian Luz 148fc00bc8aSMaximilian Luz #define SAN_DSM_REVISION 0 149fc00bc8aSMaximilian Luz 150fc00bc8aSMaximilian Luz /* 93b666c5-70c6-469f-a215-3d487c91ab3c */ 151fc00bc8aSMaximilian Luz static const guid_t SAN_DSM_UUID = 152fc00bc8aSMaximilian Luz GUID_INIT(0x93b666c5, 0x70c6, 0x469f, 0xa2, 0x15, 0x3d, 153fc00bc8aSMaximilian Luz 0x48, 0x7c, 0x91, 0xab, 0x3c); 154fc00bc8aSMaximilian Luz 155fc00bc8aSMaximilian Luz enum san_dsm_event_fn { 156fc00bc8aSMaximilian Luz SAN_DSM_EVENT_FN_BAT1_STAT = 0x03, 157fc00bc8aSMaximilian Luz SAN_DSM_EVENT_FN_BAT1_INFO = 0x04, 158fc00bc8aSMaximilian Luz SAN_DSM_EVENT_FN_ADP1_STAT = 0x05, 159fc00bc8aSMaximilian Luz SAN_DSM_EVENT_FN_ADP1_INFO = 0x06, 160fc00bc8aSMaximilian Luz SAN_DSM_EVENT_FN_BAT2_STAT = 0x07, 161fc00bc8aSMaximilian Luz SAN_DSM_EVENT_FN_BAT2_INFO = 0x08, 162fc00bc8aSMaximilian Luz SAN_DSM_EVENT_FN_THERMAL = 0x09, 163fc00bc8aSMaximilian Luz SAN_DSM_EVENT_FN_DPTF = 0x0a, 164fc00bc8aSMaximilian Luz }; 165fc00bc8aSMaximilian Luz 166fc00bc8aSMaximilian Luz enum sam_event_cid_bat { 167fc00bc8aSMaximilian Luz SAM_EVENT_CID_BAT_BIX = 0x15, 168fc00bc8aSMaximilian Luz SAM_EVENT_CID_BAT_BST = 0x16, 169fc00bc8aSMaximilian Luz SAM_EVENT_CID_BAT_ADP = 0x17, 170fc00bc8aSMaximilian Luz SAM_EVENT_CID_BAT_PROT = 0x18, 171fc00bc8aSMaximilian Luz SAM_EVENT_CID_BAT_DPTF = 0x4f, 172fc00bc8aSMaximilian Luz }; 173fc00bc8aSMaximilian Luz 174fc00bc8aSMaximilian Luz enum sam_event_cid_tmp { 175fc00bc8aSMaximilian Luz SAM_EVENT_CID_TMP_TRIP = 0x0b, 176fc00bc8aSMaximilian Luz }; 177fc00bc8aSMaximilian Luz 178fc00bc8aSMaximilian Luz struct san_event_work { 179fc00bc8aSMaximilian Luz struct delayed_work work; 180fc00bc8aSMaximilian Luz struct device *dev; 181fc00bc8aSMaximilian Luz struct ssam_event event; /* must be last */ 182fc00bc8aSMaximilian Luz }; 183fc00bc8aSMaximilian Luz 184fc00bc8aSMaximilian Luz static int san_acpi_notify_event(struct device *dev, u64 func, 185fc00bc8aSMaximilian Luz union acpi_object *param) 186fc00bc8aSMaximilian Luz { 187fc00bc8aSMaximilian Luz acpi_handle san = ACPI_HANDLE(dev); 188fc00bc8aSMaximilian Luz union acpi_object *obj; 189fc00bc8aSMaximilian Luz int status = 0; 190fc00bc8aSMaximilian Luz 191*d26cbdd2SColin Ian King if (!acpi_check_dsm(san, &SAN_DSM_UUID, SAN_DSM_REVISION, BIT_ULL(func))) 192fc00bc8aSMaximilian Luz return 0; 193fc00bc8aSMaximilian Luz 194fc00bc8aSMaximilian Luz dev_dbg(dev, "notify event %#04llx\n", func); 195fc00bc8aSMaximilian Luz 196fc00bc8aSMaximilian Luz obj = acpi_evaluate_dsm_typed(san, &SAN_DSM_UUID, SAN_DSM_REVISION, 197fc00bc8aSMaximilian Luz func, param, ACPI_TYPE_BUFFER); 198fc00bc8aSMaximilian Luz if (!obj) 199fc00bc8aSMaximilian Luz return -EFAULT; 200fc00bc8aSMaximilian Luz 201fc00bc8aSMaximilian Luz if (obj->buffer.length != 1 || obj->buffer.pointer[0] != 0) { 202fc00bc8aSMaximilian Luz dev_err(dev, "got unexpected result from _DSM\n"); 203fc00bc8aSMaximilian Luz status = -EPROTO; 204fc00bc8aSMaximilian Luz } 205fc00bc8aSMaximilian Luz 206fc00bc8aSMaximilian Luz ACPI_FREE(obj); 207fc00bc8aSMaximilian Luz return status; 208fc00bc8aSMaximilian Luz } 209fc00bc8aSMaximilian Luz 210fc00bc8aSMaximilian Luz static int san_evt_bat_adp(struct device *dev, const struct ssam_event *event) 211fc00bc8aSMaximilian Luz { 212fc00bc8aSMaximilian Luz int status; 213fc00bc8aSMaximilian Luz 214fc00bc8aSMaximilian Luz status = san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_ADP1_STAT, NULL); 215fc00bc8aSMaximilian Luz if (status) 216fc00bc8aSMaximilian Luz return status; 217fc00bc8aSMaximilian Luz 218fc00bc8aSMaximilian Luz /* 219fc00bc8aSMaximilian Luz * Ensure that the battery states get updated correctly. When the 220fc00bc8aSMaximilian Luz * battery is fully charged and an adapter is plugged in, it sometimes 221fc00bc8aSMaximilian Luz * is not updated correctly, instead showing it as charging. 222fc00bc8aSMaximilian Luz * Explicitly trigger battery updates to fix this. 223fc00bc8aSMaximilian Luz */ 224fc00bc8aSMaximilian Luz 225fc00bc8aSMaximilian Luz status = san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_BAT1_STAT, NULL); 226fc00bc8aSMaximilian Luz if (status) 227fc00bc8aSMaximilian Luz return status; 228fc00bc8aSMaximilian Luz 229fc00bc8aSMaximilian Luz return san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_BAT2_STAT, NULL); 230fc00bc8aSMaximilian Luz } 231fc00bc8aSMaximilian Luz 232fc00bc8aSMaximilian Luz static int san_evt_bat_bix(struct device *dev, const struct ssam_event *event) 233fc00bc8aSMaximilian Luz { 234fc00bc8aSMaximilian Luz enum san_dsm_event_fn fn; 235fc00bc8aSMaximilian Luz 236fc00bc8aSMaximilian Luz if (event->instance_id == 0x02) 237fc00bc8aSMaximilian Luz fn = SAN_DSM_EVENT_FN_BAT2_INFO; 238fc00bc8aSMaximilian Luz else 239fc00bc8aSMaximilian Luz fn = SAN_DSM_EVENT_FN_BAT1_INFO; 240fc00bc8aSMaximilian Luz 241fc00bc8aSMaximilian Luz return san_acpi_notify_event(dev, fn, NULL); 242fc00bc8aSMaximilian Luz } 243fc00bc8aSMaximilian Luz 244fc00bc8aSMaximilian Luz static int san_evt_bat_bst(struct device *dev, const struct ssam_event *event) 245fc00bc8aSMaximilian Luz { 246fc00bc8aSMaximilian Luz enum san_dsm_event_fn fn; 247fc00bc8aSMaximilian Luz 248fc00bc8aSMaximilian Luz if (event->instance_id == 0x02) 249fc00bc8aSMaximilian Luz fn = SAN_DSM_EVENT_FN_BAT2_STAT; 250fc00bc8aSMaximilian Luz else 251fc00bc8aSMaximilian Luz fn = SAN_DSM_EVENT_FN_BAT1_STAT; 252fc00bc8aSMaximilian Luz 253fc00bc8aSMaximilian Luz return san_acpi_notify_event(dev, fn, NULL); 254fc00bc8aSMaximilian Luz } 255fc00bc8aSMaximilian Luz 256fc00bc8aSMaximilian Luz static int san_evt_bat_dptf(struct device *dev, const struct ssam_event *event) 257fc00bc8aSMaximilian Luz { 258fc00bc8aSMaximilian Luz union acpi_object payload; 259fc00bc8aSMaximilian Luz 260fc00bc8aSMaximilian Luz /* 261fc00bc8aSMaximilian Luz * The Surface ACPI expects a buffer and not a package. It specifically 262fc00bc8aSMaximilian Luz * checks for ObjectType (Arg3) == 0x03. This will cause a warning in 263fc00bc8aSMaximilian Luz * acpica/nsarguments.c, but that warning can be safely ignored. 264fc00bc8aSMaximilian Luz */ 265fc00bc8aSMaximilian Luz payload.type = ACPI_TYPE_BUFFER; 266fc00bc8aSMaximilian Luz payload.buffer.length = event->length; 267fc00bc8aSMaximilian Luz payload.buffer.pointer = (u8 *)&event->data[0]; 268fc00bc8aSMaximilian Luz 269fc00bc8aSMaximilian Luz return san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_DPTF, &payload); 270fc00bc8aSMaximilian Luz } 271fc00bc8aSMaximilian Luz 272fc00bc8aSMaximilian Luz static unsigned long san_evt_bat_delay(u8 cid) 273fc00bc8aSMaximilian Luz { 274fc00bc8aSMaximilian Luz switch (cid) { 275fc00bc8aSMaximilian Luz case SAM_EVENT_CID_BAT_ADP: 276fc00bc8aSMaximilian Luz /* 277fc00bc8aSMaximilian Luz * Wait for battery state to update before signaling adapter 278fc00bc8aSMaximilian Luz * change. 279fc00bc8aSMaximilian Luz */ 280fc00bc8aSMaximilian Luz return msecs_to_jiffies(5000); 281fc00bc8aSMaximilian Luz 282fc00bc8aSMaximilian Luz case SAM_EVENT_CID_BAT_BST: 283fc00bc8aSMaximilian Luz /* Ensure we do not miss anything important due to caching. */ 284fc00bc8aSMaximilian Luz return msecs_to_jiffies(2000); 285fc00bc8aSMaximilian Luz 286fc00bc8aSMaximilian Luz default: 287fc00bc8aSMaximilian Luz return 0; 288fc00bc8aSMaximilian Luz } 289fc00bc8aSMaximilian Luz } 290fc00bc8aSMaximilian Luz 291fc00bc8aSMaximilian Luz static bool san_evt_bat(const struct ssam_event *event, struct device *dev) 292fc00bc8aSMaximilian Luz { 293fc00bc8aSMaximilian Luz int status; 294fc00bc8aSMaximilian Luz 295fc00bc8aSMaximilian Luz switch (event->command_id) { 296fc00bc8aSMaximilian Luz case SAM_EVENT_CID_BAT_BIX: 297fc00bc8aSMaximilian Luz status = san_evt_bat_bix(dev, event); 298fc00bc8aSMaximilian Luz break; 299fc00bc8aSMaximilian Luz 300fc00bc8aSMaximilian Luz case SAM_EVENT_CID_BAT_BST: 301fc00bc8aSMaximilian Luz status = san_evt_bat_bst(dev, event); 302fc00bc8aSMaximilian Luz break; 303fc00bc8aSMaximilian Luz 304fc00bc8aSMaximilian Luz case SAM_EVENT_CID_BAT_ADP: 305fc00bc8aSMaximilian Luz status = san_evt_bat_adp(dev, event); 306fc00bc8aSMaximilian Luz break; 307fc00bc8aSMaximilian Luz 308fc00bc8aSMaximilian Luz case SAM_EVENT_CID_BAT_PROT: 309fc00bc8aSMaximilian Luz /* 310fc00bc8aSMaximilian Luz * TODO: Implement support for battery protection status change 311fc00bc8aSMaximilian Luz * event. 312fc00bc8aSMaximilian Luz */ 313fc00bc8aSMaximilian Luz return true; 314fc00bc8aSMaximilian Luz 315fc00bc8aSMaximilian Luz case SAM_EVENT_CID_BAT_DPTF: 316fc00bc8aSMaximilian Luz status = san_evt_bat_dptf(dev, event); 317fc00bc8aSMaximilian Luz break; 318fc00bc8aSMaximilian Luz 319fc00bc8aSMaximilian Luz default: 320fc00bc8aSMaximilian Luz return false; 321fc00bc8aSMaximilian Luz } 322fc00bc8aSMaximilian Luz 323fc00bc8aSMaximilian Luz if (status) { 324fc00bc8aSMaximilian Luz dev_err(dev, "error handling power event (cid = %#04x)\n", 325fc00bc8aSMaximilian Luz event->command_id); 326fc00bc8aSMaximilian Luz } 327fc00bc8aSMaximilian Luz 328fc00bc8aSMaximilian Luz return true; 329fc00bc8aSMaximilian Luz } 330fc00bc8aSMaximilian Luz 331fc00bc8aSMaximilian Luz static void san_evt_bat_workfn(struct work_struct *work) 332fc00bc8aSMaximilian Luz { 333fc00bc8aSMaximilian Luz struct san_event_work *ev; 334fc00bc8aSMaximilian Luz 335fc00bc8aSMaximilian Luz ev = container_of(work, struct san_event_work, work.work); 336fc00bc8aSMaximilian Luz san_evt_bat(&ev->event, ev->dev); 337fc00bc8aSMaximilian Luz kfree(ev); 338fc00bc8aSMaximilian Luz } 339fc00bc8aSMaximilian Luz 340fc00bc8aSMaximilian Luz static u32 san_evt_bat_nf(struct ssam_event_notifier *nf, 341fc00bc8aSMaximilian Luz const struct ssam_event *event) 342fc00bc8aSMaximilian Luz { 343fc00bc8aSMaximilian Luz struct san_data *d = to_san_data(nf, nf_bat); 344fc00bc8aSMaximilian Luz struct san_event_work *work; 345fc00bc8aSMaximilian Luz unsigned long delay = san_evt_bat_delay(event->command_id); 346fc00bc8aSMaximilian Luz 347fc00bc8aSMaximilian Luz if (delay == 0) 348fc00bc8aSMaximilian Luz return san_evt_bat(event, d->dev) ? SSAM_NOTIF_HANDLED : 0; 349fc00bc8aSMaximilian Luz 350fc00bc8aSMaximilian Luz work = kzalloc(sizeof(*work) + event->length, GFP_KERNEL); 351fc00bc8aSMaximilian Luz if (!work) 352fc00bc8aSMaximilian Luz return ssam_notifier_from_errno(-ENOMEM); 353fc00bc8aSMaximilian Luz 354fc00bc8aSMaximilian Luz INIT_DELAYED_WORK(&work->work, san_evt_bat_workfn); 355fc00bc8aSMaximilian Luz work->dev = d->dev; 356fc00bc8aSMaximilian Luz 357fc00bc8aSMaximilian Luz memcpy(&work->event, event, sizeof(struct ssam_event) + event->length); 358fc00bc8aSMaximilian Luz 359fc00bc8aSMaximilian Luz schedule_delayed_work(&work->work, delay); 360fc00bc8aSMaximilian Luz return SSAM_NOTIF_HANDLED; 361fc00bc8aSMaximilian Luz } 362fc00bc8aSMaximilian Luz 363fc00bc8aSMaximilian Luz static int san_evt_tmp_trip(struct device *dev, const struct ssam_event *event) 364fc00bc8aSMaximilian Luz { 365fc00bc8aSMaximilian Luz union acpi_object param; 366fc00bc8aSMaximilian Luz 367fc00bc8aSMaximilian Luz /* 368fc00bc8aSMaximilian Luz * The Surface ACPI expects an integer and not a package. This will 369fc00bc8aSMaximilian Luz * cause a warning in acpica/nsarguments.c, but that warning can be 370fc00bc8aSMaximilian Luz * safely ignored. 371fc00bc8aSMaximilian Luz */ 372fc00bc8aSMaximilian Luz param.type = ACPI_TYPE_INTEGER; 373fc00bc8aSMaximilian Luz param.integer.value = event->instance_id; 374fc00bc8aSMaximilian Luz 375fc00bc8aSMaximilian Luz return san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_THERMAL, ¶m); 376fc00bc8aSMaximilian Luz } 377fc00bc8aSMaximilian Luz 378fc00bc8aSMaximilian Luz static bool san_evt_tmp(const struct ssam_event *event, struct device *dev) 379fc00bc8aSMaximilian Luz { 380fc00bc8aSMaximilian Luz int status; 381fc00bc8aSMaximilian Luz 382fc00bc8aSMaximilian Luz switch (event->command_id) { 383fc00bc8aSMaximilian Luz case SAM_EVENT_CID_TMP_TRIP: 384fc00bc8aSMaximilian Luz status = san_evt_tmp_trip(dev, event); 385fc00bc8aSMaximilian Luz break; 386fc00bc8aSMaximilian Luz 387fc00bc8aSMaximilian Luz default: 388fc00bc8aSMaximilian Luz return false; 389fc00bc8aSMaximilian Luz } 390fc00bc8aSMaximilian Luz 391fc00bc8aSMaximilian Luz if (status) { 392fc00bc8aSMaximilian Luz dev_err(dev, "error handling thermal event (cid = %#04x)\n", 393fc00bc8aSMaximilian Luz event->command_id); 394fc00bc8aSMaximilian Luz } 395fc00bc8aSMaximilian Luz 396fc00bc8aSMaximilian Luz return true; 397fc00bc8aSMaximilian Luz } 398fc00bc8aSMaximilian Luz 399fc00bc8aSMaximilian Luz static u32 san_evt_tmp_nf(struct ssam_event_notifier *nf, 400fc00bc8aSMaximilian Luz const struct ssam_event *event) 401fc00bc8aSMaximilian Luz { 402fc00bc8aSMaximilian Luz struct san_data *d = to_san_data(nf, nf_tmp); 403fc00bc8aSMaximilian Luz 404fc00bc8aSMaximilian Luz return san_evt_tmp(event, d->dev) ? SSAM_NOTIF_HANDLED : 0; 405fc00bc8aSMaximilian Luz } 406fc00bc8aSMaximilian Luz 407fc00bc8aSMaximilian Luz 408fc00bc8aSMaximilian Luz /* -- ACPI GSB OperationRegion handler -------------------------------------- */ 409fc00bc8aSMaximilian Luz 410fc00bc8aSMaximilian Luz struct gsb_data_in { 411fc00bc8aSMaximilian Luz u8 cv; 412fc00bc8aSMaximilian Luz } __packed; 413fc00bc8aSMaximilian Luz 414fc00bc8aSMaximilian Luz struct gsb_data_rqsx { 415fc00bc8aSMaximilian Luz u8 cv; /* Command value (san_gsb_request_cv). */ 416fc00bc8aSMaximilian Luz u8 tc; /* Target category. */ 417fc00bc8aSMaximilian Luz u8 tid; /* Target ID. */ 418fc00bc8aSMaximilian Luz u8 iid; /* Instance ID. */ 419fc00bc8aSMaximilian Luz u8 snc; /* Expect-response-flag. */ 420fc00bc8aSMaximilian Luz u8 cid; /* Command ID. */ 421fc00bc8aSMaximilian Luz u16 cdl; /* Payload length. */ 422fc00bc8aSMaximilian Luz u8 pld[]; /* Payload. */ 423fc00bc8aSMaximilian Luz } __packed; 424fc00bc8aSMaximilian Luz 425fc00bc8aSMaximilian Luz struct gsb_data_etwl { 426fc00bc8aSMaximilian Luz u8 cv; /* Command value (should be 0x02). */ 427fc00bc8aSMaximilian Luz u8 etw3; /* Unknown. */ 428fc00bc8aSMaximilian Luz u8 etw4; /* Unknown. */ 429fc00bc8aSMaximilian Luz u8 msg[]; /* Error message (ASCIIZ). */ 430fc00bc8aSMaximilian Luz } __packed; 431fc00bc8aSMaximilian Luz 432fc00bc8aSMaximilian Luz struct gsb_data_out { 433fc00bc8aSMaximilian Luz u8 status; /* _SSH communication status. */ 434fc00bc8aSMaximilian Luz u8 len; /* _SSH payload length. */ 435fc00bc8aSMaximilian Luz u8 pld[]; /* _SSH payload. */ 436fc00bc8aSMaximilian Luz } __packed; 437fc00bc8aSMaximilian Luz 438fc00bc8aSMaximilian Luz union gsb_buffer_data { 439fc00bc8aSMaximilian Luz struct gsb_data_in in; /* Common input. */ 440fc00bc8aSMaximilian Luz struct gsb_data_rqsx rqsx; /* RQSX input. */ 441fc00bc8aSMaximilian Luz struct gsb_data_etwl etwl; /* ETWL input. */ 442fc00bc8aSMaximilian Luz struct gsb_data_out out; /* Output. */ 443fc00bc8aSMaximilian Luz }; 444fc00bc8aSMaximilian Luz 445fc00bc8aSMaximilian Luz struct gsb_buffer { 446fc00bc8aSMaximilian Luz u8 status; /* GSB AttribRawProcess status. */ 447fc00bc8aSMaximilian Luz u8 len; /* GSB AttribRawProcess length. */ 448fc00bc8aSMaximilian Luz union gsb_buffer_data data; 449fc00bc8aSMaximilian Luz } __packed; 450fc00bc8aSMaximilian Luz 451fc00bc8aSMaximilian Luz #define SAN_GSB_MAX_RQSX_PAYLOAD (U8_MAX - 2 - sizeof(struct gsb_data_rqsx)) 452fc00bc8aSMaximilian Luz #define SAN_GSB_MAX_RESPONSE (U8_MAX - 2 - sizeof(struct gsb_data_out)) 453fc00bc8aSMaximilian Luz 454fc00bc8aSMaximilian Luz #define SAN_GSB_COMMAND 0 455fc00bc8aSMaximilian Luz 456fc00bc8aSMaximilian Luz enum san_gsb_request_cv { 457fc00bc8aSMaximilian Luz SAN_GSB_REQUEST_CV_RQST = 0x01, 458fc00bc8aSMaximilian Luz SAN_GSB_REQUEST_CV_ETWL = 0x02, 459fc00bc8aSMaximilian Luz SAN_GSB_REQUEST_CV_RQSG = 0x03, 460fc00bc8aSMaximilian Luz }; 461fc00bc8aSMaximilian Luz 462fc00bc8aSMaximilian Luz #define SAN_REQUEST_NUM_TRIES 5 463fc00bc8aSMaximilian Luz 464fc00bc8aSMaximilian Luz static acpi_status san_etwl(struct san_data *d, struct gsb_buffer *b) 465fc00bc8aSMaximilian Luz { 466fc00bc8aSMaximilian Luz struct gsb_data_etwl *etwl = &b->data.etwl; 467fc00bc8aSMaximilian Luz 468fc00bc8aSMaximilian Luz if (b->len < sizeof(struct gsb_data_etwl)) { 469fc00bc8aSMaximilian Luz dev_err(d->dev, "invalid ETWL package (len = %d)\n", b->len); 470fc00bc8aSMaximilian Luz return AE_OK; 471fc00bc8aSMaximilian Luz } 472fc00bc8aSMaximilian Luz 473fc00bc8aSMaximilian Luz dev_err(d->dev, "ETWL(%#04x, %#04x): %.*s\n", etwl->etw3, etwl->etw4, 474fc00bc8aSMaximilian Luz (unsigned int)(b->len - sizeof(struct gsb_data_etwl)), 475fc00bc8aSMaximilian Luz (char *)etwl->msg); 476fc00bc8aSMaximilian Luz 477fc00bc8aSMaximilian Luz /* Indicate success. */ 478fc00bc8aSMaximilian Luz b->status = 0x00; 479fc00bc8aSMaximilian Luz b->len = 0x00; 480fc00bc8aSMaximilian Luz 481fc00bc8aSMaximilian Luz return AE_OK; 482fc00bc8aSMaximilian Luz } 483fc00bc8aSMaximilian Luz 484fc00bc8aSMaximilian Luz static 485fc00bc8aSMaximilian Luz struct gsb_data_rqsx *san_validate_rqsx(struct device *dev, const char *type, 486fc00bc8aSMaximilian Luz struct gsb_buffer *b) 487fc00bc8aSMaximilian Luz { 488fc00bc8aSMaximilian Luz struct gsb_data_rqsx *rqsx = &b->data.rqsx; 489fc00bc8aSMaximilian Luz 490fc00bc8aSMaximilian Luz if (b->len < sizeof(struct gsb_data_rqsx)) { 491fc00bc8aSMaximilian Luz dev_err(dev, "invalid %s package (len = %d)\n", type, b->len); 492fc00bc8aSMaximilian Luz return NULL; 493fc00bc8aSMaximilian Luz } 494fc00bc8aSMaximilian Luz 495fc00bc8aSMaximilian Luz if (get_unaligned(&rqsx->cdl) != b->len - sizeof(struct gsb_data_rqsx)) { 496fc00bc8aSMaximilian Luz dev_err(dev, "bogus %s package (len = %d, cdl = %d)\n", 497fc00bc8aSMaximilian Luz type, b->len, get_unaligned(&rqsx->cdl)); 498fc00bc8aSMaximilian Luz return NULL; 499fc00bc8aSMaximilian Luz } 500fc00bc8aSMaximilian Luz 501fc00bc8aSMaximilian Luz if (get_unaligned(&rqsx->cdl) > SAN_GSB_MAX_RQSX_PAYLOAD) { 502fc00bc8aSMaximilian Luz dev_err(dev, "payload for %s package too large (cdl = %d)\n", 503fc00bc8aSMaximilian Luz type, get_unaligned(&rqsx->cdl)); 504fc00bc8aSMaximilian Luz return NULL; 505fc00bc8aSMaximilian Luz } 506fc00bc8aSMaximilian Luz 507fc00bc8aSMaximilian Luz return rqsx; 508fc00bc8aSMaximilian Luz } 509fc00bc8aSMaximilian Luz 510fc00bc8aSMaximilian Luz static void gsb_rqsx_response_error(struct gsb_buffer *gsb, int status) 511fc00bc8aSMaximilian Luz { 512fc00bc8aSMaximilian Luz gsb->status = 0x00; 513fc00bc8aSMaximilian Luz gsb->len = 0x02; 514fc00bc8aSMaximilian Luz gsb->data.out.status = (u8)(-status); 515fc00bc8aSMaximilian Luz gsb->data.out.len = 0x00; 516fc00bc8aSMaximilian Luz } 517fc00bc8aSMaximilian Luz 518fc00bc8aSMaximilian Luz static void gsb_rqsx_response_success(struct gsb_buffer *gsb, u8 *ptr, size_t len) 519fc00bc8aSMaximilian Luz { 520fc00bc8aSMaximilian Luz gsb->status = 0x00; 521fc00bc8aSMaximilian Luz gsb->len = len + 2; 522fc00bc8aSMaximilian Luz gsb->data.out.status = 0x00; 523fc00bc8aSMaximilian Luz gsb->data.out.len = len; 524fc00bc8aSMaximilian Luz 525fc00bc8aSMaximilian Luz if (len) 526fc00bc8aSMaximilian Luz memcpy(&gsb->data.out.pld[0], ptr, len); 527fc00bc8aSMaximilian Luz } 528fc00bc8aSMaximilian Luz 529fc00bc8aSMaximilian Luz static acpi_status san_rqst_fixup_suspended(struct san_data *d, 530fc00bc8aSMaximilian Luz struct ssam_request *rqst, 531fc00bc8aSMaximilian Luz struct gsb_buffer *gsb) 532fc00bc8aSMaximilian Luz { 533fc00bc8aSMaximilian Luz if (rqst->target_category == SSAM_SSH_TC_BAS && rqst->command_id == 0x0D) { 534fc00bc8aSMaximilian Luz u8 base_state = 1; 535fc00bc8aSMaximilian Luz 536fc00bc8aSMaximilian Luz /* Base state quirk: 537fc00bc8aSMaximilian Luz * The base state may be queried from ACPI when the EC is still 538fc00bc8aSMaximilian Luz * suspended. In this case it will return '-EPERM'. This query 539fc00bc8aSMaximilian Luz * will only be triggered from the ACPI lid GPE interrupt, thus 540fc00bc8aSMaximilian Luz * we are either in laptop or studio mode (base status 0x01 or 541fc00bc8aSMaximilian Luz * 0x02). Furthermore, we will only get here if the device (and 542fc00bc8aSMaximilian Luz * EC) have been suspended. 543fc00bc8aSMaximilian Luz * 544fc00bc8aSMaximilian Luz * We now assume that the device is in laptop mode (0x01). This 545fc00bc8aSMaximilian Luz * has the drawback that it will wake the device when unfolding 546fc00bc8aSMaximilian Luz * it in studio mode, but it also allows us to avoid actively 547fc00bc8aSMaximilian Luz * waiting for the EC to wake up, which may incur a notable 548fc00bc8aSMaximilian Luz * delay. 549fc00bc8aSMaximilian Luz */ 550fc00bc8aSMaximilian Luz 551fc00bc8aSMaximilian Luz dev_dbg(d->dev, "rqst: fixup: base-state quirk\n"); 552fc00bc8aSMaximilian Luz 553fc00bc8aSMaximilian Luz gsb_rqsx_response_success(gsb, &base_state, sizeof(base_state)); 554fc00bc8aSMaximilian Luz return AE_OK; 555fc00bc8aSMaximilian Luz } 556fc00bc8aSMaximilian Luz 557fc00bc8aSMaximilian Luz gsb_rqsx_response_error(gsb, -ENXIO); 558fc00bc8aSMaximilian Luz return AE_OK; 559fc00bc8aSMaximilian Luz } 560fc00bc8aSMaximilian Luz 561fc00bc8aSMaximilian Luz static acpi_status san_rqst(struct san_data *d, struct gsb_buffer *buffer) 562fc00bc8aSMaximilian Luz { 563fc00bc8aSMaximilian Luz u8 rspbuf[SAN_GSB_MAX_RESPONSE]; 564fc00bc8aSMaximilian Luz struct gsb_data_rqsx *gsb_rqst; 565fc00bc8aSMaximilian Luz struct ssam_request rqst; 566fc00bc8aSMaximilian Luz struct ssam_response rsp; 567fc00bc8aSMaximilian Luz int status = 0; 568fc00bc8aSMaximilian Luz 569fc00bc8aSMaximilian Luz gsb_rqst = san_validate_rqsx(d->dev, "RQST", buffer); 570fc00bc8aSMaximilian Luz if (!gsb_rqst) 571fc00bc8aSMaximilian Luz return AE_OK; 572fc00bc8aSMaximilian Luz 573fc00bc8aSMaximilian Luz rqst.target_category = gsb_rqst->tc; 574fc00bc8aSMaximilian Luz rqst.target_id = gsb_rqst->tid; 575fc00bc8aSMaximilian Luz rqst.command_id = gsb_rqst->cid; 576fc00bc8aSMaximilian Luz rqst.instance_id = gsb_rqst->iid; 577fc00bc8aSMaximilian Luz rqst.flags = gsb_rqst->snc ? SSAM_REQUEST_HAS_RESPONSE : 0; 578fc00bc8aSMaximilian Luz rqst.length = get_unaligned(&gsb_rqst->cdl); 579fc00bc8aSMaximilian Luz rqst.payload = &gsb_rqst->pld[0]; 580fc00bc8aSMaximilian Luz 581fc00bc8aSMaximilian Luz rsp.capacity = ARRAY_SIZE(rspbuf); 582fc00bc8aSMaximilian Luz rsp.length = 0; 583fc00bc8aSMaximilian Luz rsp.pointer = &rspbuf[0]; 584fc00bc8aSMaximilian Luz 585fc00bc8aSMaximilian Luz /* Handle suspended device. */ 586fc00bc8aSMaximilian Luz if (d->dev->power.is_suspended) { 587fc00bc8aSMaximilian Luz dev_warn(d->dev, "rqst: device is suspended, not executing\n"); 588fc00bc8aSMaximilian Luz return san_rqst_fixup_suspended(d, &rqst, buffer); 589fc00bc8aSMaximilian Luz } 590fc00bc8aSMaximilian Luz 591fc00bc8aSMaximilian Luz status = __ssam_retry(ssam_request_sync_onstack, SAN_REQUEST_NUM_TRIES, 592fc00bc8aSMaximilian Luz d->ctrl, &rqst, &rsp, SAN_GSB_MAX_RQSX_PAYLOAD); 593fc00bc8aSMaximilian Luz 594fc00bc8aSMaximilian Luz if (!status) { 595fc00bc8aSMaximilian Luz gsb_rqsx_response_success(buffer, rsp.pointer, rsp.length); 596fc00bc8aSMaximilian Luz } else { 597fc00bc8aSMaximilian Luz dev_err(d->dev, "rqst: failed with error %d\n", status); 598fc00bc8aSMaximilian Luz gsb_rqsx_response_error(buffer, status); 599fc00bc8aSMaximilian Luz } 600fc00bc8aSMaximilian Luz 601fc00bc8aSMaximilian Luz return AE_OK; 602fc00bc8aSMaximilian Luz } 603fc00bc8aSMaximilian Luz 604fc00bc8aSMaximilian Luz static acpi_status san_rqsg(struct san_data *d, struct gsb_buffer *buffer) 605fc00bc8aSMaximilian Luz { 606fc00bc8aSMaximilian Luz struct gsb_data_rqsx *gsb_rqsg; 607fc00bc8aSMaximilian Luz struct san_dgpu_event evt; 608fc00bc8aSMaximilian Luz int status; 609fc00bc8aSMaximilian Luz 610fc00bc8aSMaximilian Luz gsb_rqsg = san_validate_rqsx(d->dev, "RQSG", buffer); 611fc00bc8aSMaximilian Luz if (!gsb_rqsg) 612fc00bc8aSMaximilian Luz return AE_OK; 613fc00bc8aSMaximilian Luz 614fc00bc8aSMaximilian Luz evt.category = gsb_rqsg->tc; 615fc00bc8aSMaximilian Luz evt.target = gsb_rqsg->tid; 616fc00bc8aSMaximilian Luz evt.command = gsb_rqsg->cid; 617fc00bc8aSMaximilian Luz evt.instance = gsb_rqsg->iid; 618fc00bc8aSMaximilian Luz evt.length = get_unaligned(&gsb_rqsg->cdl); 619fc00bc8aSMaximilian Luz evt.payload = &gsb_rqsg->pld[0]; 620fc00bc8aSMaximilian Luz 621fc00bc8aSMaximilian Luz status = san_dgpu_notifier_call(&evt); 622fc00bc8aSMaximilian Luz if (!status) { 623fc00bc8aSMaximilian Luz gsb_rqsx_response_success(buffer, NULL, 0); 624fc00bc8aSMaximilian Luz } else { 625fc00bc8aSMaximilian Luz dev_err(d->dev, "rqsg: failed with error %d\n", status); 626fc00bc8aSMaximilian Luz gsb_rqsx_response_error(buffer, status); 627fc00bc8aSMaximilian Luz } 628fc00bc8aSMaximilian Luz 629fc00bc8aSMaximilian Luz return AE_OK; 630fc00bc8aSMaximilian Luz } 631fc00bc8aSMaximilian Luz 632fc00bc8aSMaximilian Luz static acpi_status san_opreg_handler(u32 function, acpi_physical_address command, 633fc00bc8aSMaximilian Luz u32 bits, u64 *value64, void *opreg_context, 634fc00bc8aSMaximilian Luz void *region_context) 635fc00bc8aSMaximilian Luz { 636fc00bc8aSMaximilian Luz struct san_data *d = to_san_data(opreg_context, info); 637fc00bc8aSMaximilian Luz struct gsb_buffer *buffer = (struct gsb_buffer *)value64; 638fc00bc8aSMaximilian Luz int accessor_type = (function & 0xFFFF0000) >> 16; 639fc00bc8aSMaximilian Luz 640fc00bc8aSMaximilian Luz if (command != SAN_GSB_COMMAND) { 641fc00bc8aSMaximilian Luz dev_warn(d->dev, "unsupported command: %#04llx\n", command); 642fc00bc8aSMaximilian Luz return AE_OK; 643fc00bc8aSMaximilian Luz } 644fc00bc8aSMaximilian Luz 645fc00bc8aSMaximilian Luz if (accessor_type != ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS) { 646fc00bc8aSMaximilian Luz dev_err(d->dev, "invalid access type: %#04x\n", accessor_type); 647fc00bc8aSMaximilian Luz return AE_OK; 648fc00bc8aSMaximilian Luz } 649fc00bc8aSMaximilian Luz 650fc00bc8aSMaximilian Luz /* Buffer must have at least contain the command-value. */ 651fc00bc8aSMaximilian Luz if (buffer->len == 0) { 652fc00bc8aSMaximilian Luz dev_err(d->dev, "request-package too small\n"); 653fc00bc8aSMaximilian Luz return AE_OK; 654fc00bc8aSMaximilian Luz } 655fc00bc8aSMaximilian Luz 656fc00bc8aSMaximilian Luz switch (buffer->data.in.cv) { 657fc00bc8aSMaximilian Luz case SAN_GSB_REQUEST_CV_RQST: 658fc00bc8aSMaximilian Luz return san_rqst(d, buffer); 659fc00bc8aSMaximilian Luz 660fc00bc8aSMaximilian Luz case SAN_GSB_REQUEST_CV_ETWL: 661fc00bc8aSMaximilian Luz return san_etwl(d, buffer); 662fc00bc8aSMaximilian Luz 663fc00bc8aSMaximilian Luz case SAN_GSB_REQUEST_CV_RQSG: 664fc00bc8aSMaximilian Luz return san_rqsg(d, buffer); 665fc00bc8aSMaximilian Luz 666fc00bc8aSMaximilian Luz default: 667fc00bc8aSMaximilian Luz dev_warn(d->dev, "unsupported SAN0 request (cv: %#04x)\n", 668fc00bc8aSMaximilian Luz buffer->data.in.cv); 669fc00bc8aSMaximilian Luz return AE_OK; 670fc00bc8aSMaximilian Luz } 671fc00bc8aSMaximilian Luz } 672fc00bc8aSMaximilian Luz 673fc00bc8aSMaximilian Luz 674fc00bc8aSMaximilian Luz /* -- Driver setup. --------------------------------------------------------- */ 675fc00bc8aSMaximilian Luz 676fc00bc8aSMaximilian Luz static int san_events_register(struct platform_device *pdev) 677fc00bc8aSMaximilian Luz { 678fc00bc8aSMaximilian Luz struct san_data *d = platform_get_drvdata(pdev); 679fc00bc8aSMaximilian Luz int status; 680fc00bc8aSMaximilian Luz 681fc00bc8aSMaximilian Luz d->nf_bat.base.priority = 1; 682fc00bc8aSMaximilian Luz d->nf_bat.base.fn = san_evt_bat_nf; 683fc00bc8aSMaximilian Luz d->nf_bat.event.reg = SSAM_EVENT_REGISTRY_SAM; 684fc00bc8aSMaximilian Luz d->nf_bat.event.id.target_category = SSAM_SSH_TC_BAT; 685fc00bc8aSMaximilian Luz d->nf_bat.event.id.instance = 0; 686fc00bc8aSMaximilian Luz d->nf_bat.event.mask = SSAM_EVENT_MASK_TARGET; 687fc00bc8aSMaximilian Luz d->nf_bat.event.flags = SSAM_EVENT_SEQUENCED; 688fc00bc8aSMaximilian Luz 689fc00bc8aSMaximilian Luz d->nf_tmp.base.priority = 1; 690fc00bc8aSMaximilian Luz d->nf_tmp.base.fn = san_evt_tmp_nf; 691fc00bc8aSMaximilian Luz d->nf_tmp.event.reg = SSAM_EVENT_REGISTRY_SAM; 692fc00bc8aSMaximilian Luz d->nf_tmp.event.id.target_category = SSAM_SSH_TC_TMP; 693fc00bc8aSMaximilian Luz d->nf_tmp.event.id.instance = 0; 694fc00bc8aSMaximilian Luz d->nf_tmp.event.mask = SSAM_EVENT_MASK_TARGET; 695fc00bc8aSMaximilian Luz d->nf_tmp.event.flags = SSAM_EVENT_SEQUENCED; 696fc00bc8aSMaximilian Luz 697fc00bc8aSMaximilian Luz status = ssam_notifier_register(d->ctrl, &d->nf_bat); 698fc00bc8aSMaximilian Luz if (status) 699fc00bc8aSMaximilian Luz return status; 700fc00bc8aSMaximilian Luz 701fc00bc8aSMaximilian Luz status = ssam_notifier_register(d->ctrl, &d->nf_tmp); 702fc00bc8aSMaximilian Luz if (status) 703fc00bc8aSMaximilian Luz ssam_notifier_unregister(d->ctrl, &d->nf_bat); 704fc00bc8aSMaximilian Luz 705fc00bc8aSMaximilian Luz return status; 706fc00bc8aSMaximilian Luz } 707fc00bc8aSMaximilian Luz 708fc00bc8aSMaximilian Luz static void san_events_unregister(struct platform_device *pdev) 709fc00bc8aSMaximilian Luz { 710fc00bc8aSMaximilian Luz struct san_data *d = platform_get_drvdata(pdev); 711fc00bc8aSMaximilian Luz 712fc00bc8aSMaximilian Luz ssam_notifier_unregister(d->ctrl, &d->nf_bat); 713fc00bc8aSMaximilian Luz ssam_notifier_unregister(d->ctrl, &d->nf_tmp); 714fc00bc8aSMaximilian Luz } 715fc00bc8aSMaximilian Luz 716fc00bc8aSMaximilian Luz #define san_consumer_printk(level, dev, handle, fmt, ...) \ 717fc00bc8aSMaximilian Luz do { \ 718fc00bc8aSMaximilian Luz char *path = "<error getting consumer path>"; \ 719fc00bc8aSMaximilian Luz struct acpi_buffer buffer = { \ 720fc00bc8aSMaximilian Luz .length = ACPI_ALLOCATE_BUFFER, \ 721fc00bc8aSMaximilian Luz .pointer = NULL, \ 722fc00bc8aSMaximilian Luz }; \ 723fc00bc8aSMaximilian Luz \ 724fc00bc8aSMaximilian Luz if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer))) \ 725fc00bc8aSMaximilian Luz path = buffer.pointer; \ 726fc00bc8aSMaximilian Luz \ 727fc00bc8aSMaximilian Luz dev_##level(dev, "[%s]: " fmt, path, ##__VA_ARGS__); \ 728fc00bc8aSMaximilian Luz kfree(buffer.pointer); \ 729fc00bc8aSMaximilian Luz } while (0) 730fc00bc8aSMaximilian Luz 731fc00bc8aSMaximilian Luz #define san_consumer_dbg(dev, handle, fmt, ...) \ 732fc00bc8aSMaximilian Luz san_consumer_printk(dbg, dev, handle, fmt, ##__VA_ARGS__) 733fc00bc8aSMaximilian Luz 734fc00bc8aSMaximilian Luz #define san_consumer_warn(dev, handle, fmt, ...) \ 735fc00bc8aSMaximilian Luz san_consumer_printk(warn, dev, handle, fmt, ##__VA_ARGS__) 736fc00bc8aSMaximilian Luz 737fc00bc8aSMaximilian Luz static bool is_san_consumer(struct platform_device *pdev, acpi_handle handle) 738fc00bc8aSMaximilian Luz { 739fc00bc8aSMaximilian Luz struct acpi_handle_list dep_devices; 740fc00bc8aSMaximilian Luz acpi_handle supplier = ACPI_HANDLE(&pdev->dev); 741fc00bc8aSMaximilian Luz acpi_status status; 742fc00bc8aSMaximilian Luz int i; 743fc00bc8aSMaximilian Luz 744fc00bc8aSMaximilian Luz if (!acpi_has_method(handle, "_DEP")) 745fc00bc8aSMaximilian Luz return false; 746fc00bc8aSMaximilian Luz 747fc00bc8aSMaximilian Luz status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices); 748fc00bc8aSMaximilian Luz if (ACPI_FAILURE(status)) { 749fc00bc8aSMaximilian Luz san_consumer_dbg(&pdev->dev, handle, "failed to evaluate _DEP\n"); 750fc00bc8aSMaximilian Luz return false; 751fc00bc8aSMaximilian Luz } 752fc00bc8aSMaximilian Luz 753fc00bc8aSMaximilian Luz for (i = 0; i < dep_devices.count; i++) { 754fc00bc8aSMaximilian Luz if (dep_devices.handles[i] == supplier) 755fc00bc8aSMaximilian Luz return true; 756fc00bc8aSMaximilian Luz } 757fc00bc8aSMaximilian Luz 758fc00bc8aSMaximilian Luz return false; 759fc00bc8aSMaximilian Luz } 760fc00bc8aSMaximilian Luz 761fc00bc8aSMaximilian Luz static acpi_status san_consumer_setup(acpi_handle handle, u32 lvl, 762fc00bc8aSMaximilian Luz void *context, void **rv) 763fc00bc8aSMaximilian Luz { 764fc00bc8aSMaximilian Luz const u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER; 765fc00bc8aSMaximilian Luz struct platform_device *pdev = context; 766fc00bc8aSMaximilian Luz struct acpi_device *adev; 767fc00bc8aSMaximilian Luz struct device_link *link; 768fc00bc8aSMaximilian Luz 769fc00bc8aSMaximilian Luz if (!is_san_consumer(pdev, handle)) 770fc00bc8aSMaximilian Luz return AE_OK; 771fc00bc8aSMaximilian Luz 772fc00bc8aSMaximilian Luz /* Ignore ACPI devices that are not present. */ 773fc00bc8aSMaximilian Luz if (acpi_bus_get_device(handle, &adev) != 0) 774fc00bc8aSMaximilian Luz return AE_OK; 775fc00bc8aSMaximilian Luz 776fc00bc8aSMaximilian Luz san_consumer_dbg(&pdev->dev, handle, "creating device link\n"); 777fc00bc8aSMaximilian Luz 778fc00bc8aSMaximilian Luz /* Try to set up device links, ignore but log errors. */ 779fc00bc8aSMaximilian Luz link = device_link_add(&adev->dev, &pdev->dev, flags); 780fc00bc8aSMaximilian Luz if (!link) { 781fc00bc8aSMaximilian Luz san_consumer_warn(&pdev->dev, handle, "failed to create device link\n"); 782fc00bc8aSMaximilian Luz return AE_OK; 783fc00bc8aSMaximilian Luz } 784fc00bc8aSMaximilian Luz 785fc00bc8aSMaximilian Luz return AE_OK; 786fc00bc8aSMaximilian Luz } 787fc00bc8aSMaximilian Luz 788fc00bc8aSMaximilian Luz static int san_consumer_links_setup(struct platform_device *pdev) 789fc00bc8aSMaximilian Luz { 790fc00bc8aSMaximilian Luz acpi_status status; 791fc00bc8aSMaximilian Luz 792fc00bc8aSMaximilian Luz status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 793fc00bc8aSMaximilian Luz ACPI_UINT32_MAX, san_consumer_setup, NULL, 794fc00bc8aSMaximilian Luz pdev, NULL); 795fc00bc8aSMaximilian Luz 796fc00bc8aSMaximilian Luz return status ? -EFAULT : 0; 797fc00bc8aSMaximilian Luz } 798fc00bc8aSMaximilian Luz 799fc00bc8aSMaximilian Luz static int san_probe(struct platform_device *pdev) 800fc00bc8aSMaximilian Luz { 801fc00bc8aSMaximilian Luz acpi_handle san = ACPI_HANDLE(&pdev->dev); 802fc00bc8aSMaximilian Luz struct ssam_controller *ctrl; 803fc00bc8aSMaximilian Luz struct san_data *data; 804fc00bc8aSMaximilian Luz acpi_status astatus; 805fc00bc8aSMaximilian Luz int status; 806fc00bc8aSMaximilian Luz 807fc00bc8aSMaximilian Luz ctrl = ssam_client_bind(&pdev->dev); 808fc00bc8aSMaximilian Luz if (IS_ERR(ctrl)) 809fc00bc8aSMaximilian Luz return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl); 810fc00bc8aSMaximilian Luz 811fc00bc8aSMaximilian Luz status = san_consumer_links_setup(pdev); 812fc00bc8aSMaximilian Luz if (status) 813fc00bc8aSMaximilian Luz return status; 814fc00bc8aSMaximilian Luz 815fc00bc8aSMaximilian Luz data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 816fc00bc8aSMaximilian Luz if (!data) 817fc00bc8aSMaximilian Luz return -ENOMEM; 818fc00bc8aSMaximilian Luz 819fc00bc8aSMaximilian Luz data->dev = &pdev->dev; 820fc00bc8aSMaximilian Luz data->ctrl = ctrl; 821fc00bc8aSMaximilian Luz 822fc00bc8aSMaximilian Luz platform_set_drvdata(pdev, data); 823fc00bc8aSMaximilian Luz 824fc00bc8aSMaximilian Luz astatus = acpi_install_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, 825fc00bc8aSMaximilian Luz &san_opreg_handler, NULL, 826fc00bc8aSMaximilian Luz &data->info); 827fc00bc8aSMaximilian Luz if (ACPI_FAILURE(astatus)) 828fc00bc8aSMaximilian Luz return -ENXIO; 829fc00bc8aSMaximilian Luz 830fc00bc8aSMaximilian Luz status = san_events_register(pdev); 831fc00bc8aSMaximilian Luz if (status) 832fc00bc8aSMaximilian Luz goto err_enable_events; 833fc00bc8aSMaximilian Luz 834fc00bc8aSMaximilian Luz status = san_set_rqsg_interface_device(&pdev->dev); 835fc00bc8aSMaximilian Luz if (status) 836fc00bc8aSMaximilian Luz goto err_install_dev; 837fc00bc8aSMaximilian Luz 838fc00bc8aSMaximilian Luz acpi_walk_dep_device_list(san); 839fc00bc8aSMaximilian Luz return 0; 840fc00bc8aSMaximilian Luz 841fc00bc8aSMaximilian Luz err_install_dev: 842fc00bc8aSMaximilian Luz san_events_unregister(pdev); 843fc00bc8aSMaximilian Luz err_enable_events: 844fc00bc8aSMaximilian Luz acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, 845fc00bc8aSMaximilian Luz &san_opreg_handler); 846fc00bc8aSMaximilian Luz return status; 847fc00bc8aSMaximilian Luz } 848fc00bc8aSMaximilian Luz 849fc00bc8aSMaximilian Luz static int san_remove(struct platform_device *pdev) 850fc00bc8aSMaximilian Luz { 851fc00bc8aSMaximilian Luz acpi_handle san = ACPI_HANDLE(&pdev->dev); 852fc00bc8aSMaximilian Luz 853fc00bc8aSMaximilian Luz san_set_rqsg_interface_device(NULL); 854fc00bc8aSMaximilian Luz acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, 855fc00bc8aSMaximilian Luz &san_opreg_handler); 856fc00bc8aSMaximilian Luz san_events_unregister(pdev); 857fc00bc8aSMaximilian Luz 858fc00bc8aSMaximilian Luz /* 859fc00bc8aSMaximilian Luz * We have unregistered our event sources. Now we need to ensure that 860fc00bc8aSMaximilian Luz * all delayed works they may have spawned are run to completion. 861fc00bc8aSMaximilian Luz */ 862fc00bc8aSMaximilian Luz flush_scheduled_work(); 863fc00bc8aSMaximilian Luz 864fc00bc8aSMaximilian Luz return 0; 865fc00bc8aSMaximilian Luz } 866fc00bc8aSMaximilian Luz 867fc00bc8aSMaximilian Luz static const struct acpi_device_id san_match[] = { 868fc00bc8aSMaximilian Luz { "MSHW0091" }, 869fc00bc8aSMaximilian Luz { }, 870fc00bc8aSMaximilian Luz }; 871fc00bc8aSMaximilian Luz MODULE_DEVICE_TABLE(acpi, san_match); 872fc00bc8aSMaximilian Luz 873fc00bc8aSMaximilian Luz static struct platform_driver surface_acpi_notify = { 874fc00bc8aSMaximilian Luz .probe = san_probe, 875fc00bc8aSMaximilian Luz .remove = san_remove, 876fc00bc8aSMaximilian Luz .driver = { 877fc00bc8aSMaximilian Luz .name = "surface_acpi_notify", 878fc00bc8aSMaximilian Luz .acpi_match_table = san_match, 879fc00bc8aSMaximilian Luz .probe_type = PROBE_PREFER_ASYNCHRONOUS, 880fc00bc8aSMaximilian Luz }, 881fc00bc8aSMaximilian Luz }; 882fc00bc8aSMaximilian Luz module_platform_driver(surface_acpi_notify); 883fc00bc8aSMaximilian Luz 884fc00bc8aSMaximilian Luz MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>"); 885fc00bc8aSMaximilian Luz MODULE_DESCRIPTION("Surface ACPI Notify driver for Surface System Aggregator Module"); 886fc00bc8aSMaximilian Luz MODULE_LICENSE("GPL"); 887