From ad62cd93198bde6644ecb5ff8f6f4c5394b5821a Mon Sep 17 00:00:00 2001 From: Daniel Dadap Date: Thu, 2 Sep 2021 19:38:39 -0500 Subject: platform/x86: Add driver for ACPI WMAA EC-based backlight control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A number of upcoming notebook computer designs drive the internal display panel's backlight PWM through the Embedded Controller (EC). This EC-based backlight control can be plumbed through to an ACPI "WMAA" method interface, which in turn can be wrapped by WMI with the GUID handle 603E9613-EF25-4338-A3D0-C46177516DB7. Add a new driver, aliased to the WMAA WMI GUID, to expose a sysfs backlight class driver to control backlight levels on systems with EC-driven backlights. Signed-off-by: Daniel Dadap Reviewed-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20210903003838.15797-1-ddadap@nvidia.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index eeb4c70b3d5b..72837339b3ae 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20246,6 +20246,12 @@ L: linux-wireless@vger.kernel.org S: Odd fixes F: drivers/net/wireless/wl3501* +WMAA BACKLIGHT DRIVER +M: Daniel Dadap +L: platform-driver-x86@vger.kernel.org +S: Supported +F: drivers/platform/x86/wmaa-backlight-wmi.c + WOLFSON MICROELECTRONICS DRIVERS L: patches@opensource.cirrus.com S: Supported -- cgit v1.2.3 From 7b6bf51de9746b43ec56567a0297378cd0172946 Mon Sep 17 00:00:00 2001 From: "K Naduvalath, Sumesh" Date: Mon, 13 Sep 2021 10:40:56 +0530 Subject: platform/x86: Add Intel ishtp eclite driver This driver is for accessing the PSE (Programmable Service Engine) - an Embedded Controller like IP - using ISHTP (Integratd Sensor Hub Transport Protocol) to get battery, thermal and UCSI (USB Type-C Connector System Software Interface) related data from the platform. Signed-off-by: K Naduvalath, Sumesh Reviewed-by: Mark Gross Link: https://lore.kernel.org/r/20210913051056.28736-1-sumesh.k.naduvalath@intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- MAINTAINERS | 6 + drivers/platform/x86/intel/Kconfig | 16 + drivers/platform/x86/intel/Makefile | 1 + drivers/platform/x86/intel/ishtp_eclite.c | 701 ++++++++++++++++++++++++++++++ 4 files changed, 724 insertions(+) create mode 100644 drivers/platform/x86/intel/ishtp_eclite.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 72837339b3ae..949eb8ce6e72 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9479,6 +9479,12 @@ L: linux-crypto@vger.kernel.org S: Maintained F: drivers/crypto/ixp4xx_crypto.c +INTEL ISHTP ECLITE DRIVER +M: Sumesh K Naduvalath +L: platform-driver-x86@vger.kernel.org +S: Supported +F: drivers/platform/x86/intel/ishtp_eclite.c + INTEL IXP4XX QMGR, NPE, ETHERNET and HSS SUPPORT M: Krzysztof Halasa S: Maintained diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig index 0b21468e1bd0..38ce3e344589 100644 --- a/drivers/platform/x86/intel/Kconfig +++ b/drivers/platform/x86/intel/Kconfig @@ -102,6 +102,22 @@ config INTEL_CHTDC_TI_PWRBTN To compile this driver as a module, choose M here: the module will be called intel_chtdc_ti_pwrbtn. +config INTEL_ISHTP_ECLITE + tristate "Intel ISHTP eclite controller Driver" + depends on INTEL_ISH_HID + depends on ACPI + help + This driver is for accessing the PSE (Programmable Service Engine) - + an Embedded Controller like IP - using ISHTP (Integrated Sensor Hub + Transport Protocol) to get battery, thermal and UCSI (USB Type-C + Connector System Software Interface) related data from the platform. + Users who don't want to use discrete Embedded Controller on Intel's + Elkhartlake platform can leverage this integrated solution of + ECLite which is part of PSE subsystem. + + To compile this driver as a module, choose M here: the module + will be called intel_ishtp_eclite. + config INTEL_MRFLD_PWRBTN tristate "Intel Merrifield Basin Cove power button driver" depends on INTEL_SOC_PMIC_MRFLD diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile index 8b3a3f7bab49..7c24be2423d8 100644 --- a/drivers/platform/x86/intel/Makefile +++ b/drivers/platform/x86/intel/Makefile @@ -21,6 +21,7 @@ intel-vbtn-y := vbtn.o obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o # Intel miscellaneous drivers +obj-$(CONFIG_INTEL_ISHTP_ECLITE) += ishtp_eclite.o intel_int0002_vgpio-y := int0002_vgpio.o obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o intel_oaktrail-y := oaktrail.o diff --git a/drivers/platform/x86/intel/ishtp_eclite.c b/drivers/platform/x86/intel/ishtp_eclite.c new file mode 100644 index 000000000000..12fc98a48657 --- /dev/null +++ b/drivers/platform/x86/intel/ishtp_eclite.c @@ -0,0 +1,701 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel ECLite opregion driver for talking to ECLite firmware running on + * Intel Integrated Sensor Hub (ISH) using ISH Transport Protocol (ISHTP) + * + * Copyright (c) 2021, Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ECLITE_DATA_OPREGION_ID 0x9E +#define ECLITE_CMD_OPREGION_ID 0x9F + +#define ECL_MSG_DATA 0x1 +#define ECL_MSG_EVENT 0x2 + +#define ECL_ISH_READ 0x1 +#define ECL_ISH_WRITE 0x2 +#define ECL_ISH_HEADER_VERSION 0 + +#define ECL_CL_RX_RING_SIZE 16 +#define ECL_CL_TX_RING_SIZE 8 + +#define ECL_DATA_OPR_BUFLEN 384 +#define ECL_EVENTS_NOTIFY 333 + +#define cmd_opr_offsetof(element) offsetof(struct opregion_cmd, element) +#define cl_data_to_dev(opr_dev) ishtp_device((opr_dev)->cl_device) + +#ifndef BITS_TO_BYTES +#define BITS_TO_BYTES(x) ((x) / 8) +#endif + +struct opregion_cmd { + unsigned int command; + unsigned int offset; + unsigned int length; + unsigned int event_id; +}; + +struct opregion_data { + char data[ECL_DATA_OPR_BUFLEN]; +}; + +struct opregion_context { + struct opregion_cmd cmd_area; + struct opregion_data data_area; +}; + +struct ecl_message_header { + unsigned int version:2; + unsigned int data_type:2; + unsigned int request_type:2; + unsigned int offset:9; + unsigned int data_len:9; + unsigned int event:8; +}; + +struct ecl_message { + struct ecl_message_header header; + char payload[ECL_DATA_OPR_BUFLEN]; +}; + +struct ishtp_opregion_dev { + struct opregion_context opr_context; + struct ishtp_cl *ecl_ishtp_cl; + struct ishtp_cl_device *cl_device; + struct ishtp_fw_client *fw_client; + struct ishtp_cl_rb *rb; + struct acpi_device *adev; + unsigned int dsm_event_id; + unsigned int ish_link_ready; + unsigned int ish_read_done; + unsigned int acpi_init_done; + wait_queue_head_t read_wait; + struct work_struct event_work; + struct work_struct reset_work; + /* lock for opregion context */ + struct mutex lock; + +}; + +/* eclite ishtp client UUID: 6a19cc4b-d760-4de3-b14d-f25ebd0fbcd9 */ +static const guid_t ecl_ishtp_guid = + GUID_INIT(0x6a19cc4b, 0xd760, 0x4de3, + 0xb1, 0x4d, 0xf2, 0x5e, 0xbd, 0xf, 0xbc, 0xd9); + +/* ACPI DSM UUID: 91d936a7-1f01-49c6-a6b4-72f00ad8d8a5 */ +static const guid_t ecl_acpi_guid = + GUID_INIT(0x91d936a7, 0x1f01, 0x49c6, 0xa6, + 0xb4, 0x72, 0xf0, 0x0a, 0xd8, 0xd8, 0xa5); + +/** + * ecl_ish_cl_read() - Read data from eclite FW + * + * @opr_dev: pointer to opregion device + * + * This function issues a read request to eclite FW and waits until it + * receives a response. When response is received the read data is copied to + * opregion buffer. + */ +static int ecl_ish_cl_read(struct ishtp_opregion_dev *opr_dev) +{ + struct ecl_message_header header; + int len, rv; + + if (!opr_dev->ish_link_ready) + return -EIO; + + if ((opr_dev->opr_context.cmd_area.offset + + opr_dev->opr_context.cmd_area.length) > ECL_DATA_OPR_BUFLEN) { + return -EINVAL; + } + + header.version = ECL_ISH_HEADER_VERSION; + header.data_type = ECL_MSG_DATA; + header.request_type = ECL_ISH_READ; + header.offset = opr_dev->opr_context.cmd_area.offset; + header.data_len = opr_dev->opr_context.cmd_area.length; + header.event = opr_dev->opr_context.cmd_area.event_id; + len = sizeof(header); + + opr_dev->ish_read_done = false; + rv = ishtp_cl_send(opr_dev->ecl_ishtp_cl, (uint8_t *)&header, len); + if (rv) { + dev_err(cl_data_to_dev(opr_dev), "ish-read : send failed\n"); + return -EIO; + } + + dev_dbg(cl_data_to_dev(opr_dev), + "[ish_rd] Req: off : %x, len : %x\n", + header.offset, + header.data_len); + + rv = wait_event_interruptible_timeout(opr_dev->read_wait, + opr_dev->ish_read_done, + 2 * HZ); + if (!rv) { + dev_err(cl_data_to_dev(opr_dev), + "[ish_rd] No response from firmware\n"); + return -EIO; + } + + return 0; +} + +/** + * ecl_ish_cl_write() - This function writes data to eclite FW. + * + * @opr_dev: pointer to opregion device + * + * This function writes data to eclite FW. + */ +static int ecl_ish_cl_write(struct ishtp_opregion_dev *opr_dev) +{ + struct ecl_message message; + int len; + + if (!opr_dev->ish_link_ready) + return -EIO; + + if ((opr_dev->opr_context.cmd_area.offset + + opr_dev->opr_context.cmd_area.length) > ECL_DATA_OPR_BUFLEN) { + return -EINVAL; + } + + message.header.version = ECL_ISH_HEADER_VERSION; + message.header.data_type = ECL_MSG_DATA; + message.header.request_type = ECL_ISH_WRITE; + message.header.offset = opr_dev->opr_context.cmd_area.offset; + message.header.data_len = opr_dev->opr_context.cmd_area.length; + message.header.event = opr_dev->opr_context.cmd_area.event_id; + len = sizeof(struct ecl_message_header) + message.header.data_len; + + memcpy(message.payload, + opr_dev->opr_context.data_area.data + message.header.offset, + message.header.data_len); + + dev_dbg(cl_data_to_dev(opr_dev), + "[ish_wr] off : %x, len : %x\n", + message.header.offset, + message.header.data_len); + + return ishtp_cl_send(opr_dev->ecl_ishtp_cl, (uint8_t *)&message, len); +} + +static acpi_status +ecl_opregion_cmd_handler(u32 function, acpi_physical_address address, + u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + struct ishtp_opregion_dev *opr_dev; + struct opregion_cmd *cmd; + acpi_status status = AE_OK; + + if (!region_context || !value64) + return AE_BAD_PARAMETER; + + if (function == ACPI_READ) + return AE_ERROR; + + opr_dev = (struct ishtp_opregion_dev *)region_context; + + mutex_lock(&opr_dev->lock); + + cmd = &opr_dev->opr_context.cmd_area; + + switch (address) { + case cmd_opr_offsetof(command): + cmd->command = (u32)*value64; + + if (cmd->command == ECL_ISH_READ) + status = ecl_ish_cl_read(opr_dev); + else if (cmd->command == ECL_ISH_WRITE) + status = ecl_ish_cl_write(opr_dev); + else + status = AE_ERROR; + break; + case cmd_opr_offsetof(offset): + cmd->offset = (u32)*value64; + break; + case cmd_opr_offsetof(length): + cmd->length = (u32)*value64; + break; + case cmd_opr_offsetof(event_id): + cmd->event_id = (u32)*value64; + break; + default: + status = AE_ERROR; + } + + mutex_unlock(&opr_dev->lock); + + return status; +} + +static acpi_status +ecl_opregion_data_handler(u32 function, acpi_physical_address address, + u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + struct ishtp_opregion_dev *opr_dev; + unsigned int bytes = BITS_TO_BYTES(bits); + void *data_addr; + + if (!region_context || !value64) + return AE_BAD_PARAMETER; + + if (address + bytes > ECL_DATA_OPR_BUFLEN) + return AE_BAD_PARAMETER; + + opr_dev = (struct ishtp_opregion_dev *)region_context; + + mutex_lock(&opr_dev->lock); + + data_addr = &opr_dev->opr_context.data_area.data[address]; + + if (function == ACPI_READ) { + memcpy(value64, data_addr, bytes); + } else if (function == ACPI_WRITE) { + memcpy(data_addr, value64, bytes); + } else { + mutex_unlock(&opr_dev->lock); + return AE_BAD_PARAMETER; + } + + mutex_unlock(&opr_dev->lock); + + return AE_OK; +} + +static int acpi_find_eclite_device(struct ishtp_opregion_dev *opr_dev) +{ + struct acpi_device *adev; + + /* Find ECLite device and save reference */ + adev = acpi_dev_get_first_match_dev("INTC1035", NULL, -1); + if (!adev) { + dev_err(cl_data_to_dev(opr_dev), "eclite ACPI device not found\n"); + return -ENODEV; + } + + opr_dev->adev = adev; + + return 0; +} + +static int acpi_opregion_init(struct ishtp_opregion_dev *opr_dev) +{ + acpi_status status; + + status = acpi_install_address_space_handler(opr_dev->adev->handle, + ECLITE_CMD_OPREGION_ID, + ecl_opregion_cmd_handler, + NULL, opr_dev); + if (ACPI_FAILURE(status)) { + dev_err(cl_data_to_dev(opr_dev), + "cmd space handler install failed\n"); + return -ENODEV; + } + + status = acpi_install_address_space_handler(opr_dev->adev->handle, + ECLITE_DATA_OPREGION_ID, + ecl_opregion_data_handler, + NULL, opr_dev); + if (ACPI_FAILURE(status)) { + dev_err(cl_data_to_dev(opr_dev), + "data space handler install failed\n"); + + acpi_remove_address_space_handler(opr_dev->adev->handle, + ECLITE_CMD_OPREGION_ID, + ecl_opregion_cmd_handler); + return -ENODEV; + } + opr_dev->acpi_init_done = true; + + dev_dbg(cl_data_to_dev(opr_dev), "Opregion handlers are installed\n"); + + return 0; +} + +static void acpi_opregion_deinit(struct ishtp_opregion_dev *opr_dev) +{ + acpi_remove_address_space_handler(opr_dev->adev->handle, + ECLITE_CMD_OPREGION_ID, + ecl_opregion_cmd_handler); + + acpi_remove_address_space_handler(opr_dev->adev->handle, + ECLITE_DATA_OPREGION_ID, + ecl_opregion_data_handler); + opr_dev->acpi_init_done = false; +} + +static void ecl_acpi_invoke_dsm(struct work_struct *work) +{ + struct ishtp_opregion_dev *opr_dev; + union acpi_object *obj; + + opr_dev = container_of(work, struct ishtp_opregion_dev, event_work); + if (!opr_dev->acpi_init_done) + return; + + obj = acpi_evaluate_dsm(opr_dev->adev->handle, &ecl_acpi_guid, 0, + opr_dev->dsm_event_id, NULL); + if (!obj) { + dev_warn(cl_data_to_dev(opr_dev), "_DSM fn call failed\n"); + return; + } + + dev_dbg(cl_data_to_dev(opr_dev), "Exec DSM function code: %d success\n", + opr_dev->dsm_event_id); + + ACPI_FREE(obj); +} + +static void ecl_ish_process_rx_data(struct ishtp_opregion_dev *opr_dev) +{ + struct ecl_message *message = + (struct ecl_message *)opr_dev->rb->buffer.data; + + dev_dbg(cl_data_to_dev(opr_dev), + "[ish_rd] Resp: off : %x, len : %x\n", + message->header.offset, + message->header.data_len); + + if ((message->header.offset + message->header.data_len) > + ECL_DATA_OPR_BUFLEN) { + return; + } + + memcpy(opr_dev->opr_context.data_area.data + message->header.offset, + message->payload, message->header.data_len); + + opr_dev->ish_read_done = true; + wake_up_interruptible(&opr_dev->read_wait); +} + +static void ecl_ish_process_rx_event(struct ishtp_opregion_dev *opr_dev) +{ + struct ecl_message_header *header = + (struct ecl_message_header *)opr_dev->rb->buffer.data; + + dev_dbg(cl_data_to_dev(opr_dev), + "[ish_ev] Evt received: %8x\n", header->event); + + opr_dev->dsm_event_id = header->event; + schedule_work(&opr_dev->event_work); +} + +static int ecl_ish_cl_enable_events(struct ishtp_opregion_dev *opr_dev, + bool config_enable) +{ + struct ecl_message message; + int len; + + message.header.version = ECL_ISH_HEADER_VERSION; + message.header.data_type = ECL_MSG_DATA; + message.header.request_type = ECL_ISH_WRITE; + message.header.offset = ECL_EVENTS_NOTIFY; + message.header.data_len = 1; + message.payload[0] = config_enable; + + len = sizeof(struct ecl_message_header) + message.header.data_len; + + return ishtp_cl_send(opr_dev->ecl_ishtp_cl, (uint8_t *)&message, len); +} + +static void ecl_ishtp_cl_event_cb(struct ishtp_cl_device *cl_device) +{ + struct ishtp_cl *ecl_ishtp_cl = ishtp_get_drvdata(cl_device); + struct ishtp_opregion_dev *opr_dev; + struct ecl_message_header *header; + struct ishtp_cl_rb *rb; + + opr_dev = ishtp_get_client_data(ecl_ishtp_cl); + while ((rb = ishtp_cl_rx_get_rb(opr_dev->ecl_ishtp_cl)) != NULL) { + opr_dev->rb = rb; + header = (struct ecl_message_header *)rb->buffer.data; + + if (header->data_type == ECL_MSG_DATA) + ecl_ish_process_rx_data(opr_dev); + else if (header->data_type == ECL_MSG_EVENT) + ecl_ish_process_rx_event(opr_dev); + else + /* Got an event with wrong data_type, ignore it */ + dev_err(cl_data_to_dev(opr_dev), + "[ish_cb] Received wrong data_type\n"); + + ishtp_cl_io_rb_recycle(rb); + } +} + +static int ecl_ishtp_cl_init(struct ishtp_cl *ecl_ishtp_cl) +{ + struct ishtp_opregion_dev *opr_dev = + ishtp_get_client_data(ecl_ishtp_cl); + struct ishtp_fw_client *fw_client; + struct ishtp_device *dev; + int rv; + + rv = ishtp_cl_link(ecl_ishtp_cl); + if (rv) { + dev_err(cl_data_to_dev(opr_dev), "ishtp_cl_link failed\n"); + return rv; + } + + dev = ishtp_get_ishtp_device(ecl_ishtp_cl); + + /* Connect to FW client */ + ishtp_set_tx_ring_size(ecl_ishtp_cl, ECL_CL_TX_RING_SIZE); + ishtp_set_rx_ring_size(ecl_ishtp_cl, ECL_CL_RX_RING_SIZE); + + fw_client = ishtp_fw_cl_get_client(dev, &ecl_ishtp_guid); + if (!fw_client) { + dev_err(cl_data_to_dev(opr_dev), "fw client not found\n"); + return -ENOENT; + } + + ishtp_cl_set_fw_client_id(ecl_ishtp_cl, + ishtp_get_fw_client_id(fw_client)); + + ishtp_set_connection_state(ecl_ishtp_cl, ISHTP_CL_CONNECTING); + + rv = ishtp_cl_connect(ecl_ishtp_cl); + if (rv) { + dev_err(cl_data_to_dev(opr_dev), "client connect failed\n"); + + ishtp_cl_unlink(ecl_ishtp_cl); + return rv; + } + + dev_dbg(cl_data_to_dev(opr_dev), "Host connected to fw client\n"); + + return 0; +} + +static void ecl_ishtp_cl_deinit(struct ishtp_cl *ecl_ishtp_cl) +{ + ishtp_cl_unlink(ecl_ishtp_cl); + ishtp_cl_flush_queues(ecl_ishtp_cl); + ishtp_cl_free(ecl_ishtp_cl); +} + +static void ecl_ishtp_cl_reset_handler(struct work_struct *work) +{ + struct ishtp_opregion_dev *opr_dev; + struct ishtp_cl_device *cl_device; + struct ishtp_cl *ecl_ishtp_cl; + int rv; + int retry; + + opr_dev = container_of(work, struct ishtp_opregion_dev, reset_work); + + opr_dev->ish_link_ready = false; + + cl_device = opr_dev->cl_device; + ecl_ishtp_cl = opr_dev->ecl_ishtp_cl; + + ecl_ishtp_cl_deinit(ecl_ishtp_cl); + + ecl_ishtp_cl = ishtp_cl_allocate(cl_device); + if (!ecl_ishtp_cl) + return; + + ishtp_set_drvdata(cl_device, ecl_ishtp_cl); + ishtp_set_client_data(ecl_ishtp_cl, opr_dev); + + opr_dev->ecl_ishtp_cl = ecl_ishtp_cl; + + for (retry = 0; retry < 3; ++retry) { + rv = ecl_ishtp_cl_init(ecl_ishtp_cl); + if (!rv) + break; + } + if (rv) { + ishtp_cl_free(ecl_ishtp_cl); + opr_dev->ecl_ishtp_cl = NULL; + dev_err(cl_data_to_dev(opr_dev), + "[ish_rst] Reset failed. Link not ready.\n"); + return; + } + + ishtp_register_event_cb(cl_device, ecl_ishtp_cl_event_cb); + dev_info(cl_data_to_dev(opr_dev), + "[ish_rst] Reset Success. Link ready.\n"); + + opr_dev->ish_link_ready = true; + + if (opr_dev->acpi_init_done) + return; + + rv = acpi_opregion_init(opr_dev); + if (rv) { + dev_err(cl_data_to_dev(opr_dev), + "ACPI opregion init failed\n"); + } +} + +static int ecl_ishtp_cl_probe(struct ishtp_cl_device *cl_device) +{ + struct ishtp_cl *ecl_ishtp_cl; + struct ishtp_opregion_dev *opr_dev; + int rv; + + opr_dev = devm_kzalloc(ishtp_device(cl_device), sizeof(*opr_dev), + GFP_KERNEL); + if (!opr_dev) + return -ENOMEM; + + ecl_ishtp_cl = ishtp_cl_allocate(cl_device); + if (!ecl_ishtp_cl) + return -ENOMEM; + + ishtp_set_drvdata(cl_device, ecl_ishtp_cl); + ishtp_set_client_data(ecl_ishtp_cl, opr_dev); + opr_dev->ecl_ishtp_cl = ecl_ishtp_cl; + opr_dev->cl_device = cl_device; + + init_waitqueue_head(&opr_dev->read_wait); + INIT_WORK(&opr_dev->event_work, ecl_acpi_invoke_dsm); + INIT_WORK(&opr_dev->reset_work, ecl_ishtp_cl_reset_handler); + + /* Initialize ish client device */ + rv = ecl_ishtp_cl_init(ecl_ishtp_cl); + if (rv) { + dev_err(cl_data_to_dev(opr_dev), "Client init failed\n"); + goto err_exit; + } + + dev_dbg(cl_data_to_dev(opr_dev), "eclite-ishtp client initialised\n"); + + opr_dev->ish_link_ready = true; + mutex_init(&opr_dev->lock); + + rv = acpi_find_eclite_device(opr_dev); + if (rv) { + dev_err(cl_data_to_dev(opr_dev), "ECLite ACPI ID not found\n"); + goto err_exit; + } + + /* Register a handler for eclite fw events */ + ishtp_register_event_cb(cl_device, ecl_ishtp_cl_event_cb); + + /* Now init opregion handlers */ + rv = acpi_opregion_init(opr_dev); + if (rv) { + dev_err(cl_data_to_dev(opr_dev), "ACPI opregion init failed\n"); + goto err_exit; + } + + /* Reprobe devices depending on ECLite - battery, fan, etc. */ + acpi_dev_clear_dependencies(opr_dev->adev); + + return 0; +err_exit: + ishtp_set_connection_state(ecl_ishtp_cl, ISHTP_CL_DISCONNECTING); + ishtp_cl_disconnect(ecl_ishtp_cl); + ecl_ishtp_cl_deinit(ecl_ishtp_cl); + + return rv; +} + +static void ecl_ishtp_cl_remove(struct ishtp_cl_device *cl_device) +{ + struct ishtp_cl *ecl_ishtp_cl = ishtp_get_drvdata(cl_device); + struct ishtp_opregion_dev *opr_dev = + ishtp_get_client_data(ecl_ishtp_cl); + + if (opr_dev->acpi_init_done) + acpi_opregion_deinit(opr_dev); + + acpi_dev_put(opr_dev->adev); + + ishtp_set_connection_state(ecl_ishtp_cl, ISHTP_CL_DISCONNECTING); + ishtp_cl_disconnect(ecl_ishtp_cl); + ecl_ishtp_cl_deinit(ecl_ishtp_cl); + + cancel_work_sync(&opr_dev->reset_work); + cancel_work_sync(&opr_dev->event_work); +} + +static int ecl_ishtp_cl_reset(struct ishtp_cl_device *cl_device) +{ + struct ishtp_cl *ecl_ishtp_cl = ishtp_get_drvdata(cl_device); + struct ishtp_opregion_dev *opr_dev = + ishtp_get_client_data(ecl_ishtp_cl); + + schedule_work(&opr_dev->reset_work); + + return 0; +} + +static int ecl_ishtp_cl_suspend(struct device *device) +{ + struct ishtp_cl_device *cl_device = ishtp_dev_to_cl_device(device); + struct ishtp_cl *ecl_ishtp_cl = ishtp_get_drvdata(cl_device); + struct ishtp_opregion_dev *opr_dev = + ishtp_get_client_data(ecl_ishtp_cl); + + if (acpi_target_system_state() == ACPI_STATE_S0) + return 0; + + acpi_opregion_deinit(opr_dev); + ecl_ish_cl_enable_events(opr_dev, false); + + return 0; +} + +static int ecl_ishtp_cl_resume(struct device *device) +{ + /* A reset is expected to call after an Sx. At this point + * we are not sure if the link is up or not to restore anything, + * so do nothing in resume path + */ + return 0; +} + +static const struct dev_pm_ops ecl_ishtp_pm_ops = { + .suspend = ecl_ishtp_cl_suspend, + .resume = ecl_ishtp_cl_resume, +}; + +static struct ishtp_cl_driver ecl_ishtp_cl_driver = { + .name = "ishtp-eclite", + .guid = &ecl_ishtp_guid, + .probe = ecl_ishtp_cl_probe, + .remove = ecl_ishtp_cl_remove, + .reset = ecl_ishtp_cl_reset, + .driver.pm = &ecl_ishtp_pm_ops, +}; + +static int __init ecl_ishtp_init(void) +{ + return ishtp_cl_driver_register(&ecl_ishtp_cl_driver, THIS_MODULE); +} + +static void __exit ecl_ishtp_exit(void) +{ + return ishtp_cl_driver_unregister(&ecl_ishtp_cl_driver); +} + +late_initcall(ecl_ishtp_init); +module_exit(ecl_ishtp_exit); + +MODULE_DESCRIPTION("ISH ISHTP eclite client opregion driver"); +MODULE_AUTHOR("K Naduvalath, Sumesh "); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("ishtp:*"); -- cgit v1.2.3 From 6a684635478c03db92727c9fa217c660f6c3abc3 Mon Sep 17 00:00:00 2001 From: Mark Gross Date: Tue, 21 Sep 2021 06:53:58 -0700 Subject: update email addresses. Change all email addresses for Mark Gross to use markgross@kernel.org. Signed-off-by: Mark Gross Link: https://lore.kernel.org/r/20210921135358.85143-1-markgross@kernel.org Signed-off-by: Hans de Goede --- MAINTAINERS | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 949eb8ce6e72..db8186530b0b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6683,7 +6683,7 @@ S: Supported F: drivers/edac/dmc520_edac.c EDAC-E752X -M: Mark Gross +M: Mark Gross L: linux-edac@vger.kernel.org S: Maintained F: drivers/edac/e752x_edac.c @@ -11985,7 +11985,7 @@ F: drivers/net/ethernet/mellanox/mlxfw/ MELLANOX HARDWARE PLATFORM SUPPORT M: Hans de Goede -M: Mark Gross +M: Mark Gross M: Vadim Pasternak L: platform-driver-x86@vger.kernel.org S: Supported @@ -12437,7 +12437,7 @@ F: drivers/platform/surface/surface_gpe.c MICROSOFT SURFACE HARDWARE PLATFORM SUPPORT M: Hans de Goede -M: Mark Gross +M: Mark Gross M: Maximilian Luz L: platform-driver-x86@vger.kernel.org S: Maintained @@ -18465,7 +18465,7 @@ S: Supported F: drivers/net/ethernet/tehuti/* TELECOM CLOCK DRIVER FOR MCPL0010 -M: Mark Gross +M: Mark Gross S: Supported F: drivers/char/tlclk.c @@ -20387,7 +20387,7 @@ F: arch/x86/mm/ X86 PLATFORM DRIVERS M: Hans de Goede -M: Mark Gross +M: Mark Gross L: platform-driver-x86@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git -- cgit v1.2.3 From ca16d33bd86246bf91d089c8ba78b9b214574b12 Mon Sep 17 00:00:00 2001 From: Daniel Dadap Date: Mon, 27 Sep 2021 15:23:59 -0500 Subject: platform/x86: Rename wmaa-backlight-wmi to nvidia-wmi-ec-backlight Rename the wmaa-backlight-wmi driver and associated KConfig option to remove the remaining references to the "WMAA" ACPI handle which was used in the previous name. The driver has already been updated to remove internal references to "WMAA". As part of the renaming, the components in the name have been rearranged to reflect the standard vendor_wmi_feature pattern. Signed-off-by: Daniel Dadap Link: https://lore.kernel.org/r/20210927202359.13684-2-ddadap@nvidia.com Signed-off-by: Hans de Goede --- MAINTAINERS | 12 +- drivers/platform/x86/Kconfig | 15 +- drivers/platform/x86/Makefile | 2 +- drivers/platform/x86/nvidia-wmi-ec-backlight.c | 213 +++++++++++++++++++++++++ drivers/platform/x86/wmaa-backlight-wmi.c | 213 ------------------------- 5 files changed, 227 insertions(+), 228 deletions(-) create mode 100644 drivers/platform/x86/nvidia-wmi-ec-backlight.c delete mode 100644 drivers/platform/x86/wmaa-backlight-wmi.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index db8186530b0b..7041c6591a53 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13389,6 +13389,12 @@ S: Maintained F: drivers/video/fbdev/nvidia/ F: drivers/video/fbdev/riva/ +NVIDIA WMI EC BACKLIGHT DRIVER +M: Daniel Dadap +L: platform-driver-x86@vger.kernel.org +S: Supported +F: drivers/platform/x86/nvidia-wmi-ec-backlight.c + NVM EXPRESS DRIVER M: Keith Busch M: Jens Axboe @@ -20252,12 +20258,6 @@ L: linux-wireless@vger.kernel.org S: Odd fixes F: drivers/net/wireless/wl3501* -WMAA BACKLIGHT DRIVER -M: Daniel Dadap -L: platform-driver-x86@vger.kernel.org -S: Supported -F: drivers/platform/x86/wmaa-backlight-wmi.c - WOLFSON MICROELECTRONICS DRIVERS L: patches@opensource.cirrus.com S: Supported diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index f3dbb0273884..520da0a6acd1 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -91,21 +91,20 @@ config PEAQ_WMI help Say Y here if you want to support WMI-based hotkeys on PEAQ 2-in-1s. -config WMAA_BACKLIGHT_WMI - tristate "ACPI WMAA Backlight Driver" +config NVIDIA_WMI_EC_BACKLIGHT + tristate "EC Backlight Driver for Hybrid Graphics Notebook Systems" depends on ACPI_WMI depends on BACKLIGHT_CLASS_DEVICE help - This driver provides a sysfs backlight interface for notebook - systems which expose the WMAA ACPI method and an associated WMI - wrapper to drive LCD backlight levels through the Embedded Controller - (EC). + This driver provides a sysfs backlight interface for notebook systems + which are equipped with NVIDIA hybrid graphics and drive LCD backlight + levels through the Embedded Controller (EC). Say Y or M here if you want to control the backlight on a notebook - system with an EC-driven backlight using the ACPI WMAA method. + system with an EC-driven backlight. If you choose to compile this driver as a module the module will be - called wmaa-backlight-wmi. + called nvidia-wmi-ec-backlight. config XIAOMI_WMI tristate "Xiaomi WMI key driver" diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index edde481c14ad..0d046ed70da1 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -11,8 +11,8 @@ obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o # WMI drivers obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o obj-$(CONFIG_MXM_WMI) += mxm-wmi.o +obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o -obj-$(CONFIG_WMAA_BACKLIGHT_WMI) += wmaa-backlight-wmi.o obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o diff --git a/drivers/platform/x86/nvidia-wmi-ec-backlight.c b/drivers/platform/x86/nvidia-wmi-ec-backlight.c new file mode 100644 index 000000000000..61e37194df70 --- /dev/null +++ b/drivers/platform/x86/nvidia-wmi-ec-backlight.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +/** + * enum wmi_brightness_method - WMI method IDs + * @WMI_BRIGHTNESS_METHOD_LEVEL: Get/Set EC brightness level status + * @WMI_BRIGHTNESS_METHOD_SOURCE: Get/Set EC Brightness Source + */ +enum wmi_brightness_method { + WMI_BRIGHTNESS_METHOD_LEVEL = 1, + WMI_BRIGHTNESS_METHOD_SOURCE = 2, + WMI_BRIGHTNESS_METHOD_MAX +}; + +/** + * enum wmi_brightness_mode - Operation mode for WMI-wrapped method + * @WMI_BRIGHTNESS_MODE_GET: Get the current brightness level/source. + * @WMI_BRIGHTNESS_MODE_SET: Set the brightness level. + * @WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL: Get the maximum brightness level. This + * is only valid when the WMI method is + * %WMI_BRIGHTNESS_METHOD_LEVEL. + */ +enum wmi_brightness_mode { + WMI_BRIGHTNESS_MODE_GET = 0, + WMI_BRIGHTNESS_MODE_SET = 1, + WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL = 2, + WMI_BRIGHTNESS_MODE_MAX +}; + +/** + * enum wmi_brightness_source - Backlight brightness control source selection + * @WMI_BRIGHTNESS_SOURCE_GPU: Backlight brightness is controlled by the GPU. + * @WMI_BRIGHTNESS_SOURCE_EC: Backlight brightness is controlled by the + * system's Embedded Controller (EC). + * @WMI_BRIGHTNESS_SOURCE_AUX: Backlight brightness is controlled over the + * DisplayPort AUX channel. + */ +enum wmi_brightness_source { + WMI_BRIGHTNESS_SOURCE_GPU = 1, + WMI_BRIGHTNESS_SOURCE_EC = 2, + WMI_BRIGHTNESS_SOURCE_AUX = 3, + WMI_BRIGHTNESS_SOURCE_MAX +}; + +/** + * struct wmi_brightness_args - arguments for the WMI-wrapped ACPI method + * @mode: Pass in an &enum wmi_brightness_mode value to select between + * getting or setting a value. + * @val: In parameter for value to set when using %WMI_BRIGHTNESS_MODE_SET + * mode. Not used in conjunction with %WMI_BRIGHTNESS_MODE_GET or + * %WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL mode. + * @ret: Out parameter returning retrieved value when operating in + * %WMI_BRIGHTNESS_MODE_GET or %WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL + * mode. Not used in %WMI_BRIGHTNESS_MODE_SET mode. + * @ignored: Padding; not used. The ACPI method expects a 24 byte params struct. + * + * This is the parameters structure for the WmiBrightnessNotify ACPI method as + * wrapped by WMI. The value passed in to @val or returned by @ret will be a + * brightness value when the WMI method ID is %WMI_BRIGHTNESS_METHOD_LEVEL, or + * an &enum wmi_brightness_source value with %WMI_BRIGHTNESS_METHOD_SOURCE. + */ +struct wmi_brightness_args { + u32 mode; + u32 val; + u32 ret; + u32 ignored[3]; +}; + +/** + * wmi_brightness_notify() - helper function for calling WMI-wrapped ACPI method + * @w: Pointer to the struct wmi_device identified by %WMI_BRIGHTNESS_GUID + * @id: The WMI method ID to call (e.g. %WMI_BRIGHTNESS_METHOD_LEVEL or + * %WMI_BRIGHTNESS_METHOD_SOURCE) + * @mode: The operation to perform on the method (e.g. %WMI_BRIGHTNESS_MODE_SET + * or %WMI_BRIGHTNESS_MODE_GET) + * @val: Pointer to a value passed in by the caller when @mode is + * %WMI_BRIGHTNESS_MODE_SET, or a value passed out to caller when @mode + * is %WMI_BRIGHTNESS_MODE_GET or %WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL. + * + * Returns 0 on success, or a negative error number on failure. + */ +static int wmi_brightness_notify(struct wmi_device *w, enum wmi_brightness_method id, enum wmi_brightness_mode mode, u32 *val) +{ + struct wmi_brightness_args args = { + .mode = mode, + .val = 0, + .ret = 0, + }; + struct acpi_buffer buf = { (acpi_size)sizeof(args), &args }; + acpi_status status; + + if (id < WMI_BRIGHTNESS_METHOD_LEVEL || + id >= WMI_BRIGHTNESS_METHOD_MAX || + mode < WMI_BRIGHTNESS_MODE_GET || mode >= WMI_BRIGHTNESS_MODE_MAX) + return -EINVAL; + + if (mode == WMI_BRIGHTNESS_MODE_SET) + args.val = *val; + + status = wmidev_evaluate_method(w, 0, id, &buf, &buf); + if (ACPI_FAILURE(status)) { + dev_err(&w->dev, "EC backlight control failed: %s\n", + acpi_format_exception(status)); + return -EIO; + } + + if (mode != WMI_BRIGHTNESS_MODE_SET) + *val = args.ret; + + return 0; +} + +static int nvidia_wmi_ec_backlight_update_status(struct backlight_device *bd) +{ + struct wmi_device *wdev = bl_get_data(bd); + + return wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL, + WMI_BRIGHTNESS_MODE_SET, + &bd->props.brightness); +} + +static int nvidia_wmi_ec_backlight_get_brightness(struct backlight_device *bd) +{ + struct wmi_device *wdev = bl_get_data(bd); + u32 level; + int ret; + + ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL, + WMI_BRIGHTNESS_MODE_GET, &level); + if (ret < 0) + return ret; + + return level; +} + +static const struct backlight_ops nvidia_wmi_ec_backlight_ops = { + .update_status = nvidia_wmi_ec_backlight_update_status, + .get_brightness = nvidia_wmi_ec_backlight_get_brightness, +}; + +static int nvidia_wmi_ec_backlight_probe(struct wmi_device *wdev, const void *ctx) +{ + struct backlight_properties props = {}; + struct backlight_device *bdev; + u32 source; + int ret; + + ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_SOURCE, + WMI_BRIGHTNESS_MODE_GET, &source); + if (ret) + return ret; + + /* + * This driver is only to be used when brightness control is handled + * by the EC; otherwise, the GPU driver(s) should control brightness. + */ + if (source != WMI_BRIGHTNESS_SOURCE_EC) + return -ENODEV; + + /* + * Identify this backlight device as a firmware device so that it can + * be prioritized over any exposed GPU-driven raw device(s). + */ + props.type = BACKLIGHT_FIRMWARE; + + ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL, + WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL, + &props.max_brightness); + if (ret) + return ret; + + ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL, + WMI_BRIGHTNESS_MODE_GET, &props.brightness); + if (ret) + return ret; + + bdev = devm_backlight_device_register(&wdev->dev, + "nvidia_wmi_ec_backlight", + &wdev->dev, wdev, + &nvidia_wmi_ec_backlight_ops, + &props); + return PTR_ERR_OR_ZERO(bdev); +} + +#define WMI_BRIGHTNESS_GUID "603E9613-EF25-4338-A3D0-C46177516DB7" + +static const struct wmi_device_id nvidia_wmi_ec_backlight_id_table[] = { + { .guid_string = WMI_BRIGHTNESS_GUID }, + { } +}; +MODULE_DEVICE_TABLE(wmi, nvidia_wmi_ec_backlight_id_table); + +static struct wmi_driver nvidia_wmi_ec_backlight_driver = { + .driver = { + .name = "nvidia-wmi-ec-backlight", + }, + .probe = nvidia_wmi_ec_backlight_probe, + .id_table = nvidia_wmi_ec_backlight_id_table, +}; +module_wmi_driver(nvidia_wmi_ec_backlight_driver); + +MODULE_AUTHOR("Daniel Dadap "); +MODULE_DESCRIPTION("NVIDIA WMI EC Backlight driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/wmaa-backlight-wmi.c b/drivers/platform/x86/wmaa-backlight-wmi.c deleted file mode 100644 index 61e37194df70..000000000000 --- a/drivers/platform/x86/wmaa-backlight-wmi.c +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. - */ - -#include -#include -#include -#include -#include -#include - -/** - * enum wmi_brightness_method - WMI method IDs - * @WMI_BRIGHTNESS_METHOD_LEVEL: Get/Set EC brightness level status - * @WMI_BRIGHTNESS_METHOD_SOURCE: Get/Set EC Brightness Source - */ -enum wmi_brightness_method { - WMI_BRIGHTNESS_METHOD_LEVEL = 1, - WMI_BRIGHTNESS_METHOD_SOURCE = 2, - WMI_BRIGHTNESS_METHOD_MAX -}; - -/** - * enum wmi_brightness_mode - Operation mode for WMI-wrapped method - * @WMI_BRIGHTNESS_MODE_GET: Get the current brightness level/source. - * @WMI_BRIGHTNESS_MODE_SET: Set the brightness level. - * @WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL: Get the maximum brightness level. This - * is only valid when the WMI method is - * %WMI_BRIGHTNESS_METHOD_LEVEL. - */ -enum wmi_brightness_mode { - WMI_BRIGHTNESS_MODE_GET = 0, - WMI_BRIGHTNESS_MODE_SET = 1, - WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL = 2, - WMI_BRIGHTNESS_MODE_MAX -}; - -/** - * enum wmi_brightness_source - Backlight brightness control source selection - * @WMI_BRIGHTNESS_SOURCE_GPU: Backlight brightness is controlled by the GPU. - * @WMI_BRIGHTNESS_SOURCE_EC: Backlight brightness is controlled by the - * system's Embedded Controller (EC). - * @WMI_BRIGHTNESS_SOURCE_AUX: Backlight brightness is controlled over the - * DisplayPort AUX channel. - */ -enum wmi_brightness_source { - WMI_BRIGHTNESS_SOURCE_GPU = 1, - WMI_BRIGHTNESS_SOURCE_EC = 2, - WMI_BRIGHTNESS_SOURCE_AUX = 3, - WMI_BRIGHTNESS_SOURCE_MAX -}; - -/** - * struct wmi_brightness_args - arguments for the WMI-wrapped ACPI method - * @mode: Pass in an &enum wmi_brightness_mode value to select between - * getting or setting a value. - * @val: In parameter for value to set when using %WMI_BRIGHTNESS_MODE_SET - * mode. Not used in conjunction with %WMI_BRIGHTNESS_MODE_GET or - * %WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL mode. - * @ret: Out parameter returning retrieved value when operating in - * %WMI_BRIGHTNESS_MODE_GET or %WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL - * mode. Not used in %WMI_BRIGHTNESS_MODE_SET mode. - * @ignored: Padding; not used. The ACPI method expects a 24 byte params struct. - * - * This is the parameters structure for the WmiBrightnessNotify ACPI method as - * wrapped by WMI. The value passed in to @val or returned by @ret will be a - * brightness value when the WMI method ID is %WMI_BRIGHTNESS_METHOD_LEVEL, or - * an &enum wmi_brightness_source value with %WMI_BRIGHTNESS_METHOD_SOURCE. - */ -struct wmi_brightness_args { - u32 mode; - u32 val; - u32 ret; - u32 ignored[3]; -}; - -/** - * wmi_brightness_notify() - helper function for calling WMI-wrapped ACPI method - * @w: Pointer to the struct wmi_device identified by %WMI_BRIGHTNESS_GUID - * @id: The WMI method ID to call (e.g. %WMI_BRIGHTNESS_METHOD_LEVEL or - * %WMI_BRIGHTNESS_METHOD_SOURCE) - * @mode: The operation to perform on the method (e.g. %WMI_BRIGHTNESS_MODE_SET - * or %WMI_BRIGHTNESS_MODE_GET) - * @val: Pointer to a value passed in by the caller when @mode is - * %WMI_BRIGHTNESS_MODE_SET, or a value passed out to caller when @mode - * is %WMI_BRIGHTNESS_MODE_GET or %WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL. - * - * Returns 0 on success, or a negative error number on failure. - */ -static int wmi_brightness_notify(struct wmi_device *w, enum wmi_brightness_method id, enum wmi_brightness_mode mode, u32 *val) -{ - struct wmi_brightness_args args = { - .mode = mode, - .val = 0, - .ret = 0, - }; - struct acpi_buffer buf = { (acpi_size)sizeof(args), &args }; - acpi_status status; - - if (id < WMI_BRIGHTNESS_METHOD_LEVEL || - id >= WMI_BRIGHTNESS_METHOD_MAX || - mode < WMI_BRIGHTNESS_MODE_GET || mode >= WMI_BRIGHTNESS_MODE_MAX) - return -EINVAL; - - if (mode == WMI_BRIGHTNESS_MODE_SET) - args.val = *val; - - status = wmidev_evaluate_method(w, 0, id, &buf, &buf); - if (ACPI_FAILURE(status)) { - dev_err(&w->dev, "EC backlight control failed: %s\n", - acpi_format_exception(status)); - return -EIO; - } - - if (mode != WMI_BRIGHTNESS_MODE_SET) - *val = args.ret; - - return 0; -} - -static int nvidia_wmi_ec_backlight_update_status(struct backlight_device *bd) -{ - struct wmi_device *wdev = bl_get_data(bd); - - return wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL, - WMI_BRIGHTNESS_MODE_SET, - &bd->props.brightness); -} - -static int nvidia_wmi_ec_backlight_get_brightness(struct backlight_device *bd) -{ - struct wmi_device *wdev = bl_get_data(bd); - u32 level; - int ret; - - ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL, - WMI_BRIGHTNESS_MODE_GET, &level); - if (ret < 0) - return ret; - - return level; -} - -static const struct backlight_ops nvidia_wmi_ec_backlight_ops = { - .update_status = nvidia_wmi_ec_backlight_update_status, - .get_brightness = nvidia_wmi_ec_backlight_get_brightness, -}; - -static int nvidia_wmi_ec_backlight_probe(struct wmi_device *wdev, const void *ctx) -{ - struct backlight_properties props = {}; - struct backlight_device *bdev; - u32 source; - int ret; - - ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_SOURCE, - WMI_BRIGHTNESS_MODE_GET, &source); - if (ret) - return ret; - - /* - * This driver is only to be used when brightness control is handled - * by the EC; otherwise, the GPU driver(s) should control brightness. - */ - if (source != WMI_BRIGHTNESS_SOURCE_EC) - return -ENODEV; - - /* - * Identify this backlight device as a firmware device so that it can - * be prioritized over any exposed GPU-driven raw device(s). - */ - props.type = BACKLIGHT_FIRMWARE; - - ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL, - WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL, - &props.max_brightness); - if (ret) - return ret; - - ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL, - WMI_BRIGHTNESS_MODE_GET, &props.brightness); - if (ret) - return ret; - - bdev = devm_backlight_device_register(&wdev->dev, - "nvidia_wmi_ec_backlight", - &wdev->dev, wdev, - &nvidia_wmi_ec_backlight_ops, - &props); - return PTR_ERR_OR_ZERO(bdev); -} - -#define WMI_BRIGHTNESS_GUID "603E9613-EF25-4338-A3D0-C46177516DB7" - -static const struct wmi_device_id nvidia_wmi_ec_backlight_id_table[] = { - { .guid_string = WMI_BRIGHTNESS_GUID }, - { } -}; -MODULE_DEVICE_TABLE(wmi, nvidia_wmi_ec_backlight_id_table); - -static struct wmi_driver nvidia_wmi_ec_backlight_driver = { - .driver = { - .name = "nvidia-wmi-ec-backlight", - }, - .probe = nvidia_wmi_ec_backlight_probe, - .id_table = nvidia_wmi_ec_backlight_id_table, -}; -module_wmi_driver(nvidia_wmi_ec_backlight_driver); - -MODULE_AUTHOR("Daniel Dadap "); -MODULE_DESCRIPTION("NVIDIA WMI EC Backlight driver"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 85303db36b6e170917a7bc6aae4898c31a5272a0 Mon Sep 17 00:00:00 2001 From: Shravan S Date: Wed, 6 Oct 2021 13:05:25 +0530 Subject: platform/x86: int1092: Fix non sequential device mode handling SAR information from BIOS may come in non sequential pattern. To overcome the issue, a check is made to extract the right SAR information using the device mode which is currently being used. Remove .owner field if calls are used which set it automatically. Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: Shravan S Link: https://lore.kernel.org/r/20211006073525.1332925-1-s.shravan@intel.com Signed-off-by: Hans de Goede Reviewed-by: Hans de Goede --- MAINTAINERS | 2 +- drivers/platform/x86/intel/int1092/intel_sar.c | 23 +++++++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 7041c6591a53..6d6135915073 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9302,7 +9302,7 @@ S: Maintained F: drivers/platform/x86/intel/atomisp2/led.c INTEL BIOS SAR INT1092 DRIVER -M: Shravan S +M: Shravan Sudhakar M: Intel Corporation L: platform-driver-x86@vger.kernel.org S: Maintained diff --git a/drivers/platform/x86/intel/int1092/intel_sar.c b/drivers/platform/x86/intel/int1092/intel_sar.c index 379560fe5df9..e03943e6380a 100644 --- a/drivers/platform/x86/intel/int1092/intel_sar.c +++ b/drivers/platform/x86/intel/int1092/intel_sar.c @@ -42,12 +42,20 @@ static void update_sar_data(struct wwan_sar_context *context) if (config->device_mode_info && context->sar_data.device_mode < config->total_dev_mode) { - struct wwan_device_mode_info *dev_mode = - &config->device_mode_info[context->sar_data.device_mode]; - - context->sar_data.antennatable_index = dev_mode->antennatable_index; - context->sar_data.bandtable_index = dev_mode->bandtable_index; - context->sar_data.sartable_index = dev_mode->sartable_index; + int itr = 0; + + for (itr = 0; itr < config->total_dev_mode; itr++) { + if (context->sar_data.device_mode == + config->device_mode_info[itr].device_mode) { + struct wwan_device_mode_info *dev_mode = + &config->device_mode_info[itr]; + + context->sar_data.antennatable_index = dev_mode->antennatable_index; + context->sar_data.bandtable_index = dev_mode->bandtable_index; + context->sar_data.sartable_index = dev_mode->sartable_index; + break; + } + } } } @@ -305,7 +313,6 @@ static struct platform_driver sar_driver = { .remove = sar_remove, .driver = { .name = DRVNAME, - .owner = THIS_MODULE, .acpi_match_table = ACPI_PTR(sar_device_ids) } }; @@ -313,4 +320,4 @@ module_platform_driver(sar_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Platform device driver for INTEL MODEM BIOS SAR"); -MODULE_AUTHOR("Shravan S "); +MODULE_AUTHOR("Shravan Sudhakar "); -- cgit v1.2.3 From 86af1d02d458379ae53031fabad560f10f8bdadd Mon Sep 17 00:00:00 2001 From: Santosh Kumar Yadav Date: Wed, 20 Oct 2021 14:36:34 +0200 Subject: platform/x86: Support for EC-connected GPIOs for identify LED/button on Barco P50 board Add a driver providing access to the GPIOs for the identify button and led present on Barco P50 board, based on the pcengines-apuv2.c driver. There is unfortunately no suitable ACPI entry for the EC communication interface, so instead bind to boards with "P50" as their DMI product family and hard code the I/O port number (0x299). The driver also hooks up the leds-gpio and gpio-keys-polled drivers to the GPIOs, so they are finally exposed as: LED: /sys/class/leds/identify Button: (/proc/bus/input/devices) I: Bus=0019 Vendor=0001 Product=0001 Version=0100 N: Name="identify" P: Phys=gpio-keys-polled/input0 S: Sysfs=/devices/platform/barco-p50-gpio/gpio-keys-polled/input/input10 U: Uniq= H: Handlers=event10 B: PROP=0 B: EV=3 B: KEY=1000000 0 0 0 0 0 0 Signed-off-by: Santosh Kumar Yadav Signed-off-by: Peter Korsgaard Link: https://lore.kernel.org/r/20211020123634.2638-1-peter@korsgaard.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- MAINTAINERS | 6 + drivers/platform/x86/Kconfig | 10 + drivers/platform/x86/Makefile | 3 + drivers/platform/x86/barco-p50-gpio.c | 436 ++++++++++++++++++++++++++++++++++ 4 files changed, 455 insertions(+) create mode 100644 drivers/platform/x86/barco-p50-gpio.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 6d6135915073..ebdfc74e841d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3215,6 +3215,12 @@ F: drivers/video/backlight/ F: include/linux/backlight.h F: include/linux/pwm_backlight.h +BARCO P50 GPIO DRIVER +M: Santosh Kumar Yadav +M: Peter Korsgaard +S: Maintained +F: drivers/platform/x86/barco-p50-gpio.c + BATMAN ADVANCED M: Marek Lindner M: Simon Wunderlich diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 520da0a6acd1..9fd0c15d77a8 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -729,6 +729,16 @@ config PCENGINES_APU2 To compile this driver as a module, choose M here: the module will be called pcengines-apuv2. +config BARCO_P50_GPIO + tristate "Barco P50 GPIO driver for identify LED/button" + depends on GPIOLIB + help + This driver provides access to the GPIOs for the identify button + and led present on Barco P50 board. + + To compile this driver as a module, choose M here: the module + will be called barco-p50-gpio. + config SAMSUNG_LAPTOP tristate "Samsung Laptop driver" depends on RFKILL || RFKILL = n diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 0d046ed70da1..219478061683 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -81,6 +81,9 @@ obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o # PC Engines obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o +# Barco +obj-$(CONFIG_BARCO_P50_GPIO) += barco-p50-gpio.o + # Samsung obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c new file mode 100644 index 000000000000..ca0b2564c407 --- /dev/null +++ b/drivers/platform/x86/barco-p50-gpio.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Support for EC-connected GPIOs for identify + * LED/button on Barco P50 board + * + * Copyright (C) 2021 Barco NV + * Author: Santosh Kumar Yadav + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DRIVER_NAME "barco-p50-gpio" + +/* GPIO lines */ +#define P50_GPIO_LINE_LED 0 +#define P50_GPIO_LINE_BTN 1 + +/* GPIO IO Ports */ +#define P50_GPIO_IO_PORT_BASE 0x299 + +#define P50_PORT_DATA 0x00 +#define P50_PORT_CMD 0x01 + +#define P50_STATUS_OBF 0x01 /* EC output buffer full */ +#define P50_STATUS_IBF 0x02 /* EC input buffer full */ + +#define P50_CMD_READ 0xa0 +#define P50_CMD_WRITE 0x50 + +/* EC mailbox registers */ +#define P50_MBOX_REG_CMD 0x00 +#define P50_MBOX_REG_STATUS 0x01 +#define P50_MBOX_REG_PARAM 0x02 +#define P50_MBOX_REG_DATA 0x03 + +#define P50_MBOX_CMD_READ_GPIO 0x11 +#define P50_MBOX_CMD_WRITE_GPIO 0x12 +#define P50_MBOX_CMD_CLEAR 0xff + +#define P50_MBOX_STATUS_SUCCESS 0x01 + +#define P50_MBOX_PARAM_LED 0x12 +#define P50_MBOX_PARAM_BTN 0x13 + + +struct p50_gpio { + struct gpio_chip gc; + struct mutex lock; + unsigned long base; + struct platform_device *leds_pdev; + struct platform_device *keys_pdev; +}; + +static struct platform_device *gpio_pdev; + +static int gpio_params[] = { + [P50_GPIO_LINE_LED] = P50_MBOX_PARAM_LED, + [P50_GPIO_LINE_BTN] = P50_MBOX_PARAM_BTN, +}; + +static const char * const gpio_names[] = { + [P50_GPIO_LINE_LED] = "identify-led", + [P50_GPIO_LINE_BTN] = "identify-button", +}; + + +static struct gpiod_lookup_table p50_gpio_led_table = { + .dev_id = "leds-gpio", + .table = { + GPIO_LOOKUP_IDX(DRIVER_NAME, P50_GPIO_LINE_LED, NULL, 0, GPIO_ACTIVE_HIGH), + {} + } +}; + +/* GPIO LEDs */ +static struct gpio_led leds[] = { + { .name = "identify" } +}; + +static struct gpio_led_platform_data leds_pdata = { + .num_leds = ARRAY_SIZE(leds), + .leds = leds, +}; + +/* GPIO keyboard */ +static struct gpio_keys_button buttons[] = { + { + .code = KEY_RESTART, + .gpio = P50_GPIO_LINE_BTN, + .active_low = 1, + .type = EV_KEY, + .value = 1, + }, +}; + +static struct gpio_keys_platform_data keys_pdata = { + .buttons = buttons, + .nbuttons = ARRAY_SIZE(buttons), + .poll_interval = 100, + .rep = 0, + .name = "identify", +}; + + +/* low level access routines */ + +static int p50_wait_ec(struct p50_gpio *p50, int mask, int expected) +{ + int i, val; + + for (i = 0; i < 100; i++) { + val = inb(p50->base + P50_PORT_CMD) & mask; + if (val == expected) + return 0; + usleep_range(500, 2000); + } + + dev_err(p50->gc.parent, "Timed out waiting for EC (0x%x)\n", val); + return -ETIMEDOUT; +} + + +static int p50_read_mbox_reg(struct p50_gpio *p50, int reg) +{ + int ret; + + ret = p50_wait_ec(p50, P50_STATUS_IBF, 0); + if (ret) + return ret; + + /* clear output buffer flag, prevent unfinished commands */ + inb(p50->base + P50_PORT_DATA); + + /* cmd/address */ + outb(P50_CMD_READ | reg, p50->base + P50_PORT_CMD); + + ret = p50_wait_ec(p50, P50_STATUS_OBF, P50_STATUS_OBF); + if (ret) + return ret; + + return inb(p50->base + P50_PORT_DATA); +} + +static int p50_write_mbox_reg(struct p50_gpio *p50, int reg, int val) +{ + int ret; + + ret = p50_wait_ec(p50, P50_STATUS_IBF, 0); + if (ret) + return ret; + + /* cmd/address */ + outb(P50_CMD_WRITE | reg, p50->base + P50_PORT_CMD); + + ret = p50_wait_ec(p50, P50_STATUS_IBF, 0); + if (ret) + return ret; + + /* data */ + outb(val, p50->base + P50_PORT_DATA); + + return 0; +} + + +/* mbox routines */ + +static int p50_wait_mbox_idle(struct p50_gpio *p50) +{ + int i, val; + + for (i = 0; i < 1000; i++) { + val = p50_read_mbox_reg(p50, P50_MBOX_REG_CMD); + /* cmd is 0 when idle */ + if (val <= 0) + return val; + + usleep_range(500, 2000); + } + + dev_err(p50->gc.parent, "Timed out waiting for EC mbox idle (CMD: 0x%x)\n", val); + + return -ETIMEDOUT; +} + +static int p50_send_mbox_cmd(struct p50_gpio *p50, int cmd, int param, int data) +{ + int ret; + + ret = p50_wait_mbox_idle(p50); + if (ret) + return ret; + + ret = p50_write_mbox_reg(p50, P50_MBOX_REG_DATA, data); + if (ret) + return ret; + + ret = p50_write_mbox_reg(p50, P50_MBOX_REG_PARAM, param); + if (ret) + return ret; + + ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, cmd); + if (ret) + return ret; + + ret = p50_wait_mbox_idle(p50); + if (ret) + return ret; + + ret = p50_read_mbox_reg(p50, P50_MBOX_REG_STATUS); + if (ret < 0) + return ret; + + if (ret == P50_MBOX_STATUS_SUCCESS) + return 0; + + dev_err(p50->gc.parent, "Mbox command failed (CMD=0x%x STAT=0x%x PARAM=0x%x DATA=0x%x)\n", + cmd, ret, param, data); + + return -EIO; +} + + +/* gpio routines */ + +static int p50_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + switch (offset) { + case P50_GPIO_LINE_BTN: + return GPIO_LINE_DIRECTION_IN; + + case P50_GPIO_LINE_LED: + return GPIO_LINE_DIRECTION_OUT; + + default: + return -EINVAL; + } +} + +static int p50_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct p50_gpio *p50 = gpiochip_get_data(gc); + int ret; + + mutex_lock(&p50->lock); + + ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_READ_GPIO, gpio_params[offset], 0); + if (ret == 0) + ret = p50_read_mbox_reg(p50, P50_MBOX_REG_DATA); + + mutex_unlock(&p50->lock); + + return ret; +} + +static void p50_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct p50_gpio *p50 = gpiochip_get_data(gc); + + mutex_lock(&p50->lock); + + p50_send_mbox_cmd(p50, P50_MBOX_CMD_WRITE_GPIO, gpio_params[offset], value); + + mutex_unlock(&p50->lock); +} + +static int p50_gpio_probe(struct platform_device *pdev) +{ + struct p50_gpio *p50; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(&pdev->dev, "Cannot get I/O ports\n"); + return -ENODEV; + } + + if (!devm_request_region(&pdev->dev, res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "Unable to reserve I/O region\n"); + return -EBUSY; + } + + p50 = devm_kzalloc(&pdev->dev, sizeof(*p50), GFP_KERNEL); + if (!p50) + return -ENOMEM; + + platform_set_drvdata(pdev, p50); + mutex_init(&p50->lock); + p50->base = res->start; + p50->gc.owner = THIS_MODULE; + p50->gc.parent = &pdev->dev; + p50->gc.label = dev_name(&pdev->dev); + p50->gc.ngpio = ARRAY_SIZE(gpio_names); + p50->gc.names = gpio_names; + p50->gc.can_sleep = true; + p50->gc.base = -1; + p50->gc.get_direction = p50_gpio_get_direction; + p50->gc.get = p50_gpio_get; + p50->gc.set = p50_gpio_set; + + + /* reset mbox */ + ret = p50_wait_mbox_idle(p50); + if (ret) + return ret; + + ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, P50_MBOX_CMD_CLEAR); + if (ret) + return ret; + + ret = p50_wait_mbox_idle(p50); + if (ret) + return ret; + + + ret = devm_gpiochip_add_data(&pdev->dev, &p50->gc, p50); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip: %d\n", ret); + return ret; + } + + gpiod_add_lookup_table(&p50_gpio_led_table); + + p50->leds_pdev = platform_device_register_data(&pdev->dev, + "leds-gpio", PLATFORM_DEVID_NONE, &leds_pdata, sizeof(leds_pdata)); + + if (IS_ERR(p50->leds_pdev)) { + ret = PTR_ERR(p50->leds_pdev); + dev_err(&pdev->dev, "Could not register leds-gpio: %d\n", ret); + goto err_leds; + } + + /* gpio-keys-polled uses old-style gpio interface, pass the right identifier */ + buttons[0].gpio += p50->gc.base; + + p50->keys_pdev = + platform_device_register_data(&pdev->dev, "gpio-keys-polled", + PLATFORM_DEVID_NONE, + &keys_pdata, sizeof(keys_pdata)); + + if (IS_ERR(p50->keys_pdev)) { + ret = PTR_ERR(p50->keys_pdev); + dev_err(&pdev->dev, "Could not register gpio-keys-polled: %d\n", ret); + goto err_keys; + } + + return 0; + +err_keys: + platform_device_unregister(p50->leds_pdev); +err_leds: + gpiod_remove_lookup_table(&p50_gpio_led_table); + + return ret; +} + +static int p50_gpio_remove(struct platform_device *pdev) +{ + struct p50_gpio *p50 = platform_get_drvdata(pdev); + + platform_device_unregister(p50->keys_pdev); + platform_device_unregister(p50->leds_pdev); + + gpiod_remove_lookup_table(&p50_gpio_led_table); + + return 0; +} + +static struct platform_driver p50_gpio_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = p50_gpio_probe, + .remove = p50_gpio_remove, +}; + +/* Board setup */ +static const struct dmi_system_id dmi_ids[] __initconst = { + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Barco"), + DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "P50") + }, + }, + {} +}; +MODULE_DEVICE_TABLE(dmi, dmi_ids); + +static int __init p50_module_init(void) +{ + struct resource res = DEFINE_RES_IO(P50_GPIO_IO_PORT_BASE, P50_PORT_CMD + 1); + + if (!dmi_first_match(dmi_ids)) + return -ENODEV; + + platform_driver_register(&p50_gpio_driver); + + gpio_pdev = platform_device_register_simple(DRIVER_NAME, PLATFORM_DEVID_NONE, &res, 1); + if (IS_ERR(gpio_pdev)) { + pr_err("failed registering %s: %ld\n", DRIVER_NAME, PTR_ERR(gpio_pdev)); + platform_driver_unregister(&p50_gpio_driver); + return PTR_ERR(gpio_pdev); + } + + return 0; +} + +static void __exit p50_module_exit(void) +{ + platform_device_unregister(gpio_pdev); + platform_driver_unregister(&p50_gpio_driver); +} + +module_init(p50_module_init); +module_exit(p50_module_exit); + +MODULE_AUTHOR("Santosh Kumar Yadav, Barco NV "); +MODULE_DESCRIPTION("Barco P50 identify GPIOs driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3