diff options
Diffstat (limited to 'drivers/platform')
106 files changed, 3960 insertions, 1848 deletions
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index b7dbaf77b6db..1b2f2bd09662 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -237,12 +237,19 @@ config CROS_EC_SYSFS To compile this driver as a module, choose M here: the module will be called cros_ec_sysfs. +config CROS_EC_TYPEC_ALTMODES + bool + help + Selectable symbol to enable altmodes. + config CROS_EC_TYPEC tristate "ChromeOS EC Type-C Connector Control" depends on MFD_CROS_EC_DEV && TYPEC depends on CROS_USBPD_NOTIFY depends on USB_ROLE_SWITCH default MFD_CROS_EC_DEV + select CROS_EC_TYPEC_ALTMODES if TYPEC_DP_ALTMODE + select CROS_EC_TYPEC_ALTMODES if TYPEC_TBT_ALTMODE help If you say Y here, you get support for accessing Type C connector information from the Chrome OS EC. diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index fb8335458a22..1a5a484563cc 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -19,7 +19,11 @@ obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o obj-$(CONFIG_CROS_EC_UART) += cros_ec_uart.o cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_mec.o cros-ec-typec-objs := cros_ec_typec.o cros_typec_vdm.o +ifneq ($(CONFIG_CROS_EC_TYPEC_ALTMODES),) + cros-ec-typec-objs += cros_typec_altmode.o +endif obj-$(CONFIG_CROS_EC_TYPEC) += cros-ec-typec.o + obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o cros_ec_trace.o obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c index e821b3d39590..110771a8645e 100644 --- a/drivers/platform/chrome/cros_ec.c +++ b/drivers/platform/chrome/cros_ec.c @@ -204,6 +204,11 @@ int cros_ec_register(struct cros_ec_device *ec_dev) mutex_init(&ec_dev->lock); lockdep_set_class(&ec_dev->lock, &ec_dev->lockdep_key); + /* Send RWSIG continue to jump to RW for devices using RWSIG. */ + err = cros_ec_rwsig_continue(ec_dev); + if (err) + dev_info(dev, "Failed to continue RWSIG: %d\n", err); + err = cros_ec_query_all(ec_dev); if (err) { dev_err(dev, "Cannot identify the EC: error %d\n", err); diff --git a/drivers/platform/chrome/cros_ec_i2c.c b/drivers/platform/chrome/cros_ec_i2c.c index 62662ba5bf6e..38af97cdaab2 100644 --- a/drivers/platform/chrome/cros_ec_i2c.c +++ b/drivers/platform/chrome/cros_ec_i2c.c @@ -305,7 +305,8 @@ static int cros_ec_i2c_probe(struct i2c_client *client) ec_dev->phys_name = client->adapter->name; ec_dev->din_size = sizeof(struct ec_host_response_i2c) + sizeof(struct ec_response_get_protocol_info); - ec_dev->dout_size = sizeof(struct ec_host_request_i2c); + ec_dev->dout_size = sizeof(struct ec_host_request_i2c) + + sizeof(struct ec_params_rwsig_action); err = cros_ec_register(ec_dev); if (err) { diff --git a/drivers/platform/chrome/cros_ec_ishtp.c b/drivers/platform/chrome/cros_ec_ishtp.c index 5ac37bd024c8..7e7190b30cbb 100644 --- a/drivers/platform/chrome/cros_ec_ishtp.c +++ b/drivers/platform/chrome/cros_ec_ishtp.c @@ -557,7 +557,7 @@ static int cros_ec_dev_init(struct ishtp_cl_data *client_data) ec_dev->phys_name = dev_name(dev); ec_dev->din_size = sizeof(struct cros_ish_in_msg) + sizeof(struct ec_response_get_protocol_info); - ec_dev->dout_size = sizeof(struct cros_ish_out_msg); + ec_dev->dout_size = sizeof(struct cros_ish_out_msg) + sizeof(struct ec_params_rwsig_action); return cros_ec_register(ec_dev); } diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index 8470b7f2b135..be319949b941 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -30,6 +30,7 @@ #define DRV_NAME "cros_ec_lpcs" #define ACPI_DRV_NAME "GOOG0004" +#define FRMW_ACPI_DRV_NAME "FRMWC004" /* True if ACPI device is present */ static bool cros_ec_lpc_acpi_device_found; @@ -70,13 +71,8 @@ struct lpc_driver_data { /** * struct cros_ec_lpc - LPC device-specific data * @mmio_memory_base: The first I/O port addressing EC mapped memory. - */ -struct cros_ec_lpc { - u16 mmio_memory_base; -}; - -/** - * struct lpc_driver_ops - LPC driver operations + * @base: For EC supporting memory mapping, base address of the mapped region. + * @mem32: Information about the memory mapped register region, if present. * @read: Copy length bytes from EC address offset into buffer dest. * Returns a negative error code on error, or the 8-bit checksum * of all bytes read. @@ -84,18 +80,21 @@ struct cros_ec_lpc { * Returns a negative error code on error, or the 8-bit checksum * of all bytes written. */ -struct lpc_driver_ops { - int (*read)(unsigned int offset, unsigned int length, u8 *dest); - int (*write)(unsigned int offset, unsigned int length, const u8 *msg); +struct cros_ec_lpc { + u16 mmio_memory_base; + void __iomem *base; + struct acpi_resource_fixed_memory32 mem32; + int (*read)(struct cros_ec_lpc *ec_lpc, unsigned int offset, + unsigned int length, u8 *dest); + int (*write)(struct cros_ec_lpc *ec_lpc, unsigned int offset, + unsigned int length, const u8 *msg); }; -static struct lpc_driver_ops cros_ec_lpc_ops = { }; - /* * A generic instance of the read function of struct lpc_driver_ops, used for * the LPC EC. */ -static int cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, +static int cros_ec_lpc_read_bytes(struct cros_ec_lpc *_, unsigned int offset, unsigned int length, u8 *dest) { u8 sum = 0; @@ -114,7 +113,7 @@ static int cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, * A generic instance of the write function of struct lpc_driver_ops, used for * the LPC EC. */ -static int cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, +static int cros_ec_lpc_write_bytes(struct cros_ec_lpc *_, unsigned int offset, unsigned int length, const u8 *msg) { u8 sum = 0; @@ -133,8 +132,8 @@ static int cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, * An instance of the read function of struct lpc_driver_ops, used for the * MEC variant of LPC EC. */ -static int cros_ec_lpc_mec_read_bytes(unsigned int offset, unsigned int length, - u8 *dest) +static int cros_ec_lpc_mec_read_bytes(struct cros_ec_lpc *ec_lpc, unsigned int offset, + unsigned int length, u8 *dest) { int in_range = cros_ec_lpc_mec_in_range(offset, length); @@ -145,15 +144,15 @@ static int cros_ec_lpc_mec_read_bytes(unsigned int offset, unsigned int length, cros_ec_lpc_io_bytes_mec(MEC_IO_READ, offset - EC_HOST_CMD_REGION0, length, dest) : - cros_ec_lpc_read_bytes(offset, length, dest); + cros_ec_lpc_read_bytes(ec_lpc, offset, length, dest); } /* * An instance of the write function of struct lpc_driver_ops, used for the * MEC variant of LPC EC. */ -static int cros_ec_lpc_mec_write_bytes(unsigned int offset, unsigned int length, - const u8 *msg) +static int cros_ec_lpc_mec_write_bytes(struct cros_ec_lpc *ec_lpc, unsigned int offset, + unsigned int length, const u8 *msg) { int in_range = cros_ec_lpc_mec_in_range(offset, length); @@ -164,10 +163,50 @@ static int cros_ec_lpc_mec_write_bytes(unsigned int offset, unsigned int length, cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, offset - EC_HOST_CMD_REGION0, length, (u8 *)msg) : - cros_ec_lpc_write_bytes(offset, length, msg); + cros_ec_lpc_write_bytes(ec_lpc, offset, length, msg); +} + +static int cros_ec_lpc_direct_read(struct cros_ec_lpc *ec_lpc, unsigned int offset, + unsigned int length, u8 *dest) +{ + int sum = 0; + int i; + + if (offset < EC_HOST_CMD_REGION0 || offset > EC_LPC_ADDR_MEMMAP + + EC_MEMMAP_SIZE) { + return cros_ec_lpc_read_bytes(ec_lpc, offset, length, dest); + } + + for (i = 0; i < length; ++i) { + dest[i] = readb(ec_lpc->base + offset - EC_HOST_CMD_REGION0 + i); + sum += dest[i]; + } + + /* Return checksum of all bytes read */ + return sum; +} + +static int cros_ec_lpc_direct_write(struct cros_ec_lpc *ec_lpc, unsigned int offset, + unsigned int length, const u8 *msg) +{ + int sum = 0; + int i; + + if (offset < EC_HOST_CMD_REGION0 || offset > EC_LPC_ADDR_MEMMAP + + EC_MEMMAP_SIZE) { + return cros_ec_lpc_write_bytes(ec_lpc, offset, length, msg); + } + + for (i = 0; i < length; ++i) { + writeb(msg[i], ec_lpc->base + offset - EC_HOST_CMD_REGION0 + i); + sum += msg[i]; + } + + /* Return checksum of all bytes written */ + return sum; } -static int ec_response_timed_out(void) +static int ec_response_timed_out(struct cros_ec_lpc *ec_lpc) { unsigned long one_second = jiffies + HZ; u8 data; @@ -175,7 +214,7 @@ static int ec_response_timed_out(void) usleep_range(200, 300); do { - ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_CMD, 1, &data); + ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_HOST_CMD, 1, &data); if (ret < 0) return ret; if (!(data & EC_LPC_STATUS_BUSY_MASK)) @@ -189,6 +228,7 @@ static int ec_response_timed_out(void) static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, struct cros_ec_command *msg) { + struct cros_ec_lpc *ec_lpc = ec->priv; struct ec_host_response response; u8 sum; int ret = 0; @@ -199,17 +239,17 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, goto done; /* Write buffer */ - ret = cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout); + ret = ec_lpc->write(ec_lpc, EC_LPC_ADDR_HOST_PACKET, ret, ec->dout); if (ret < 0) goto done; /* Here we go */ sum = EC_COMMAND_PROTOCOL_3; - ret = cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum); + ret = ec_lpc->write(ec_lpc, EC_LPC_ADDR_HOST_CMD, 1, &sum); if (ret < 0) goto done; - ret = ec_response_timed_out(); + ret = ec_response_timed_out(ec_lpc); if (ret < 0) goto done; if (ret) { @@ -219,7 +259,7 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, } /* Check result */ - ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum); + ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_HOST_DATA, 1, &sum); if (ret < 0) goto done; msg->result = ret; @@ -229,7 +269,7 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, /* Read back response */ dout = (u8 *)&response; - ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET, sizeof(response), + ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_HOST_PACKET, sizeof(response), dout); if (ret < 0) goto done; @@ -246,7 +286,7 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, } /* Read response and process checksum */ - ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET + + ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_HOST_PACKET + sizeof(response), response.data_len, msg->data); if (ret < 0) @@ -270,6 +310,7 @@ done: static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, struct cros_ec_command *msg) { + struct cros_ec_lpc *ec_lpc = ec->priv; struct ec_lpc_host_args args; u8 sum; int ret = 0; @@ -291,7 +332,7 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, sum = msg->command + args.flags + args.command_version + args.data_size; /* Copy data and update checksum */ - ret = cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PARAM, msg->outsize, + ret = ec_lpc->write(ec_lpc, EC_LPC_ADDR_HOST_PARAM, msg->outsize, msg->data); if (ret < 0) goto done; @@ -299,18 +340,18 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, /* Finalize checksum and write args */ args.checksum = sum; - ret = cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_ARGS, sizeof(args), + ret = ec_lpc->write(ec_lpc, EC_LPC_ADDR_HOST_ARGS, sizeof(args), (u8 *)&args); if (ret < 0) goto done; /* Here we go */ sum = msg->command; - ret = cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum); + ret = ec_lpc->write(ec_lpc, EC_LPC_ADDR_HOST_CMD, 1, &sum); if (ret < 0) goto done; - ret = ec_response_timed_out(); + ret = ec_response_timed_out(ec_lpc); if (ret < 0) goto done; if (ret) { @@ -320,7 +361,7 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, } /* Check result */ - ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum); + ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_HOST_DATA, 1, &sum); if (ret < 0) goto done; msg->result = ret; @@ -329,7 +370,7 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, goto done; /* Read back args */ - ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_ARGS, sizeof(args), (u8 *)&args); + ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_HOST_ARGS, sizeof(args), (u8 *)&args); if (ret < 0) goto done; @@ -345,7 +386,7 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, sum = msg->command + args.flags + args.command_version + args.data_size; /* Read response and update checksum */ - ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PARAM, args.data_size, + ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_HOST_PARAM, args.data_size, msg->data); if (ret < 0) goto done; @@ -381,7 +422,7 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset, /* fixed length */ if (bytes) { - ret = cros_ec_lpc_ops.read(ec_lpc->mmio_memory_base + offset, bytes, s); + ret = ec_lpc->read(ec_lpc, ec_lpc->mmio_memory_base + offset, bytes, s); if (ret < 0) return ret; return bytes; @@ -389,7 +430,7 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset, /* string */ for (; i < EC_MEMMAP_SIZE; i++, s++) { - ret = cros_ec_lpc_ops.read(ec_lpc->mmio_memory_base + i, 1, s); + ret = ec_lpc->read(ec_lpc, ec_lpc->mmio_memory_base + i, 1, s); if (ret < 0) return ret; cnt++; @@ -419,7 +460,7 @@ static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data) return; } - if (ec_dev->mkbp_event_supported) + if (value == ACPI_NOTIFY_CROS_EC_MKBP && ec_dev->mkbp_event_supported) do { ret = cros_ec_get_next_event(ec_dev, NULL, &ec_has_more_events); @@ -453,6 +494,20 @@ static struct acpi_device *cros_ec_lpc_get_device(const char *id) return adev; } +static acpi_status cros_ec_lpc_resources(struct acpi_resource *res, void *data) +{ + struct cros_ec_lpc *ec_lpc = data; + + switch (res->type) { + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + ec_lpc->mem32 = res->data.fixed_memory32; + break; + default: + break; + } + return AE_OK; +} + static int cros_ec_lpc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -460,7 +515,7 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) acpi_status status; struct cros_ec_device *ec_dev; struct cros_ec_lpc *ec_lpc; - struct lpc_driver_data *driver_data; + const struct lpc_driver_data *driver_data; u8 buf[2] = {}; int irq, ret; u32 quirks; @@ -472,6 +527,9 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) ec_lpc->mmio_memory_base = EC_LPC_ADDR_MEMMAP; driver_data = platform_get_drvdata(pdev); + if (!driver_data) + driver_data = acpi_device_get_match_data(dev); + if (driver_data) { quirks = driver_data->quirks; @@ -492,8 +550,7 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) } if (quirks & CROS_EC_LPC_QUIRK_AML_MUTEX) { - const char *name - = driver_data->quirk_aml_mutex_name; + const char *name = driver_data->quirk_aml_mutex_name; ret = cros_ec_lpc_mec_acpi_mutex(ACPI_COMPANION(dev), name); if (ret) { dev_err(dev, "failed to get AML mutex '%s'", name); @@ -502,30 +559,49 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) dev_info(dev, "got AML mutex '%s'", name); } } - - /* - * The Framework Laptop (and possibly other non-ChromeOS devices) - * only exposes the eight I/O ports that are required for the Microchip EC. - * Requesting a larger reservation will fail. - */ - if (!devm_request_region(dev, EC_HOST_CMD_REGION0, - EC_HOST_CMD_MEC_REGION_SIZE, dev_name(dev))) { - dev_err(dev, "couldn't reserve MEC region\n"); - return -EBUSY; + adev = ACPI_COMPANION(dev); + if (adev) { + /* + * Retrieve the resource information in the CRS register, if available. + */ + status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS, + cros_ec_lpc_resources, ec_lpc); + if (ACPI_SUCCESS(status) && ec_lpc->mem32.address_length) { + ec_lpc->base = devm_ioremap(dev, + ec_lpc->mem32.address, + ec_lpc->mem32.address_length); + if (!ec_lpc->base) + return -EINVAL; + + ec_lpc->read = cros_ec_lpc_direct_read; + ec_lpc->write = cros_ec_lpc_direct_write; + } } + if (!ec_lpc->read) { + /* + * The Framework Laptop (and possibly other non-ChromeOS devices) + * only exposes the eight I/O ports that are required for the Microchip EC. + * Requesting a larger reservation will fail. + */ + if (!devm_request_region(dev, EC_HOST_CMD_REGION0, + EC_HOST_CMD_MEC_REGION_SIZE, dev_name(dev))) { + dev_err(dev, "couldn't reserve MEC region\n"); + return -EBUSY; + } - cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0, - EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE); + cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0, + EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE); - /* - * Read the mapped ID twice, the first one is assuming the - * EC is a Microchip Embedded Controller (MEC) variant, if the - * protocol fails, fallback to the non MEC variant and try to - * read again the ID. - */ - cros_ec_lpc_ops.read = cros_ec_lpc_mec_read_bytes; - cros_ec_lpc_ops.write = cros_ec_lpc_mec_write_bytes; - ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf); + /* + * Read the mapped ID twice, the first one is assuming the + * EC is a Microchip Embedded Controller (MEC) variant, if the + * protocol fails, fallback to the non MEC variant and try to + * read again the ID. + */ + ec_lpc->read = cros_ec_lpc_mec_read_bytes; + ec_lpc->write = cros_ec_lpc_mec_write_bytes; + } + ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf); if (ret < 0) return ret; if (buf[0] != 'E' || buf[1] != 'C') { @@ -536,9 +612,9 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) } /* Re-assign read/write operations for the non MEC variant */ - cros_ec_lpc_ops.read = cros_ec_lpc_read_bytes; - cros_ec_lpc_ops.write = cros_ec_lpc_write_bytes; - ret = cros_ec_lpc_ops.read(ec_lpc->mmio_memory_base + EC_MEMMAP_ID, 2, + ec_lpc->read = cros_ec_lpc_read_bytes; + ec_lpc->write = cros_ec_lpc_write_bytes; + ret = ec_lpc->read(ec_lpc, ec_lpc->mmio_memory_base + EC_MEMMAP_ID, 2, buf); if (ret < 0) return ret; @@ -573,7 +649,7 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) ec_dev->cmd_readmem = cros_ec_lpc_readmem; ec_dev->din_size = sizeof(struct ec_host_response) + sizeof(struct ec_response_get_protocol_info); - ec_dev->dout_size = sizeof(struct ec_host_request); + ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action); ec_dev->priv = ec_lpc; /* @@ -598,7 +674,6 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) * Connect a notify handler to process MKBP messages if we have a * companion ACPI device. */ - adev = ACPI_COMPANION(dev); if (adev) { status = acpi_install_notify_handler(adev->handle, ACPI_ALL_NOTIFY, @@ -625,12 +700,6 @@ static void cros_ec_lpc_remove(struct platform_device *pdev) cros_ec_unregister(ec_dev); } -static const struct acpi_device_id cros_ec_lpc_acpi_device_ids[] = { - { ACPI_DRV_NAME, 0 }, - { } -}; -MODULE_DEVICE_TABLE(acpi, cros_ec_lpc_acpi_device_ids); - static const struct lpc_driver_data framework_laptop_npcx_lpc_driver_data __initconst = { .quirks = CROS_EC_LPC_QUIRK_REMAP_MEMORY, .quirk_mmio_memory_base = 0xE00, @@ -642,6 +711,13 @@ static const struct lpc_driver_data framework_laptop_mec_lpc_driver_data __initc .quirk_aml_mutex_name = "ECMT", }; +static const struct acpi_device_id cros_ec_lpc_acpi_device_ids[] = { + { ACPI_DRV_NAME, 0 }, + { FRMW_ACPI_DRV_NAME, (kernel_ulong_t)&framework_laptop_npcx_lpc_driver_data }, + { } +}; +MODULE_DEVICE_TABLE(acpi, cros_ec_lpc_acpi_device_ids); + static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = { { /* @@ -795,7 +871,8 @@ static int __init cros_ec_lpc_init(void) int ret; const struct dmi_system_id *dmi_match; - cros_ec_lpc_acpi_device_found = !!cros_ec_lpc_get_device(ACPI_DRV_NAME); + cros_ec_lpc_acpi_device_found = !!cros_ec_lpc_get_device(ACPI_DRV_NAME) || + !!cros_ec_lpc_get_device(FRMW_ACPI_DRV_NAME); dmi_match = dmi_first_match(cros_ec_lpc_dmi_table); diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index 5c9a53dffcf9..877b107fee4b 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -15,6 +15,8 @@ #include "cros_ec_trace.h" #define EC_COMMAND_RETRIES 50 +#define RWSIG_CONTINUE_RETRIES 8 +#define RWSIG_CONTINUE_MAX_ERRORS_IN_ROW 3 static const int cros_ec_error_map[] = { [EC_RES_INVALID_COMMAND] = -EOPNOTSUPP, @@ -288,6 +290,64 @@ exit: return ret; } +int cros_ec_rwsig_continue(struct cros_ec_device *ec_dev) +{ + struct cros_ec_command *msg; + struct ec_params_rwsig_action *rwsig_action; + int ret = 0; + int error_count = 0; + + ec_dev->proto_version = 3; + + msg = kmalloc(sizeof(*msg) + sizeof(*rwsig_action), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->version = 0; + msg->command = EC_CMD_RWSIG_ACTION; + msg->insize = 0; + msg->outsize = sizeof(*rwsig_action); + + rwsig_action = (struct ec_params_rwsig_action *)msg->data; + rwsig_action->action = RWSIG_ACTION_CONTINUE; + + for (int i = 0; i < RWSIG_CONTINUE_RETRIES; i++) { + ret = cros_ec_send_command(ec_dev, msg); + + if (ret < 0) { + if (++error_count >= RWSIG_CONTINUE_MAX_ERRORS_IN_ROW) + break; + } else if (msg->result == EC_RES_INVALID_COMMAND) { + /* + * If EC_RES_INVALID_COMMAND is retured, it means RWSIG + * is not supported or EC is already in RW, so there is + * nothing left to do. + */ + break; + } else if (msg->result != EC_RES_SUCCESS) { + /* Unexpected command error. */ + ret = cros_ec_map_error(msg->result); + break; + } else { + /* + * The EC_CMD_RWSIG_ACTION succeed. Send the command + * more times, to make sure EC is in RW. A following + * command can timeout, because EC may need some time to + * initialize after jump to RW. + */ + error_count = 0; + } + + if (ret != -ETIMEDOUT) + usleep_range(90000, 100000); + } + + kfree(msg); + + return ret; +} +EXPORT_SYMBOL(cros_ec_rwsig_continue); + static int cros_ec_get_proto_info(struct cros_ec_device *ec_dev, int devidx) { struct cros_ec_command *msg; @@ -306,15 +366,6 @@ static int cros_ec_get_proto_info(struct cros_ec_device *ec_dev, int devidx) msg->insize = sizeof(*info); ret = cros_ec_send_command(ec_dev, msg); - /* - * Send command once again when timeout occurred. - * Fingerprint MCU (FPMCU) is restarted during system boot which - * introduces small window in which FPMCU won't respond for any - * messages sent by kernel. There is no need to wait before next - * attempt because we waited at least EC_MSG_DEADLINE_MS. - */ - if (ret == -ETIMEDOUT) - ret = cros_ec_send_command(ec_dev, msg); if (ret < 0) { dev_dbg(ec_dev->dev, diff --git a/drivers/platform/chrome/cros_ec_rpmsg.c b/drivers/platform/chrome/cros_ec_rpmsg.c index 39d3b50a7c09..bc2666491db1 100644 --- a/drivers/platform/chrome/cros_ec_rpmsg.c +++ b/drivers/platform/chrome/cros_ec_rpmsg.c @@ -231,7 +231,7 @@ static int cros_ec_rpmsg_probe(struct rpmsg_device *rpdev) ec_dev->phys_name = dev_name(&rpdev->dev); ec_dev->din_size = sizeof(struct ec_host_response) + sizeof(struct ec_response_get_protocol_info); - ec_dev->dout_size = sizeof(struct ec_host_request); + ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action); dev_set_drvdata(dev, ec_dev); ec_rpmsg->rpdev = rpdev; diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c index 86a3d32a7763..8ca0f854e7ac 100644 --- a/drivers/platform/chrome/cros_ec_spi.c +++ b/drivers/platform/chrome/cros_ec_spi.c @@ -715,7 +715,7 @@ static int cros_ec_spi_devm_high_pri_alloc(struct device *dev, int err; ec_spi->high_pri_worker = - kthread_create_worker(0, "cros_ec_spi_high_pri"); + kthread_run_worker(0, "cros_ec_spi_high_pri"); if (IS_ERR(ec_spi->high_pri_worker)) { err = PTR_ERR(ec_spi->high_pri_worker); @@ -766,7 +766,7 @@ static int cros_ec_spi_probe(struct spi_device *spi) ec_dev->din_size = EC_MSG_PREAMBLE_COUNT + sizeof(struct ec_host_response) + sizeof(struct ec_response_get_protocol_info); - ec_dev->dout_size = sizeof(struct ec_host_request); + ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action); ec_spi->last_transfer_ns = ktime_get_ns(); diff --git a/drivers/platform/chrome/cros_ec_trace.c b/drivers/platform/chrome/cros_ec_trace.c index 425e9441b7ca..9827b3117597 100644 --- a/drivers/platform/chrome/cros_ec_trace.c +++ b/drivers/platform/chrome/cros_ec_trace.c @@ -122,8 +122,10 @@ TRACE_SYMBOL(EC_CMD_ENTERING_MODE), \ TRACE_SYMBOL(EC_CMD_I2C_PASSTHRU_PROTECT), \ TRACE_SYMBOL(EC_CMD_CEC_WRITE_MSG), \ + TRACE_SYMBOL(EC_CMD_CEC_READ_MSG), \ TRACE_SYMBOL(EC_CMD_CEC_SET), \ TRACE_SYMBOL(EC_CMD_CEC_GET), \ + TRACE_SYMBOL(EC_CMD_CEC_PORT_COUNT), \ TRACE_SYMBOL(EC_CMD_EC_CODEC), \ TRACE_SYMBOL(EC_CMD_EC_CODEC_DMIC), \ TRACE_SYMBOL(EC_CMD_EC_CODEC_I2S_RX), \ @@ -161,11 +163,18 @@ TRACE_SYMBOL(EC_CMD_ADC_READ), \ TRACE_SYMBOL(EC_CMD_ROLLBACK_INFO), \ TRACE_SYMBOL(EC_CMD_AP_RESET), \ + TRACE_SYMBOL(EC_CMD_PCHG_COUNT), \ + TRACE_SYMBOL(EC_CMD_PCHG), \ + TRACE_SYMBOL(EC_CMD_PCHG_UPDATE), \ TRACE_SYMBOL(EC_CMD_REGULATOR_GET_INFO), \ TRACE_SYMBOL(EC_CMD_REGULATOR_ENABLE), \ TRACE_SYMBOL(EC_CMD_REGULATOR_IS_ENABLED), \ TRACE_SYMBOL(EC_CMD_REGULATOR_SET_VOLTAGE), \ TRACE_SYMBOL(EC_CMD_REGULATOR_GET_VOLTAGE), \ + TRACE_SYMBOL(EC_CMD_TYPEC_DISCOVERY), \ + TRACE_SYMBOL(EC_CMD_TYPEC_CONTROL), \ + TRACE_SYMBOL(EC_CMD_TYPEC_STATUS), \ + TRACE_SYMBOL(EC_CMD_TYPEC_VDM_RESPONSE), \ TRACE_SYMBOL(EC_CMD_CR51_BASE), \ TRACE_SYMBOL(EC_CMD_CR51_LAST), \ TRACE_SYMBOL(EC_CMD_FP_PASSTHRU), \ @@ -184,6 +193,7 @@ TRACE_SYMBOL(EC_CMD_BATTERY_GET_STATIC), \ TRACE_SYMBOL(EC_CMD_BATTERY_GET_DYNAMIC), \ TRACE_SYMBOL(EC_CMD_CHARGER_CONTROL), \ + TRACE_SYMBOL(EC_CMD_USB_PD_MUX_ACK), \ TRACE_SYMBOL(EC_CMD_BOARD_SPECIFIC_BASE), \ TRACE_SYMBOL(EC_CMD_BOARD_SPECIFIC_LAST) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index ae2f86296954..6ee182101bc9 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -18,6 +18,7 @@ #include "cros_ec_typec.h" #include "cros_typec_vdm.h" +#include "cros_typec_altmode.h" #define DRV_NAME "cros-ec-typec" @@ -290,30 +291,32 @@ static int cros_typec_register_port_altmodes(struct cros_typec_data *typec, struct typec_altmode *amode; /* All PD capable CrOS devices are assumed to support DP altmode. */ + memset(&desc, 0, sizeof(desc)); desc.svid = USB_TYPEC_DP_SID; desc.mode = USB_TYPEC_DP_MODE; desc.vdo = DP_PORT_VDO; - amode = typec_port_register_altmode(port->port, &desc); + amode = cros_typec_register_displayport(port, &desc, + typec->ap_driven_altmode); if (IS_ERR(amode)) return PTR_ERR(amode); port->port_altmode[CROS_EC_ALTMODE_DP] = amode; - typec_altmode_set_drvdata(amode, port); - amode->ops = &port_amode_ops; /* * Register TBT compatibility alt mode. The EC will not enter the mode - * if it doesn't support it, so it's safe to register it unconditionally - * here for now. + * if it doesn't support it and it will not enter automatically by + * design so we can use the |ap_driven_altmode| feature to check if we + * should register it. */ - memset(&desc, 0, sizeof(desc)); - desc.svid = USB_TYPEC_TBT_SID; - desc.mode = TYPEC_ANY_MODE; - amode = typec_port_register_altmode(port->port, &desc); - if (IS_ERR(amode)) - return PTR_ERR(amode); - port->port_altmode[CROS_EC_ALTMODE_TBT] = amode; - typec_altmode_set_drvdata(amode, port); - amode->ops = &port_amode_ops; + if (typec->ap_driven_altmode) { + memset(&desc, 0, sizeof(desc)); + desc.svid = USB_TYPEC_TBT_SID; + desc.mode = TBT_MODE; + desc.inactive = true; + amode = cros_typec_register_thunderbolt(port, &desc); + if (IS_ERR(amode)) + return PTR_ERR(amode); + port->port_altmode[CROS_EC_ALTMODE_TBT] = amode; + } port->state.alt = NULL; port->state.mode = TYPEC_STATE_USB; @@ -576,6 +579,10 @@ static int cros_typec_enable_dp(struct cros_typec_data *typec, if (!ret) ret = typec_mux_set(port->mux, &port->state); + if (!ret) + ret = cros_typec_displayport_status_update(port->state.alt, + port->state.data); + return ret; } @@ -619,6 +626,7 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num, }; struct ec_params_usb_pd_mux_ack mux_ack; enum typec_orientation orientation; + struct cros_typec_altmode_node *node; int ret; ret = cros_ec_cmd(typec->ec, 0, EC_CMD_USB_PD_MUX_INFO, @@ -677,6 +685,14 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num, port->mux_flags); } + /* Iterate all partner alt-modes and set the active alternate mode. */ + list_for_each_entry(node, &port->partner_mode_list, list) { + typec_altmode_update_active( + node->amode, + port->state.alt && + node->amode->svid == port->state.alt->svid); + } + mux_ack: if (!typec->needs_mux_ack) return ret; @@ -1244,6 +1260,8 @@ static int cros_typec_probe(struct platform_device *pdev) typec->typec_cmd_supported = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_CMD); typec->needs_mux_ack = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK); + typec->ap_driven_altmode = cros_ec_check_features( + ec_dev, EC_FEATURE_TYPEC_REQUIRE_AP_MODE_ENTRY); ret = cros_ec_cmd(typec->ec, 0, EC_CMD_USB_PD_PORTS, NULL, 0, &resp, sizeof(resp)); diff --git a/drivers/platform/chrome/cros_ec_typec.h b/drivers/platform/chrome/cros_ec_typec.h index deda180a646f..9fd5342bb0ad 100644 --- a/drivers/platform/chrome/cros_ec_typec.h +++ b/drivers/platform/chrome/cros_ec_typec.h @@ -39,6 +39,7 @@ struct cros_typec_data { struct work_struct port_work; bool typec_cmd_supported; bool needs_mux_ack; + bool ap_driven_altmode; }; /* Per port data. */ diff --git a/drivers/platform/chrome/cros_ec_uart.c b/drivers/platform/chrome/cros_ec_uart.c index 62bc24f6dcc7..19c179d49c90 100644 --- a/drivers/platform/chrome/cros_ec_uart.c +++ b/drivers/platform/chrome/cros_ec_uart.c @@ -283,7 +283,7 @@ static int cros_ec_uart_probe(struct serdev_device *serdev) ec_dev->pkt_xfer = cros_ec_uart_pkt_xfer; ec_dev->din_size = sizeof(struct ec_host_response) + sizeof(struct ec_response_get_protocol_info); - ec_dev->dout_size = sizeof(struct ec_host_request); + ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action); serdev_device_set_client_ops(serdev, &cros_ec_uart_client_ops); diff --git a/drivers/platform/chrome/cros_ec_vbc.c b/drivers/platform/chrome/cros_ec_vbc.c index 7bdb489354c5..963c4db23055 100644 --- a/drivers/platform/chrome/cros_ec_vbc.c +++ b/drivers/platform/chrome/cros_ec_vbc.c @@ -15,7 +15,7 @@ #define DRV_NAME "cros-ec-vbc" static ssize_t vboot_context_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *att, char *buf, + const struct bin_attribute *att, char *buf, loff_t pos, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -59,7 +59,7 @@ static ssize_t vboot_context_read(struct file *filp, struct kobject *kobj, } static ssize_t vboot_context_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, + const struct bin_attribute *attr, char *buf, loff_t pos, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -99,16 +99,16 @@ static ssize_t vboot_context_write(struct file *filp, struct kobject *kobj, return data_sz; } -static BIN_ATTR_RW(vboot_context, 16); +static const BIN_ATTR_RW(vboot_context, 16); -static struct bin_attribute *cros_ec_vbc_bin_attrs[] = { +static const struct bin_attribute *const cros_ec_vbc_bin_attrs[] = { &bin_attr_vboot_context, NULL }; static const struct attribute_group cros_ec_vbc_attr_group = { .name = "vbc", - .bin_attrs = cros_ec_vbc_bin_attrs, + .bin_attrs_new = cros_ec_vbc_bin_attrs, }; static int cros_ec_vbc_probe(struct platform_device *pd) diff --git a/drivers/platform/chrome/cros_kbd_led_backlight.c b/drivers/platform/chrome/cros_kbd_led_backlight.c index 78097c8a4966..fc27bd7fc4b9 100644 --- a/drivers/platform/chrome/cros_kbd_led_backlight.c +++ b/drivers/platform/chrome/cros_kbd_led_backlight.c @@ -121,7 +121,17 @@ static const struct keyboard_led_drvdata keyboard_led_drvdata_acpi = { #endif /* CONFIG_ACPI */ -#if IS_ENABLED(CONFIG_CROS_EC) +#if IS_ENABLED(CONFIG_MFD_CROS_EC_DEV) +static int keyboard_led_init_ec_pwm_mfd(struct platform_device *pdev) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_device *cros_ec = ec_dev->ec_dev; + struct keyboard_led *keyboard_led = platform_get_drvdata(pdev); + + keyboard_led->ec = cros_ec; + + return 0; +} static int keyboard_led_set_brightness_ec_pwm(struct led_classdev *cdev, @@ -169,44 +179,6 @@ keyboard_led_get_brightness_ec_pwm(struct led_classdev *cdev) return resp->percent; } -static int keyboard_led_init_ec_pwm(struct platform_device *pdev) -{ - struct keyboard_led *keyboard_led = platform_get_drvdata(pdev); - - keyboard_led->ec = dev_get_drvdata(pdev->dev.parent); - if (!keyboard_led->ec) { - dev_err(&pdev->dev, "no parent EC device\n"); - return -EINVAL; - } - - return 0; -} - -static const __maybe_unused struct keyboard_led_drvdata keyboard_led_drvdata_ec_pwm = { - .init = keyboard_led_init_ec_pwm, - .brightness_set_blocking = keyboard_led_set_brightness_ec_pwm, - .brightness_get = keyboard_led_get_brightness_ec_pwm, - .max_brightness = KEYBOARD_BACKLIGHT_MAX, -}; - -#else /* IS_ENABLED(CONFIG_CROS_EC) */ - -static const __maybe_unused struct keyboard_led_drvdata keyboard_led_drvdata_ec_pwm = {}; - -#endif /* IS_ENABLED(CONFIG_CROS_EC) */ - -#if IS_ENABLED(CONFIG_MFD_CROS_EC_DEV) -static int keyboard_led_init_ec_pwm_mfd(struct platform_device *pdev) -{ - struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent); - struct cros_ec_device *cros_ec = ec_dev->ec_dev; - struct keyboard_led *keyboard_led = platform_get_drvdata(pdev); - - keyboard_led->ec = cros_ec; - - return 0; -} - static const struct keyboard_led_drvdata keyboard_led_drvdata_ec_pwm_mfd = { .init = keyboard_led_init_ec_pwm_mfd, .brightness_set_blocking = keyboard_led_set_brightness_ec_pwm, @@ -229,7 +201,7 @@ static int keyboard_led_probe(struct platform_device *pdev) { const struct keyboard_led_drvdata *drvdata; struct keyboard_led *keyboard_led; - int error; + int err; if (keyboard_led_is_mfd_device(pdev)) drvdata = &keyboard_led_drvdata_ec_pwm_mfd; @@ -244,9 +216,9 @@ static int keyboard_led_probe(struct platform_device *pdev) platform_set_drvdata(pdev, keyboard_led); if (drvdata->init) { - error = drvdata->init(pdev); - if (error) - return error; + err = drvdata->init(pdev); + if (err) + return err; } keyboard_led->cdev.name = "chromeos::kbd_backlight"; @@ -256,13 +228,10 @@ static int keyboard_led_probe(struct platform_device *pdev) keyboard_led->cdev.brightness_set_blocking = drvdata->brightness_set_blocking; keyboard_led->cdev.brightness_get = drvdata->brightness_get; - error = devm_led_classdev_register(&pdev->dev, &keyboard_led->cdev); - if (error == -EEXIST) /* Already bound via other mechanism */ + err = devm_led_classdev_register(&pdev->dev, &keyboard_led->cdev); + if (err == -EEXIST) /* Already bound via other mechanism */ return -ENODEV; - if (error) - return error; - - return 0; + return err; } #ifdef CONFIG_ACPI @@ -273,17 +242,6 @@ static const struct acpi_device_id keyboard_led_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, keyboard_led_acpi_match); #endif -#ifdef CONFIG_OF -static const struct of_device_id keyboard_led_of_match[] = { - { - .compatible = "google,cros-kbd-led-backlight", - .data = &keyboard_led_drvdata_ec_pwm, - }, - {} -}; -MODULE_DEVICE_TABLE(of, keyboard_led_of_match); -#endif - static const struct platform_device_id keyboard_led_id[] = { { "cros-keyboard-leds", 0 }, {} @@ -294,7 +252,6 @@ static struct platform_driver keyboard_led_driver = { .driver = { .name = "cros-keyboard-leds", .acpi_match_table = ACPI_PTR(keyboard_led_acpi_match), - .of_match_table = of_match_ptr(keyboard_led_of_match), }, .probe = keyboard_led_probe, .id_table = keyboard_led_id, diff --git a/drivers/platform/chrome/cros_typec_altmode.c b/drivers/platform/chrome/cros_typec_altmode.c new file mode 100644 index 000000000000..557340b53af0 --- /dev/null +++ b/drivers/platform/chrome/cros_typec_altmode.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Alt-mode implementation on ChromeOS EC. + * + * Copyright 2024 Google LLC + * Author: Abhishek Pandit-Subedi <abhishekpandit@chromium.org> + */ +#include "cros_ec_typec.h" + +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/usb/typec_dp.h> +#include <linux/usb/typec_tbt.h> +#include <linux/usb/pd_vdo.h> + +#include "cros_typec_altmode.h" + +struct cros_typec_altmode_data { + struct work_struct work; + struct cros_typec_port *port; + struct typec_altmode *alt; + bool ap_mode_entry; + + struct mutex lock; + u32 header; + u32 *vdo_data; + u8 vdo_size; + + u16 sid; + u8 mode; +}; + +struct cros_typec_dp_data { + struct cros_typec_altmode_data adata; + struct typec_displayport_data data; + bool configured; + bool pending_status_update; +}; + +static void cros_typec_altmode_work(struct work_struct *work) +{ + struct cros_typec_altmode_data *data = + container_of(work, struct cros_typec_altmode_data, work); + + mutex_lock(&data->lock); + + if (typec_altmode_vdm(data->alt, data->header, data->vdo_data, + data->vdo_size)) + dev_err(&data->alt->dev, "VDM 0x%x failed\n", data->header); + + data->header = 0; + data->vdo_data = NULL; + data->vdo_size = 0; + + mutex_unlock(&data->lock); +} + +static int cros_typec_altmode_enter(struct typec_altmode *alt, u32 *vdo) +{ + struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt); + struct ec_params_typec_control req = { + .port = adata->port->port_num, + .command = TYPEC_CONTROL_COMMAND_ENTER_MODE, + }; + int svdm_version; + int ret; + + if (!adata->ap_mode_entry) { + dev_warn(&alt->dev, + "EC does not support AP driven mode entry\n"); + return -EOPNOTSUPP; + } + + if (adata->sid == USB_TYPEC_DP_SID) + req.mode_to_enter = CROS_EC_ALTMODE_DP; + else if (adata->sid == USB_TYPEC_TBT_SID) + req.mode_to_enter = CROS_EC_ALTMODE_TBT; + else + return -EOPNOTSUPP; + + ret = cros_ec_cmd(adata->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL, + &req, sizeof(req), NULL, 0); + if (ret < 0) + return ret; + + svdm_version = typec_altmode_get_svdm_version(alt); + if (svdm_version < 0) + return svdm_version; + + mutex_lock(&adata->lock); + + adata->header = VDO(adata->sid, 1, svdm_version, CMD_ENTER_MODE); + adata->header |= VDO_OPOS(adata->mode); + adata->header |= VDO_CMDT(CMDT_RSP_ACK); + adata->vdo_data = NULL; + adata->vdo_size = 1; + schedule_work(&adata->work); + + mutex_unlock(&adata->lock); + return ret; +} + +static int cros_typec_altmode_exit(struct typec_altmode *alt) +{ + struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt); + struct ec_params_typec_control req = { + .port = adata->port->port_num, + .command = TYPEC_CONTROL_COMMAND_EXIT_MODES, + }; + int svdm_version; + int ret; + + if (!adata->ap_mode_entry) { + dev_warn(&alt->dev, + "EC does not support AP driven mode exit\n"); + return -EOPNOTSUPP; + } + + ret = cros_ec_cmd(adata->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL, + &req, sizeof(req), NULL, 0); + + if (ret < 0) + return ret; + + svdm_version = typec_altmode_get_svdm_version(alt); + if (svdm_version < 0) + return svdm_version; + + mutex_lock(&adata->lock); + + adata->header = VDO(adata->sid, 1, svdm_version, CMD_EXIT_MODE); + adata->header |= VDO_OPOS(adata->mode); + adata->header |= VDO_CMDT(CMDT_RSP_ACK); + adata->vdo_data = NULL; + adata->vdo_size = 1; + schedule_work(&adata->work); + + mutex_unlock(&adata->lock); + return ret; +} + +static int cros_typec_displayport_vdm(struct typec_altmode *alt, u32 header, + const u32 *data, int count) +{ + struct cros_typec_dp_data *dp_data = typec_altmode_get_drvdata(alt); + struct cros_typec_altmode_data *adata = &dp_data->adata; + + + int cmd_type = PD_VDO_CMDT(header); + int cmd = PD_VDO_CMD(header); + int svdm_version; + + svdm_version = typec_altmode_get_svdm_version(alt); + if (svdm_version < 0) + return svdm_version; + + mutex_lock(&adata->lock); + + switch (cmd_type) { + case CMDT_INIT: + if (PD_VDO_SVDM_VER(header) < svdm_version) { + typec_partner_set_svdm_version(adata->port->partner, + PD_VDO_SVDM_VER(header)); + svdm_version = PD_VDO_SVDM_VER(header); + } + + adata->header = VDO(adata->sid, 1, svdm_version, cmd); + adata->header |= VDO_OPOS(adata->mode); + + /* + * DP_CMD_CONFIGURE: We can't actually do anything with the + * provided VDO yet so just send back an ACK. + * + * DP_CMD_STATUS_UPDATE: We wait for Mux changes to send + * DPStatus Acks. + */ + switch (cmd) { + case DP_CMD_CONFIGURE: + dp_data->data.conf = *data; + adata->header |= VDO_CMDT(CMDT_RSP_ACK); + dp_data->configured = true; + schedule_work(&adata->work); + break; + case DP_CMD_STATUS_UPDATE: + dp_data->pending_status_update = true; + break; + default: + adata->header |= VDO_CMDT(CMDT_RSP_ACK); + schedule_work(&adata->work); + break; + } + + break; + default: + break; + } + + mutex_unlock(&adata->lock); + return 0; +} + +static int cros_typec_thunderbolt_vdm(struct typec_altmode *alt, u32 header, + const u32 *data, int count) +{ + struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt); + + int cmd_type = PD_VDO_CMDT(header); + int cmd = PD_VDO_CMD(header); + int svdm_version; + + svdm_version = typec_altmode_get_svdm_version(alt); + if (svdm_version < 0) + return svdm_version; + + mutex_lock(&adata->lock); + + switch (cmd_type) { + case CMDT_INIT: + if (PD_VDO_SVDM_VER(header) < svdm_version) { + typec_partner_set_svdm_version(adata->port->partner, + PD_VDO_SVDM_VER(header)); + svdm_version = PD_VDO_SVDM_VER(header); + } + + adata->header = VDO(adata->sid, 1, svdm_version, cmd); + adata->header |= VDO_OPOS(adata->mode); + + switch (cmd) { + case CMD_ENTER_MODE: + /* Don't respond to the enter mode vdm because it + * triggers mux configuration. This is handled directly + * by the cros_ec_typec driver so the Thunderbolt driver + * doesn't need to be involved. + */ + break; + default: + adata->header |= VDO_CMDT(CMDT_RSP_ACK); + schedule_work(&adata->work); + break; + } + + break; + default: + break; + } + + mutex_unlock(&adata->lock); + return 0; +} + + +static int cros_typec_altmode_vdm(struct typec_altmode *alt, u32 header, + const u32 *data, int count) +{ + struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt); + + if (!adata->ap_mode_entry) + return -EOPNOTSUPP; + + if (adata->sid == USB_TYPEC_DP_SID) + return cros_typec_displayport_vdm(alt, header, data, count); + + if (adata->sid == USB_TYPEC_TBT_SID) + return cros_typec_thunderbolt_vdm(alt, header, data, count); + + return -EINVAL; +} + +static const struct typec_altmode_ops cros_typec_altmode_ops = { + .enter = cros_typec_altmode_enter, + .exit = cros_typec_altmode_exit, + .vdm = cros_typec_altmode_vdm, +}; + +#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE) +int cros_typec_displayport_status_update(struct typec_altmode *altmode, + struct typec_displayport_data *data) +{ + struct cros_typec_dp_data *dp_data = + typec_altmode_get_drvdata(altmode); + struct cros_typec_altmode_data *adata = &dp_data->adata; + + if (!dp_data->pending_status_update) { + dev_dbg(&altmode->dev, + "Got DPStatus without a pending request\n"); + return 0; + } + + if (dp_data->configured && dp_data->data.conf != data->conf) + dev_dbg(&altmode->dev, + "DP Conf doesn't match. Requested 0x%04x, Actual 0x%04x\n", + dp_data->data.conf, data->conf); + + mutex_lock(&adata->lock); + + dp_data->data = *data; + dp_data->pending_status_update = false; + adata->header |= VDO_CMDT(CMDT_RSP_ACK); + adata->vdo_data = &dp_data->data.status; + adata->vdo_size = 2; + schedule_work(&adata->work); + + mutex_unlock(&adata->lock); + + return 0; +} + +struct typec_altmode * +cros_typec_register_displayport(struct cros_typec_port *port, + struct typec_altmode_desc *desc, + bool ap_mode_entry) +{ + struct typec_altmode *alt; + struct cros_typec_dp_data *dp_data; + struct cros_typec_altmode_data *adata; + + alt = typec_port_register_altmode(port->port, desc); + if (IS_ERR(alt)) + return alt; + + dp_data = devm_kzalloc(&alt->dev, sizeof(*dp_data), GFP_KERNEL); + if (!dp_data) { + typec_unregister_altmode(alt); + return ERR_PTR(-ENOMEM); + } + + adata = &dp_data->adata; + INIT_WORK(&adata->work, cros_typec_altmode_work); + mutex_init(&adata->lock); + adata->alt = alt; + adata->port = port; + adata->ap_mode_entry = ap_mode_entry; + adata->sid = desc->svid; + adata->mode = desc->mode; + + typec_altmode_set_ops(alt, &cros_typec_altmode_ops); + typec_altmode_set_drvdata(alt, adata); + + return alt; +} +#endif + +#if IS_ENABLED(CONFIG_TYPEC_TBT_ALTMODE) +struct typec_altmode * +cros_typec_register_thunderbolt(struct cros_typec_port *port, + struct typec_altmode_desc *desc) +{ + struct typec_altmode *alt; + struct cros_typec_altmode_data *adata; + + alt = typec_port_register_altmode(port->port, desc); + if (IS_ERR(alt)) + return alt; + + adata = devm_kzalloc(&alt->dev, sizeof(*adata), GFP_KERNEL); + if (!adata) { + typec_unregister_altmode(alt); + return ERR_PTR(-ENOMEM); + } + + INIT_WORK(&adata->work, cros_typec_altmode_work); + adata->alt = alt; + adata->port = port; + adata->ap_mode_entry = true; + adata->sid = desc->svid; + adata->mode = desc->mode; + + typec_altmode_set_ops(alt, &cros_typec_altmode_ops); + typec_altmode_set_drvdata(alt, adata); + + return alt; +} +#endif diff --git a/drivers/platform/chrome/cros_typec_altmode.h b/drivers/platform/chrome/cros_typec_altmode.h new file mode 100644 index 000000000000..3f2aa95d065a --- /dev/null +++ b/drivers/platform/chrome/cros_typec_altmode.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __CROS_TYPEC_ALTMODE_H__ +#define __CROS_TYPEC_ALTMODE_H__ + +#include <linux/kconfig.h> +#include <linux/usb/typec.h> + +struct cros_typec_port; +struct typec_altmode; +struct typec_altmode_desc; +struct typec_displayport_data; + +#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE) +struct typec_altmode * +cros_typec_register_displayport(struct cros_typec_port *port, + struct typec_altmode_desc *desc, + bool ap_mode_entry); + +int cros_typec_displayport_status_update(struct typec_altmode *altmode, + struct typec_displayport_data *data); +#else +static inline struct typec_altmode * +cros_typec_register_displayport(struct cros_typec_port *port, + struct typec_altmode_desc *desc, + bool ap_mode_entry) +{ + return typec_port_register_altmode(port->port, desc); +} + +static inline int cros_typec_displayport_status_update(struct typec_altmode *altmode, + struct typec_displayport_data *data) +{ + return 0; +} +#endif + +#if IS_ENABLED(CONFIG_TYPEC_TBT_ALTMODE) +struct typec_altmode * +cros_typec_register_thunderbolt(struct cros_typec_port *port, + struct typec_altmode_desc *desc); +#else +static inline struct typec_altmode * +cros_typec_register_thunderbolt(struct cros_typec_port *port, + struct typec_altmode_desc *desc) +{ + return typec_port_register_altmode(port->port, desc); +} +#endif + +#endif /* __CROS_TYPEC_ALTMODE_H__ */ diff --git a/drivers/platform/chrome/cros_usbpd_logger.c b/drivers/platform/chrome/cros_usbpd_logger.c index cd71f1caea81..7ce75e2e039e 100644 --- a/drivers/platform/chrome/cros_usbpd_logger.c +++ b/drivers/platform/chrome/cros_usbpd_logger.c @@ -13,6 +13,7 @@ #include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_device.h> #include <linux/rtc.h> +#include <linux/string_choices.h> #define DRV_NAME "cros-usbpd-logger" @@ -135,8 +136,8 @@ static void cros_usbpd_print_log_entry(struct ec_response_pd_log *r, len += append_str(buf, len, "Power supply fault: %s", fault); break; case PD_EVENT_VIDEO_DP_MODE: - len += append_str(buf, len, "DP mode %sabled", r->data == 1 ? - "en" : "dis"); + len += append_str(buf, len, "DP mode %s", + str_enabled_disabled(r->data == 1)); break; case PD_EVENT_VIDEO_CODEC: minfo = (struct mcdp_info *)r->payload; diff --git a/drivers/platform/cznic/turris-omnia-mcu-base.c b/drivers/platform/cznic/turris-omnia-mcu-base.c index 58f9afae2867..770e680b96f9 100644 --- a/drivers/platform/cznic/turris-omnia-mcu-base.c +++ b/drivers/platform/cznic/turris-omnia-mcu-base.c @@ -52,6 +52,7 @@ int omnia_cmd_write_read(const struct i2c_client *client, return 0; } +EXPORT_SYMBOL_GPL(omnia_cmd_write_read); static int omnia_get_version_hash(struct omnia_mcu *mcu, bool bootloader, char version[static OMNIA_FW_VERSION_HEX_LEN]) @@ -257,6 +258,8 @@ static int omnia_mcu_read_features(struct omnia_mcu *mcu) _DEF_FEAT(NEW_INT_API, "new interrupt API"), _DEF_FEAT(POWEROFF_WAKEUP, "poweroff and wakeup"), _DEF_FEAT(TRNG, "true random number generator"), + _DEF_FEAT(BRIGHTNESS_INT, "LED panel brightness change interrupt"), + _DEF_FEAT(LED_GAMMA_CORRECTION, "LED gamma correction"), #undef _DEF_FEAT }; struct i2c_client *client = mcu->client; diff --git a/drivers/platform/cznic/turris-omnia-mcu.h b/drivers/platform/cznic/turris-omnia-mcu.h index 2b13e28ee323..088541be3f4c 100644 --- a/drivers/platform/cznic/turris-omnia-mcu.h +++ b/drivers/platform/cznic/turris-omnia-mcu.h @@ -8,7 +8,6 @@ #ifndef __TURRIS_OMNIA_MCU_H #define __TURRIS_OMNIA_MCU_H -#include <linux/bitops.h> #include <linux/completion.h> #include <linux/gpio/driver.h> #include <linux/hw_random.h> @@ -17,8 +16,6 @@ #include <linux/types.h> #include <linux/watchdog.h> #include <linux/workqueue.h> -#include <asm/byteorder.h> -#include <linux/unaligned.h> struct i2c_client; struct rtc_device; @@ -93,133 +90,6 @@ struct omnia_mcu { #endif }; -int omnia_cmd_write_read(const struct i2c_client *client, - void *cmd, unsigned int cmd_len, - void *reply, unsigned int reply_len); - -static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd, - unsigned int len) -{ - return omnia_cmd_write_read(client, cmd, len, NULL, 0); -} - -static inline int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd, - u8 val) -{ - u8 buf[2] = { cmd, val }; - - return omnia_cmd_write(client, buf, sizeof(buf)); -} - -static inline int omnia_cmd_write_u16(const struct i2c_client *client, u8 cmd, - u16 val) -{ - u8 buf[3]; - - buf[0] = cmd; - put_unaligned_le16(val, &buf[1]); - - return omnia_cmd_write(client, buf, sizeof(buf)); -} - -static inline int omnia_cmd_write_u32(const struct i2c_client *client, u8 cmd, - u32 val) -{ - u8 buf[5]; - - buf[0] = cmd; - put_unaligned_le32(val, &buf[1]); - - return omnia_cmd_write(client, buf, sizeof(buf)); -} - -static inline int omnia_cmd_read(const struct i2c_client *client, u8 cmd, - void *reply, unsigned int len) -{ - return omnia_cmd_write_read(client, &cmd, 1, reply, len); -} - -static inline unsigned int -omnia_compute_reply_length(unsigned long mask, bool interleaved, - unsigned int offset) -{ - if (!mask) - return 0; - - return ((__fls(mask) >> 3) << interleaved) + 1 + offset; -} - -/* Returns 0 on success */ -static inline int omnia_cmd_read_bits(const struct i2c_client *client, u8 cmd, - unsigned long bits, unsigned long *dst) -{ - __le32 reply; - int err; - - if (!bits) { - *dst = 0; - return 0; - } - - err = omnia_cmd_read(client, cmd, &reply, - omnia_compute_reply_length(bits, false, 0)); - if (err) - return err; - - *dst = le32_to_cpu(reply) & bits; - - return 0; -} - -static inline int omnia_cmd_read_bit(const struct i2c_client *client, u8 cmd, - unsigned long bit) -{ - unsigned long reply; - int err; - - err = omnia_cmd_read_bits(client, cmd, bit, &reply); - if (err) - return err; - - return !!reply; -} - -static inline int omnia_cmd_read_u32(const struct i2c_client *client, u8 cmd, - u32 *dst) -{ - __le32 reply; - int err; - - err = omnia_cmd_read(client, cmd, &reply, sizeof(reply)); - if (err) - return err; - - *dst = le32_to_cpu(reply); - - return 0; -} - -static inline int omnia_cmd_read_u16(const struct i2c_client *client, u8 cmd, - u16 *dst) -{ - __le16 reply; - int err; - - err = omnia_cmd_read(client, cmd, &reply, sizeof(reply)); - if (err) - return err; - - *dst = le16_to_cpu(reply); - - return 0; -} - -static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd, - u8 *reply) -{ - return omnia_cmd_read(client, cmd, reply, sizeof(*reply)); -} - #ifdef CONFIG_TURRIS_OMNIA_MCU_GPIO extern const u8 omnia_int_to_gpio_idx[32]; extern const struct attribute_group omnia_mcu_gpio_group; diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c index c5b36837e694..49d4394e9f8a 100644 --- a/drivers/platform/mellanox/mlxbf-bootctl.c +++ b/drivers/platform/mellanox/mlxbf-bootctl.c @@ -177,7 +177,7 @@ static ssize_t post_reset_wdog_show(struct device *dev, if (ret < 0) return ret; - return sprintf(buf, "%d\n", ret); + return sysfs_emit(buf, "%d\n", ret); } static ssize_t post_reset_wdog_store(struct device *dev, @@ -206,7 +206,7 @@ static ssize_t mlxbf_bootctl_show(int smc_op, char *buf) if (action < 0) return action; - return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action)); + return sysfs_emit(buf, "%s\n", mlxbf_bootctl_action_to_string(action)); } static int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count) @@ -274,14 +274,14 @@ static ssize_t lifecycle_state_show(struct device *dev, * due to using the test bits. */ if (test_state) { - return sprintf(buf, "%s(test)\n", + return sysfs_emit(buf, "%s(test)\n", mlxbf_bootctl_lifecycle_states[lc_state]); } else if (use_dev_key && (lc_state == MLXBF_BOOTCTL_SB_LIFECYCLE_GA_SECURE)) { - return sprintf(buf, "Secured (development)\n"); + return sysfs_emit(buf, "Secured (development)\n"); } - return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]); + return sysfs_emit(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]); } static ssize_t secure_boot_fuse_state_show(struct device *dev, @@ -332,9 +332,9 @@ static ssize_t secure_boot_fuse_state_show(struct device *dev, else status = valid ? "Invalid" : "Free"; } - buf_len += sprintf(buf + buf_len, "%d:%s ", key, status); + buf_len += sysfs_emit_at(buf, buf_len, "%d:%s ", key, status); } - buf_len += sprintf(buf + buf_len, "\n"); + buf_len += sysfs_emit_at(buf, buf_len, "\n"); return buf_len; } @@ -939,7 +939,7 @@ MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids); static ssize_t mlxbf_bootctl_bootfifo_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { @@ -971,9 +971,9 @@ static ssize_t mlxbf_bootctl_bootfifo_read(struct file *filp, return p - buf; } -static struct bin_attribute mlxbf_bootctl_bootfifo_sysfs_attr = { +static const struct bin_attribute mlxbf_bootctl_bootfifo_sysfs_attr = { .attr = { .name = "bootfifo", .mode = 0400 }, - .read = mlxbf_bootctl_bootfifo_read, + .read_new = mlxbf_bootctl_bootfifo_read, }; static bool mlxbf_bootctl_guid_match(const guid_t *guid, diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c index 9ff7b487dc48..36a00692347d 100644 --- a/drivers/platform/mellanox/mlxbf-pmc.c +++ b/drivers/platform/mellanox/mlxbf-pmc.c @@ -88,6 +88,7 @@ #define MLXBF_PMC_CRSPACE_PERFMON_CTL(n) (n * MLXBF_PMC_CRSPACE_PERFMON_REG0_SZ) #define MLXBF_PMC_CRSPACE_PERFMON_EN BIT(30) #define MLXBF_PMC_CRSPACE_PERFMON_CLR BIT(28) +#define MLXBF_PMC_CRSPACE_PERFMON_COUNT_CLOCK(n) (MLXBF_PMC_CRSPACE_PERFMON_CTL(n) + 0x4) #define MLXBF_PMC_CRSPACE_PERFMON_VAL0(n) (MLXBF_PMC_CRSPACE_PERFMON_CTL(n) + 0xc) /** @@ -114,6 +115,7 @@ struct mlxbf_pmc_attribute { * @attr_event: Attributes for "event" sysfs files * @attr_event_list: Attributes for "event_list" sysfs files * @attr_enable: Attributes for "enable" sysfs files + * @attr_count_clock: Attributes for "count_clock" sysfs files * @block_attr: All attributes needed for the block * @block_attr_grp: Attribute group for the block */ @@ -126,6 +128,7 @@ struct mlxbf_pmc_block_info { struct mlxbf_pmc_attribute *attr_event; struct mlxbf_pmc_attribute attr_event_list; struct mlxbf_pmc_attribute attr_enable; + struct mlxbf_pmc_attribute attr_count_clock; struct attribute *block_attr[MLXBF_PMC_MAX_ATTRS]; struct attribute_group block_attr_grp; }; @@ -859,6 +862,37 @@ static const struct mlxbf_pmc_events mlxbf_pmc_llt_miss_events[] = { {75, "HISTOGRAM_HISTOGRAM_BIN9"}, }; +static const struct mlxbf_pmc_events mlxbf_pmc_clock_events[] = { + { 0x0, "FMON_CLK_LAST_COUNT_PLL_D1_INST0" }, + { 0x4, "REFERENCE_WINDOW_WIDTH_PLL_D1_INST0" }, + { 0x8, "FMON_CLK_LAST_COUNT_PLL_D1_INST1" }, + { 0xc, "REFERENCE_WINDOW_WIDTH_PLL_D1_INST1" }, + { 0x10, "FMON_CLK_LAST_COUNT_PLL_G1" }, + { 0x14, "REFERENCE_WINDOW_WIDTH_PLL_G1" }, + { 0x18, "FMON_CLK_LAST_COUNT_PLL_W1" }, + { 0x1c, "REFERENCE_WINDOW_WIDTH_PLL_W1" }, + { 0x20, "FMON_CLK_LAST_COUNT_PLL_T1" }, + { 0x24, "REFERENCE_WINDOW_WIDTH_PLL_T1" }, + { 0x28, "FMON_CLK_LAST_COUNT_PLL_A0" }, + { 0x2c, "REFERENCE_WINDOW_WIDTH_PLL_A0" }, + { 0x30, "FMON_CLK_LAST_COUNT_PLL_C0" }, + { 0x34, "REFERENCE_WINDOW_WIDTH_PLL_C0" }, + { 0x38, "FMON_CLK_LAST_COUNT_PLL_N1" }, + { 0x3c, "REFERENCE_WINDOW_WIDTH_PLL_N1" }, + { 0x40, "FMON_CLK_LAST_COUNT_PLL_I1" }, + { 0x44, "REFERENCE_WINDOW_WIDTH_PLL_I1" }, + { 0x48, "FMON_CLK_LAST_COUNT_PLL_R1" }, + { 0x4c, "REFERENCE_WINDOW_WIDTH_PLL_R1" }, + { 0x50, "FMON_CLK_LAST_COUNT_PLL_P1" }, + { 0x54, "REFERENCE_WINDOW_WIDTH_PLL_P1" }, + { 0x58, "FMON_CLK_LAST_COUNT_REF_100_INST0" }, + { 0x5c, "REFERENCE_WINDOW_WIDTH_REF_100_INST0" }, + { 0x60, "FMON_CLK_LAST_COUNT_REF_100_INST1" }, + { 0x64, "REFERENCE_WINDOW_WIDTH_REF_100_INST1" }, + { 0x68, "FMON_CLK_LAST_COUNT_REF_156" }, + { 0x6c, "REFERENCE_WINDOW_WIDTH_REF_156" }, +}; + static struct mlxbf_pmc_context *pmc; /* UUID used to probe ATF service. */ @@ -1032,6 +1066,9 @@ static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, size } else if (strstr(blk, "llt")) { events = mlxbf_pmc_llt_events; size = ARRAY_SIZE(mlxbf_pmc_llt_events); + } else if (strstr(blk, "clock_measure")) { + events = mlxbf_pmc_clock_events; + size = ARRAY_SIZE(mlxbf_pmc_clock_events); } else { events = NULL; size = 0; @@ -1466,14 +1503,15 @@ static int mlxbf_pmc_read_event(unsigned int blk_num, u32 cnt_num, bool is_l3, u /* Method to read a register */ static int mlxbf_pmc_read_reg(unsigned int blk_num, u32 offset, u64 *result) { - u32 ecc_out; + u32 reg; - if (strstr(pmc->block_name[blk_num], "ecc")) { + if ((strstr(pmc->block_name[blk_num], "ecc")) || + (strstr(pmc->block_name[blk_num], "clock_measure"))) { if (mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + offset, - &ecc_out)) + ®)) return -EFAULT; - *result = ecc_out; + *result = reg; return 0; } @@ -1487,6 +1525,9 @@ static int mlxbf_pmc_read_reg(unsigned int blk_num, u32 offset, u64 *result) /* Method to write to a register */ static int mlxbf_pmc_write_reg(unsigned int blk_num, u32 offset, u64 data) { + if (strstr(pmc->block_name[blk_num], "clock_measure")) + return -EINVAL; + if (strstr(pmc->block_name[blk_num], "ecc")) { return mlxbf_pmc_write(pmc->block[blk_num].mmio_base + offset, MLXBF_PMC_WRITE_REG_32, data); @@ -1763,6 +1804,49 @@ static ssize_t mlxbf_pmc_enable_store(struct device *dev, return count; } +/* Show function for "count_clock" sysfs files - only for crspace */ +static ssize_t mlxbf_pmc_count_clock_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mlxbf_pmc_attribute *attr_count_clock = container_of( + attr, struct mlxbf_pmc_attribute, dev_attr); + unsigned int blk_num; + u32 reg; + + blk_num = attr_count_clock->nr; + + if (mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + + MLXBF_PMC_CRSPACE_PERFMON_COUNT_CLOCK(pmc->block[blk_num].counters), + ®)) + return -EINVAL; + + return sysfs_emit(buf, "%u\n", reg); +} + +/* Store function for "count_clock" sysfs files - only for crspace */ +static ssize_t mlxbf_pmc_count_clock_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mlxbf_pmc_attribute *attr_count_clock = container_of( + attr, struct mlxbf_pmc_attribute, dev_attr); + unsigned int blk_num; + u32 reg; + int err; + + blk_num = attr_count_clock->nr; + + err = kstrtouint(buf, 0, ®); + if (err < 0) + return err; + + mlxbf_pmc_write(pmc->block[blk_num].mmio_base + + MLXBF_PMC_CRSPACE_PERFMON_COUNT_CLOCK(pmc->block[blk_num].counters), + MLXBF_PMC_WRITE_REG_32, reg); + + return count; +} + /* Populate attributes for blocks with counters to monitor performance */ static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_num) { @@ -1801,6 +1885,21 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_ attr = NULL; } + if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE) { + /* Program crspace counters to count clock cycles using "count_clock" sysfs */ + attr = &pmc->block[blk_num].attr_count_clock; + attr->dev_attr.attr.mode = 0644; + attr->dev_attr.show = mlxbf_pmc_count_clock_show; + attr->dev_attr.store = mlxbf_pmc_count_clock_store; + attr->nr = blk_num; + attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, + "count_clock"); + if (!attr->dev_attr.attr.name) + return -ENOMEM; + pmc->block[blk_num].block_attr[++i] = &attr->dev_attr.attr; + attr = NULL; + } + pmc->block[blk_num].attr_counter = devm_kcalloc( dev, pmc->block[blk_num].counters, sizeof(struct mlxbf_pmc_attribute), GFP_KERNEL); diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c index 6aa2a4650367..b347000e4329 100644 --- a/drivers/platform/mellanox/mlxreg-hotplug.c +++ b/drivers/platform/mellanox/mlxreg-hotplug.c @@ -232,7 +232,7 @@ static ssize_t mlxreg_hotplug_attr_show(struct device *dev, regval = !!(regval & data->mask); } - return sprintf(buf, "%u\n", regval); + return sysfs_emit(buf, "%u\n", regval); } #define PRIV_ATTR(i) priv->mlxreg_hotplug_attr[i] diff --git a/drivers/platform/mellanox/mlxreg-io.c b/drivers/platform/mellanox/mlxreg-io.c index 595276206baf..97fefe6c38d1 100644 --- a/drivers/platform/mellanox/mlxreg-io.c +++ b/drivers/platform/mellanox/mlxreg-io.c @@ -126,7 +126,7 @@ mlxreg_io_attr_show(struct device *dev, struct device_attribute *attr, mutex_unlock(&priv->io_lock); - return sprintf(buf, "%u\n", regval); + return sysfs_emit(buf, "%u\n", regval); access_error: mutex_unlock(&priv->io_lock); diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index d4f32ad66530..a594d5fcfcfd 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -371,7 +371,7 @@ static const struct software_node *ssam_node_group_sp8[] = { NULL, }; -/* Devices for Surface Pro 9 (Intel/x86) and 10 */ +/* Devices for Surface Pro 9, 10 and 11 (Intel/x86) */ static const struct software_node *ssam_node_group_sp9[] = { &ssam_node_root, &ssam_node_hub_kip, @@ -430,6 +430,9 @@ static const struct acpi_device_id ssam_platform_hub_acpi_match[] = { /* Surface Pro 10 */ { "MSHW0510", (unsigned long)ssam_node_group_sp9 }, + /* Surface Pro 11 */ + { "MSHW0583", (unsigned long)ssam_node_group_sp9 }, + /* Surface Book 2 */ { "MSHW0107", (unsigned long)ssam_node_group_gen5 }, diff --git a/drivers/platform/surface/surface_platform_profile.c b/drivers/platform/surface/surface_platform_profile.c index 08db878f1d7d..0e479e35e66e 100644 --- a/drivers/platform/surface/surface_platform_profile.c +++ b/drivers/platform/surface/surface_platform_profile.c @@ -40,7 +40,7 @@ struct ssam_tmp_profile_info { struct ssam_platform_profile_device { struct ssam_device *sdev; - struct platform_profile_handler handler; + struct device *ppdev; bool has_fan; }; @@ -154,14 +154,14 @@ static int convert_profile_to_ssam_fan(struct ssam_device *sdev, enum platform_p } } -static int ssam_platform_profile_get(struct platform_profile_handler *pprof, +static int ssam_platform_profile_get(struct device *dev, enum platform_profile_option *profile) { struct ssam_platform_profile_device *tpd; enum ssam_tmp_profile tp; int status; - tpd = container_of(pprof, struct ssam_platform_profile_device, handler); + tpd = dev_get_drvdata(dev); status = ssam_tmp_profile_get(tpd->sdev, &tp); if (status) @@ -175,13 +175,13 @@ static int ssam_platform_profile_get(struct platform_profile_handler *pprof, return 0; } -static int ssam_platform_profile_set(struct platform_profile_handler *pprof, +static int ssam_platform_profile_set(struct device *dev, enum platform_profile_option profile) { struct ssam_platform_profile_device *tpd; int tp; - tpd = container_of(pprof, struct ssam_platform_profile_device, handler); + tpd = dev_get_drvdata(dev); tp = convert_profile_to_ssam_tmp(tpd->sdev, profile); if (tp < 0) @@ -201,6 +201,22 @@ static int ssam_platform_profile_set(struct platform_profile_handler *pprof, return tp; } +static int ssam_platform_profile_probe(void *drvdata, unsigned long *choices) +{ + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); + set_bit(PLATFORM_PROFILE_BALANCED, choices); + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + + return 0; +} + +static const struct platform_profile_ops ssam_platform_profile_ops = { + .probe = ssam_platform_profile_probe, + .profile_get = ssam_platform_profile_get, + .profile_set = ssam_platform_profile_set, +}; + static int surface_platform_profile_probe(struct ssam_device *sdev) { struct ssam_platform_profile_device *tpd; @@ -210,23 +226,14 @@ static int surface_platform_profile_probe(struct ssam_device *sdev) return -ENOMEM; tpd->sdev = sdev; - - tpd->handler.profile_get = ssam_platform_profile_get; - tpd->handler.profile_set = ssam_platform_profile_set; + ssam_device_set_drvdata(sdev, tpd); tpd->has_fan = device_property_read_bool(&sdev->dev, "has_fan"); - set_bit(PLATFORM_PROFILE_LOW_POWER, tpd->handler.choices); - set_bit(PLATFORM_PROFILE_BALANCED, tpd->handler.choices); - set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, tpd->handler.choices); - set_bit(PLATFORM_PROFILE_PERFORMANCE, tpd->handler.choices); + tpd->ppdev = devm_platform_profile_register(&sdev->dev, "Surface Platform Profile", + tpd, &ssam_platform_profile_ops); - return platform_profile_register(&tpd->handler); -} - -static void surface_platform_profile_remove(struct ssam_device *sdev) -{ - platform_profile_remove(); + return PTR_ERR_OR_ZERO(tpd->ppdev); } static const struct ssam_device_id ssam_platform_profile_match[] = { @@ -237,7 +244,6 @@ MODULE_DEVICE_TABLE(ssam, ssam_platform_profile_match); static struct ssam_device_driver surface_platform_profile = { .probe = surface_platform_profile_probe, - .remove = surface_platform_profile_remove, .match_table = ssam_platform_profile_match, .driver = { .name = "surface_platform_profile", diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index ac4f8ab45bdc..69336bd778ee 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -30,7 +30,10 @@ #include <linux/input/sparse-keymap.h> #include <acpi/video.h> #include <linux/hwmon.h> +#include <linux/units.h> +#include <linux/unaligned.h> #include <linux/bitfield.h> +#include <linux/bitmap.h> MODULE_AUTHOR("Carlos Corbacho"); MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver"); @@ -67,10 +70,16 @@ MODULE_LICENSE("GPL"); #define ACER_WMID_GET_GAMING_SYS_INFO_METHODID 5 #define ACER_WMID_SET_GAMING_FAN_BEHAVIOR 14 #define ACER_WMID_SET_GAMING_MISC_SETTING_METHODID 22 +#define ACER_WMID_GET_GAMING_MISC_SETTING_METHODID 23 -#define ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET 0x54 +#define ACER_GAMING_MISC_SETTING_STATUS_MASK GENMASK_ULL(7, 0) +#define ACER_GAMING_MISC_SETTING_INDEX_MASK GENMASK_ULL(7, 0) +#define ACER_GAMING_MISC_SETTING_VALUE_MASK GENMASK_ULL(15, 8) -#define ACER_PREDATOR_V4_FAN_SPEED_READ_BIT_MASK GENMASK(20, 8) +#define ACER_PREDATOR_V4_RETURN_STATUS_BIT_MASK GENMASK_ULL(7, 0) +#define ACER_PREDATOR_V4_SENSOR_INDEX_BIT_MASK GENMASK_ULL(15, 8) +#define ACER_PREDATOR_V4_SENSOR_READING_BIT_MASK GENMASK_ULL(23, 8) +#define ACER_PREDATOR_V4_SUPPORTED_SENSORS_BIT_MASK GENMASK_ULL(39, 24) /* * Acer ACPI method GUIDs @@ -99,9 +108,29 @@ enum acer_wmi_event_ids { }; enum acer_wmi_predator_v4_sys_info_command { - ACER_WMID_CMD_GET_PREDATOR_V4_BAT_STATUS = 0x02, - ACER_WMID_CMD_GET_PREDATOR_V4_CPU_FAN_SPEED = 0x0201, - ACER_WMID_CMD_GET_PREDATOR_V4_GPU_FAN_SPEED = 0x0601, + ACER_WMID_CMD_GET_PREDATOR_V4_SUPPORTED_SENSORS = 0x0000, + ACER_WMID_CMD_GET_PREDATOR_V4_SENSOR_READING = 0x0001, + ACER_WMID_CMD_GET_PREDATOR_V4_BAT_STATUS = 0x0002, +}; + +enum acer_wmi_predator_v4_sensor_id { + ACER_WMID_SENSOR_CPU_TEMPERATURE = 0x01, + ACER_WMID_SENSOR_CPU_FAN_SPEED = 0x02, + ACER_WMID_SENSOR_EXTERNAL_TEMPERATURE_2 = 0x03, + ACER_WMID_SENSOR_GPU_FAN_SPEED = 0x06, + ACER_WMID_SENSOR_GPU_TEMPERATURE = 0x0A, +}; + +enum acer_wmi_predator_v4_oc { + ACER_WMID_OC_NORMAL = 0x0000, + ACER_WMID_OC_TURBO = 0x0002, +}; + +enum acer_wmi_gaming_misc_setting { + ACER_WMID_MISC_SETTING_OC_1 = 0x0005, + ACER_WMID_MISC_SETTING_OC_2 = 0x0007, + ACER_WMID_MISC_SETTING_SUPPORTED_PROFILES = 0x000A, + ACER_WMID_MISC_SETTING_PLATFORM_PROFILE = 0x000B, }; static const struct key_entry acer_wmi_keymap[] __initconst = { @@ -247,7 +276,7 @@ struct hotkey_function_type_aa { #define ACER_CAP_TURBO_LED BIT(8) #define ACER_CAP_TURBO_FAN BIT(9) #define ACER_CAP_PLATFORM_PROFILE BIT(10) -#define ACER_CAP_FAN_SPEED_READ BIT(11) +#define ACER_CAP_HWMON BIT(11) /* * Interface type flags @@ -272,6 +301,7 @@ static u16 commun_func_bitmap; static u8 commun_fn_key_number; static bool cycle_gaming_thermal_profile = true; static bool predator_v4; +static u64 supported_sensors; module_param(mailled, int, 0444); module_param(brightness, int, 0444); @@ -359,7 +389,7 @@ static void __init set_quirks(void) if (quirks->predator_v4) interface->capability |= ACER_CAP_PLATFORM_PROFILE | - ACER_CAP_FAN_SPEED_READ; + ACER_CAP_HWMON; } static int __init dmi_matched(const struct dmi_system_id *dmi) @@ -755,29 +785,24 @@ static const struct dmi_system_id non_acer_quirks[] __initconst = { {} }; -static struct platform_profile_handler platform_profile_handler; +static struct device *platform_profile_device; static bool platform_profile_support; /* * The profile used before turbo mode. This variable is needed for * returning from turbo mode when the mode key is in toggle mode. */ -static int last_non_turbo_profile; - -enum acer_predator_v4_thermal_profile_ec { - ACER_PREDATOR_V4_THERMAL_PROFILE_ECO = 0x04, - ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO = 0x03, - ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE = 0x02, - ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET = 0x01, - ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED = 0x00, -}; +static int last_non_turbo_profile = INT_MIN; + +/* The most performant supported profile */ +static int acer_predator_v4_max_perf; -enum acer_predator_v4_thermal_profile_wmi { - ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI = 0x060B, - ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI = 0x050B, - ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE_WMI = 0x040B, - ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET_WMI = 0x0B, - ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI = 0x010B, +enum acer_predator_v4_thermal_profile { + ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET = 0x00, + ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED = 0x01, + ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE = 0x04, + ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO = 0x05, + ACER_PREDATOR_V4_THERMAL_PROFILE_ECO = 0x06, }; /* Find which quirks are needed for a particular vendor/ model pair */ @@ -1490,6 +1515,45 @@ WMI_gaming_execute_u64(u32 method_id, u64 in, u64 *out) return status; } +static int WMI_gaming_execute_u32_u64(u32 method_id, u32 in, u64 *out) +{ + struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer input = { + .length = sizeof(in), + .pointer = &in, + }; + union acpi_object *obj; + acpi_status status; + int ret = 0; + + status = wmi_evaluate_method(WMID_GUID4, 0, method_id, &input, &result); + if (ACPI_FAILURE(status)) + return -EIO; + + obj = result.pointer; + if (obj && out) { + switch (obj->type) { + case ACPI_TYPE_INTEGER: + *out = obj->integer.value; + break; + case ACPI_TYPE_BUFFER: + if (obj->buffer.length < sizeof(*out)) + ret = -ENOMSG; + else + *out = get_unaligned_le64(obj->buffer.pointer); + + break; + default: + ret = -ENOMSG; + break; + } + } + + kfree(obj); + + return ret; +} + static acpi_status WMID_gaming_set_u64(u64 value, u32 cap) { u32 method_id = 0; @@ -1504,9 +1568,6 @@ static acpi_status WMID_gaming_set_u64(u64 value, u32 cap) case ACER_CAP_TURBO_FAN: method_id = ACER_WMID_SET_GAMING_FAN_BEHAVIOR; break; - case ACER_CAP_TURBO_OC: - method_id = ACER_WMID_SET_GAMING_MISC_SETTING_METHODID; - break; default: return AE_BAD_PARAMETER; } @@ -1539,6 +1600,24 @@ static acpi_status WMID_gaming_get_u64(u64 *value, u32 cap) return status; } +static int WMID_gaming_get_sys_info(u32 command, u64 *out) +{ + acpi_status status; + u64 result; + + status = WMI_gaming_execute_u64(ACER_WMID_GET_GAMING_SYS_INFO_METHODID, command, &result); + if (ACPI_FAILURE(status)) + return -EIO; + + /* The return status must be zero for the operation to have succeeded */ + if (FIELD_GET(ACER_PREDATOR_V4_RETURN_STATUS_BIT_MASK, result)) + return -EIO; + + *out = result; + + return 0; +} + static void WMID_gaming_set_fan_mode(u8 fan_mode) { /* fan_mode = 1 is used for auto, fan_mode = 2 used for turbo*/ @@ -1560,6 +1639,48 @@ static void WMID_gaming_set_fan_mode(u8 fan_mode) WMID_gaming_set_u64(gpu_fan_config2 | gpu_fan_config1 << 16, ACER_CAP_TURBO_FAN); } +static int WMID_gaming_set_misc_setting(enum acer_wmi_gaming_misc_setting setting, u8 value) +{ + acpi_status status; + u64 input = 0; + u64 result; + + input |= FIELD_PREP(ACER_GAMING_MISC_SETTING_INDEX_MASK, setting); + input |= FIELD_PREP(ACER_GAMING_MISC_SETTING_VALUE_MASK, value); + + status = WMI_gaming_execute_u64(ACER_WMID_SET_GAMING_MISC_SETTING_METHODID, input, &result); + if (ACPI_FAILURE(status)) + return -EIO; + + /* The return status must be zero for the operation to have succeeded */ + if (FIELD_GET(ACER_GAMING_MISC_SETTING_STATUS_MASK, result)) + return -EIO; + + return 0; +} + +static int WMID_gaming_get_misc_setting(enum acer_wmi_gaming_misc_setting setting, u8 *value) +{ + u64 input = 0; + u64 result; + int ret; + + input |= FIELD_PREP(ACER_GAMING_MISC_SETTING_INDEX_MASK, setting); + + ret = WMI_gaming_execute_u32_u64(ACER_WMID_GET_GAMING_MISC_SETTING_METHODID, input, + &result); + if (ret < 0) + return ret; + + /* The return status must be zero for the operation to have succeeded */ + if (FIELD_GET(ACER_GAMING_MISC_SETTING_STATUS_MASK, result)) + return -EIO; + + *value = FIELD_GET(ACER_GAMING_MISC_SETTING_VALUE_MASK, result); + + return 0; +} + /* * Generic Device (interface-independent) */ @@ -1786,26 +1907,6 @@ static int acer_gsensor_event(void) return 0; } -static int acer_get_fan_speed(int fan) -{ - if (quirks->predator_v4) { - acpi_status status; - u64 fanspeed; - - status = WMI_gaming_execute_u64( - ACER_WMID_GET_GAMING_SYS_INFO_METHODID, - fan == 0 ? ACER_WMID_CMD_GET_PREDATOR_V4_CPU_FAN_SPEED : - ACER_WMID_CMD_GET_PREDATOR_V4_GPU_FAN_SPEED, - &fanspeed); - - if (ACPI_FAILURE(status)) - return -EIO; - - return FIELD_GET(ACER_PREDATOR_V4_FAN_SPEED_READ_BIT_MASK, fanspeed); - } - return -EOPNOTSUPP; -} - /* * Predator series turbo button */ @@ -1825,8 +1926,12 @@ static int acer_toggle_turbo(void) WMID_gaming_set_fan_mode(0x1); /* Set OC to normal */ - WMID_gaming_set_u64(0x5, ACER_CAP_TURBO_OC); - WMID_gaming_set_u64(0x7, ACER_CAP_TURBO_OC); + if (has_cap(ACER_CAP_TURBO_OC)) { + WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_OC_1, + ACER_WMID_OC_NORMAL); + WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_OC_2, + ACER_WMID_OC_NORMAL); + } } else { /* Turn on turbo led */ WMID_gaming_set_u64(0x10001, ACER_CAP_TURBO_LED); @@ -1835,22 +1940,25 @@ static int acer_toggle_turbo(void) WMID_gaming_set_fan_mode(0x2); /* Set OC to turbo mode */ - WMID_gaming_set_u64(0x205, ACER_CAP_TURBO_OC); - WMID_gaming_set_u64(0x207, ACER_CAP_TURBO_OC); + if (has_cap(ACER_CAP_TURBO_OC)) { + WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_OC_1, + ACER_WMID_OC_TURBO); + WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_OC_2, + ACER_WMID_OC_TURBO); + } } return turbo_led_state; } static int -acer_predator_v4_platform_profile_get(struct platform_profile_handler *pprof, +acer_predator_v4_platform_profile_get(struct device *dev, enum platform_profile_option *profile) { u8 tp; int err; - err = ec_read(ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET, &tp); - - if (err < 0) + err = WMID_gaming_get_misc_setting(ACER_WMID_MISC_SETTING_PLATFORM_PROFILE, &tp); + if (err) return err; switch (tp) { @@ -1877,74 +1985,112 @@ acer_predator_v4_platform_profile_get(struct platform_profile_handler *pprof, } static int -acer_predator_v4_platform_profile_set(struct platform_profile_handler *pprof, +acer_predator_v4_platform_profile_set(struct device *dev, enum platform_profile_option profile) { - int tp; - acpi_status status; + int err, tp; switch (profile) { case PLATFORM_PROFILE_PERFORMANCE: - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI; + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO; break; case PLATFORM_PROFILE_BALANCED_PERFORMANCE: - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE_WMI; + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE; break; case PLATFORM_PROFILE_BALANCED: - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI; + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED; break; case PLATFORM_PROFILE_QUIET: - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET_WMI; + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET; break; case PLATFORM_PROFILE_LOW_POWER: - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI; + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO; break; default: return -EOPNOTSUPP; } - status = WMI_gaming_execute_u64( - ACER_WMID_SET_GAMING_MISC_SETTING_METHODID, tp, NULL); - - if (ACPI_FAILURE(status)) - return -EIO; + err = WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_PLATFORM_PROFILE, tp); + if (err) + return err; - if (tp != ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI) + if (tp != acer_predator_v4_max_perf) last_non_turbo_profile = tp; return 0; } -static int acer_platform_profile_setup(void) +static int +acer_predator_v4_platform_profile_probe(void *drvdata, unsigned long *choices) +{ + unsigned long supported_profiles; + int err; + + err = WMID_gaming_get_misc_setting(ACER_WMID_MISC_SETTING_SUPPORTED_PROFILES, + (u8 *)&supported_profiles); + if (err) + return err; + + /* Iterate through supported profiles in order of increasing performance */ + if (test_bit(ACER_PREDATOR_V4_THERMAL_PROFILE_ECO, &supported_profiles)) { + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); + acer_predator_v4_max_perf = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO; + last_non_turbo_profile = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO; + } + + if (test_bit(ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET, &supported_profiles)) { + set_bit(PLATFORM_PROFILE_QUIET, choices); + acer_predator_v4_max_perf = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET; + last_non_turbo_profile = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET; + } + + if (test_bit(ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED, &supported_profiles)) { + set_bit(PLATFORM_PROFILE_BALANCED, choices); + acer_predator_v4_max_perf = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED; + last_non_turbo_profile = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED; + } + + if (test_bit(ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE, &supported_profiles)) { + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices); + acer_predator_v4_max_perf = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE; + + /* We only use this profile as a fallback option in case no prior + * profile is supported. + */ + if (last_non_turbo_profile < 0) + last_non_turbo_profile = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE; + } + + if (test_bit(ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO, &supported_profiles)) { + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + acer_predator_v4_max_perf = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO; + + /* We need to handle the hypothetical case where only the turbo profile + * is supported. In this case the turbo toggle will essentially be a + * no-op. + */ + if (last_non_turbo_profile < 0) + last_non_turbo_profile = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO; + } + + return 0; +} + +static const struct platform_profile_ops acer_predator_v4_platform_profile_ops = { + .probe = acer_predator_v4_platform_profile_probe, + .profile_get = acer_predator_v4_platform_profile_get, + .profile_set = acer_predator_v4_platform_profile_set, +}; + +static int acer_platform_profile_setup(struct platform_device *device) { if (quirks->predator_v4) { - int err; - - platform_profile_handler.profile_get = - acer_predator_v4_platform_profile_get; - platform_profile_handler.profile_set = - acer_predator_v4_platform_profile_set; - - set_bit(PLATFORM_PROFILE_PERFORMANCE, - platform_profile_handler.choices); - set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, - platform_profile_handler.choices); - set_bit(PLATFORM_PROFILE_BALANCED, - platform_profile_handler.choices); - set_bit(PLATFORM_PROFILE_QUIET, - platform_profile_handler.choices); - set_bit(PLATFORM_PROFILE_LOW_POWER, - platform_profile_handler.choices); - - err = platform_profile_register(&platform_profile_handler); - if (err) - return err; + platform_profile_device = devm_platform_profile_register( + &device->dev, "acer-wmi", NULL, &acer_predator_v4_platform_profile_ops); + if (IS_ERR(platform_profile_device)) + return PTR_ERR(platform_profile_device); platform_profile_support = true; - - /* Set default non-turbo profile */ - last_non_turbo_profile = - ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI; } return 0; } @@ -1952,83 +2098,41 @@ static int acer_platform_profile_setup(void) static int acer_thermal_profile_change(void) { /* - * This mode key can rotate each mode or toggle turbo mode. - * On battery, only ECO and BALANCED mode are available. + * This mode key will either cycle through each mode or toggle the + * most performant profile. */ if (quirks->predator_v4) { u8 current_tp; - int tp, err; - u64 on_AC; - acpi_status status; + int err, tp; - err = ec_read(ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET, - ¤t_tp); - - if (err < 0) - return err; + if (cycle_gaming_thermal_profile) { + platform_profile_cycle(); + } else { + /* Do nothing if no suitable platform profiles where found */ + if (last_non_turbo_profile < 0) + return 0; - /* Check power source */ - status = WMI_gaming_execute_u64( - ACER_WMID_GET_GAMING_SYS_INFO_METHODID, - ACER_WMID_CMD_GET_PREDATOR_V4_BAT_STATUS, &on_AC); + err = WMID_gaming_get_misc_setting( + ACER_WMID_MISC_SETTING_PLATFORM_PROFILE, ¤t_tp); + if (err) + return err; - if (ACPI_FAILURE(status)) - return -EIO; - - switch (current_tp) { - case ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO: - if (!on_AC) - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI; - else if (cycle_gaming_thermal_profile) - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI; - else + if (current_tp == acer_predator_v4_max_perf) tp = last_non_turbo_profile; - break; - case ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE: - if (!on_AC) - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI; - else - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI; - break; - case ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED: - if (!on_AC) - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI; - else if (cycle_gaming_thermal_profile) - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE_WMI; - else - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI; - break; - case ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET: - if (!on_AC) - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI; - else if (cycle_gaming_thermal_profile) - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI; - else - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI; - break; - case ACER_PREDATOR_V4_THERMAL_PROFILE_ECO: - if (!on_AC) - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI; - else if (cycle_gaming_thermal_profile) - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET_WMI; else - tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI; - break; - default: - return -EOPNOTSUPP; - } - - status = WMI_gaming_execute_u64( - ACER_WMID_SET_GAMING_MISC_SETTING_METHODID, tp, NULL); + tp = acer_predator_v4_max_perf; - if (ACPI_FAILURE(status)) - return -EIO; + err = WMID_gaming_set_misc_setting( + ACER_WMID_MISC_SETTING_PLATFORM_PROFILE, tp); + if (err) + return err; - /* Store non-turbo profile for turbo mode toggle*/ - if (tp != ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI) - last_non_turbo_profile = tp; + /* Store last profile for toggle */ + if (current_tp != acer_predator_v4_max_perf) + last_non_turbo_profile = current_tp; - platform_profile_notify(); + platform_profile_notify(platform_profile_device); + } } return 0; @@ -2575,12 +2679,12 @@ static int acer_platform_probe(struct platform_device *device) goto error_rfkill; if (has_cap(ACER_CAP_PLATFORM_PROFILE)) { - err = acer_platform_profile_setup(); + err = acer_platform_profile_setup(device); if (err) goto error_platform_profile; } - if (has_cap(ACER_CAP_FAN_SPEED_READ)) { + if (has_cap(ACER_CAP_HWMON)) { err = acer_wmi_hwmon_init(); if (err) goto error_hwmon; @@ -2589,8 +2693,6 @@ static int acer_platform_probe(struct platform_device *device) return 0; error_hwmon: - if (platform_profile_support) - platform_profile_remove(); error_platform_profile: acer_rfkill_exit(); error_rfkill: @@ -2611,9 +2713,6 @@ static void acer_platform_remove(struct platform_device *device) acer_backlight_exit(); acer_rfkill_exit(); - - if (platform_profile_support) - platform_profile_remove(); } #ifdef CONFIG_PM_SLEEP @@ -2700,43 +2799,86 @@ static void __init create_debugfs(void) &interface->debug.wmid_devices); } +static const enum acer_wmi_predator_v4_sensor_id acer_wmi_temp_channel_to_sensor_id[] = { + [0] = ACER_WMID_SENSOR_CPU_TEMPERATURE, + [1] = ACER_WMID_SENSOR_GPU_TEMPERATURE, + [2] = ACER_WMID_SENSOR_EXTERNAL_TEMPERATURE_2, +}; + +static const enum acer_wmi_predator_v4_sensor_id acer_wmi_fan_channel_to_sensor_id[] = { + [0] = ACER_WMID_SENSOR_CPU_FAN_SPEED, + [1] = ACER_WMID_SENSOR_GPU_FAN_SPEED, +}; + static umode_t acer_wmi_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) { + enum acer_wmi_predator_v4_sensor_id sensor_id; + const u64 *supported_sensors = data; + switch (type) { + case hwmon_temp: + sensor_id = acer_wmi_temp_channel_to_sensor_id[channel]; + break; case hwmon_fan: - if (acer_get_fan_speed(channel) >= 0) - return 0444; + sensor_id = acer_wmi_fan_channel_to_sensor_id[channel]; break; default: return 0; } + if (*supported_sensors & BIT(sensor_id - 1)) + return 0444; + return 0; } static int acer_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { + u64 command = ACER_WMID_CMD_GET_PREDATOR_V4_SENSOR_READING; + u64 result; int ret; switch (type) { + case hwmon_temp: + command |= FIELD_PREP(ACER_PREDATOR_V4_SENSOR_INDEX_BIT_MASK, + acer_wmi_temp_channel_to_sensor_id[channel]); + + ret = WMID_gaming_get_sys_info(command, &result); + if (ret < 0) + return ret; + + result = FIELD_GET(ACER_PREDATOR_V4_SENSOR_READING_BIT_MASK, result); + *val = result * MILLIDEGREE_PER_DEGREE; + return 0; case hwmon_fan: - ret = acer_get_fan_speed(channel); + command |= FIELD_PREP(ACER_PREDATOR_V4_SENSOR_INDEX_BIT_MASK, + acer_wmi_fan_channel_to_sensor_id[channel]); + + ret = WMID_gaming_get_sys_info(command, &result); if (ret < 0) return ret; - *val = ret; - break; + + *val = FIELD_GET(ACER_PREDATOR_V4_SENSOR_READING_BIT_MASK, result); + return 0; default: return -EOPNOTSUPP; } - - return 0; } static const struct hwmon_channel_info *const acer_wmi_hwmon_info[] = { - HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT), NULL + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT, + HWMON_T_INPUT, + HWMON_T_INPUT + ), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT, + HWMON_F_INPUT + ), + NULL }; static const struct hwmon_ops acer_wmi_hwmon_ops = { @@ -2753,9 +2895,20 @@ static int acer_wmi_hwmon_init(void) { struct device *dev = &acer_platform_device->dev; struct device *hwmon; + u64 result; + int ret; + + ret = WMID_gaming_get_sys_info(ACER_WMID_CMD_GET_PREDATOR_V4_SUPPORTED_SENSORS, &result); + if (ret < 0) + return ret; + + /* Return early if no sensors are available */ + supported_sensors = FIELD_GET(ACER_PREDATOR_V4_SUPPORTED_SENSORS_BIT_MASK, result); + if (!supported_sensors) + return 0; hwmon = devm_hwmon_device_register_with_info(dev, "acer", - &acer_platform_driver, + &supported_sensors, &acer_wmi_hwmon_chip_info, NULL); diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c index e981d45e1c12..444b43be35a2 100644 --- a/drivers/platform/x86/amd/hsmp/acpi.c +++ b/drivers/platform/x86/amd/hsmp/acpi.c @@ -226,7 +226,7 @@ static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind) } static ssize_t hsmp_metric_tbl_acpi_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); @@ -285,19 +285,19 @@ static int init_acpi(struct device *dev) return ret; } -static struct bin_attribute hsmp_metric_tbl_attr = { +static const struct bin_attribute hsmp_metric_tbl_attr = { .attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444}, - .read = hsmp_metric_tbl_acpi_read, + .read_new = hsmp_metric_tbl_acpi_read, .size = sizeof(struct hsmp_metric_table), }; -static struct bin_attribute *hsmp_attr_list[] = { +static const struct bin_attribute *hsmp_attr_list[] = { &hsmp_metric_tbl_attr, NULL }; -static struct attribute_group hsmp_attr_grp = { - .bin_attrs = hsmp_attr_list, +static const struct attribute_group hsmp_attr_grp = { + .bin_attrs_new = hsmp_attr_list, .is_bin_visible = hsmp_is_sock_attr_visible, }; diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c index 227b4ad4a51a..03164e30b3a5 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.c +++ b/drivers/platform/x86/amd/hsmp/hsmp.c @@ -33,7 +33,13 @@ #define HSMP_WR true #define HSMP_RD false -#define DRIVER_VERSION "2.3" +#define DRIVER_VERSION "2.4" + +/* + * When same message numbers are used for both GET and SET operation, + * bit:31 indicates whether its SET or GET operation. + */ +#define CHECK_GET_BIT BIT(31) static struct hsmp_plat_device hsmp_pdev; @@ -167,11 +173,28 @@ static int validate_message(struct hsmp_message *msg) if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_RSVD) return -ENOMSG; - /* num_args and response_sz against the HSMP spec */ - if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args || - msg->response_sz != hsmp_msg_desc_table[msg->msg_id].response_sz) + /* + * num_args passed by user should match the num_args specified in + * message description table. + */ + if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args) return -EINVAL; + /* + * Some older HSMP SET messages are updated to add GET in the same message. + * In these messages, GET returns the current value and SET also returns + * the successfully set value. To support this GET and SET in same message + * while maintaining backward compatibility for the HSMP users, + * hsmp_msg_desc_table[] indicates only maximum allowed response_sz. + */ + if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_SET_GET) { + if (msg->response_sz > hsmp_msg_desc_table[msg->msg_id].response_sz) + return -EINVAL; + } else { + /* only HSMP_SET or HSMP_GET messages go through this strict check */ + if (msg->response_sz != hsmp_msg_desc_table[msg->msg_id].response_sz) + return -EINVAL; + } return 0; } @@ -239,6 +262,18 @@ int hsmp_test(u16 sock_ind, u32 value) } EXPORT_SYMBOL_NS_GPL(hsmp_test, "AMD_HSMP"); +static bool is_get_msg(struct hsmp_message *msg) +{ + if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_GET) + return true; + + if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_SET_GET && + (msg->args[0] & CHECK_GET_BIT)) + return true; + + return false; +} + long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { int __user *arguser = (int __user *)arg; @@ -261,7 +296,7 @@ long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) * Device is opened in O_WRONLY mode * Execute only set/configure commands */ - if (hsmp_msg_desc_table[msg.msg_id].type != HSMP_SET) + if (is_get_msg(&msg)) return -EPERM; break; case FMODE_READ: @@ -269,7 +304,7 @@ long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) * Device is opened in O_RDONLY mode * Execute only get/monitor commands */ - if (hsmp_msg_desc_table[msg.msg_id].type != HSMP_GET) + if (!is_get_msg(&msg)) return -EPERM; break; case FMODE_READ | FMODE_WRITE: diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c index a61f815c9f80..02ca85762b68 100644 --- a/drivers/platform/x86/amd/hsmp/plat.c +++ b/drivers/platform/x86/amd/hsmp/plat.c @@ -59,7 +59,7 @@ static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset, } static ssize_t hsmp_metric_tbl_plat_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct hsmp_socket *sock; @@ -97,13 +97,13 @@ static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj, * is_bin_visible function is used to show / hide the necessary groups. */ #define HSMP_BIN_ATTR(index, _list) \ -static struct bin_attribute attr##index = { \ +static const struct bin_attribute attr##index = { \ .attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444}, \ .private = (void *)index, \ - .read = hsmp_metric_tbl_plat_read, \ + .read_new = hsmp_metric_tbl_plat_read, \ .size = sizeof(struct hsmp_metric_table), \ }; \ -static struct bin_attribute _list[] = { \ +static const struct bin_attribute _list[] = { \ &attr##index, \ NULL \ } @@ -118,8 +118,8 @@ HSMP_BIN_ATTR(6, *sock6_attr_list); HSMP_BIN_ATTR(7, *sock7_attr_list); #define HSMP_BIN_ATTR_GRP(index, _list, _name) \ -static struct attribute_group sock##index##_attr_grp = { \ - .bin_attrs = _list, \ +static const struct attribute_group sock##index##_attr_grp = { \ + .bin_attrs_new = _list, \ .is_bin_visible = hsmp_is_sock_attr_visible, \ .name = #_name, \ } diff --git a/drivers/platform/x86/amd/pmc/Kconfig b/drivers/platform/x86/amd/pmc/Kconfig index 94f9563d8be7..eeffdafd686e 100644 --- a/drivers/platform/x86/amd/pmc/Kconfig +++ b/drivers/platform/x86/amd/pmc/Kconfig @@ -5,7 +5,7 @@ config AMD_PMC tristate "AMD SoC PMC driver" - depends on ACPI && PCI && RTC_CLASS && AMD_NB + depends on ACPI && PCI && RTC_CLASS && AMD_NODE depends on SUSPEND select SERIO help diff --git a/drivers/platform/x86/amd/pmc/Makefile b/drivers/platform/x86/amd/pmc/Makefile index f1d9ab19d24c..255d94ddf999 100644 --- a/drivers/platform/x86/amd/pmc/Makefile +++ b/drivers/platform/x86/amd/pmc/Makefile @@ -4,6 +4,6 @@ # AMD Power Management Controller Driver # -amd-pmc-objs := pmc.o pmc-quirks.o +amd-pmc-objs := pmc.o pmc-quirks.o mp1_stb.o obj-$(CONFIG_AMD_PMC) += amd-pmc.o amd-pmc-$(CONFIG_AMD_MP2_STB) += mp2_stb.o diff --git a/drivers/platform/x86/amd/pmc/mp1_stb.c b/drivers/platform/x86/amd/pmc/mp1_stb.c new file mode 100644 index 000000000000..c005f00988f7 --- /dev/null +++ b/drivers/platform/x86/amd/pmc/mp1_stb.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AMD MP1 Smart Trace Buffer (STB) Layer + * + * Copyright (c) 2024, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> + * Sanket Goswami <Sanket.Goswami@amd.com> + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <asm/amd_nb.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> + +#include "pmc.h" + +/* STB Spill to DRAM Parameters */ +#define S2D_TELEMETRY_DRAMBYTES_MAX 0x1000000 +#define S2D_TELEMETRY_BYTES_MAX 0x100000U +#define S2D_RSVD_RAM_SPACE 0x100000 + +/* STB Registers */ +#define AMD_STB_PMI_0 0x03E30600 +#define AMD_PMC_STB_DUMMY_PC 0xC6000007 + +/* STB Spill to DRAM Message Definition */ +#define STB_FORCE_FLUSH_DATA 0xCF +#define FIFO_SIZE 4096 + +/* STB S2D(Spill to DRAM) has different message port offset */ +#define AMD_S2D_REGISTER_MESSAGE 0xA20 +#define AMD_S2D_REGISTER_RESPONSE 0xA80 +#define AMD_S2D_REGISTER_ARGUMENT 0xA88 + +/* STB S2D (Spill to DRAM) message port offset for 44h model */ +#define AMD_GNR_REGISTER_MESSAGE 0x524 +#define AMD_GNR_REGISTER_RESPONSE 0x570 +#define AMD_GNR_REGISTER_ARGUMENT 0xA40 + +static bool enable_stb; +module_param(enable_stb, bool, 0644); +MODULE_PARM_DESC(enable_stb, "Enable the STB debug mechanism"); + +static bool dump_custom_stb; +module_param(dump_custom_stb, bool, 0644); +MODULE_PARM_DESC(dump_custom_stb, "Enable to dump full STB buffer"); + +enum s2d_arg { + S2D_TELEMETRY_SIZE = 0x01, + S2D_PHYS_ADDR_LOW, + S2D_PHYS_ADDR_HIGH, + S2D_NUM_SAMPLES, + S2D_DRAM_SIZE, +}; + +struct amd_stb_v2_data { + size_t size; + u8 data[] __counted_by(size); +}; + +int amd_stb_write(struct amd_pmc_dev *dev, u32 data) +{ + int err; + + err = amd_smn_write(0, AMD_STB_PMI_0, data); + if (err) { + dev_err(dev->dev, "failed to write data in stb: 0x%X\n", AMD_STB_PMI_0); + return pcibios_err_to_errno(err); + } + + return 0; +} + +int amd_stb_read(struct amd_pmc_dev *dev, u32 *buf) +{ + int i, err; + + for (i = 0; i < FIFO_SIZE; i++) { + err = amd_smn_read(0, AMD_STB_PMI_0, buf++); + if (err) { + dev_err(dev->dev, "error reading data from stb: 0x%X\n", AMD_STB_PMI_0); + return pcibios_err_to_errno(err); + } + } + + return 0; +} + +static int amd_stb_debugfs_open(struct inode *inode, struct file *filp) +{ + struct amd_pmc_dev *dev = filp->f_inode->i_private; + u32 size = FIFO_SIZE * sizeof(u32); + u32 *buf; + int rc; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + rc = amd_stb_read(dev, buf); + if (rc) { + kfree(buf); + return rc; + } + + filp->private_data = buf; + return rc; +} + +static ssize_t amd_stb_debugfs_read(struct file *filp, char __user *buf, size_t size, loff_t *pos) +{ + if (!filp->private_data) + return -EINVAL; + + return simple_read_from_buffer(buf, size, pos, filp->private_data, + FIFO_SIZE * sizeof(u32)); +} + +static int amd_stb_debugfs_release(struct inode *inode, struct file *filp) +{ + kfree(filp->private_data); + return 0; +} + +static const struct file_operations amd_stb_debugfs_fops = { + .owner = THIS_MODULE, + .open = amd_stb_debugfs_open, + .read = amd_stb_debugfs_read, + .release = amd_stb_debugfs_release, +}; + +/* Enhanced STB Firmware Reporting Mechanism */ +static int amd_stb_handle_efr(struct file *filp) +{ + struct amd_pmc_dev *dev = filp->f_inode->i_private; + struct amd_stb_v2_data *stb_data_arr; + u32 fsize; + + fsize = dev->dram_size - S2D_RSVD_RAM_SPACE; + stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL); + if (!stb_data_arr) + return -ENOMEM; + + stb_data_arr->size = fsize; + memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize); + filp->private_data = stb_data_arr; + + return 0; +} + +static int amd_stb_debugfs_open_v2(struct inode *inode, struct file *filp) +{ + struct amd_pmc_dev *dev = filp->f_inode->i_private; + u32 fsize, num_samples, val, stb_rdptr_offset = 0; + struct amd_stb_v2_data *stb_data_arr; + int ret; + + /* Write dummy postcode while reading the STB buffer */ + ret = amd_stb_write(dev, AMD_PMC_STB_DUMMY_PC); + if (ret) + dev_err(dev->dev, "error writing to STB: %d\n", ret); + + /* Spill to DRAM num_samples uses separate SMU message port */ + dev->msg_port = MSG_PORT_S2D; + + ret = amd_pmc_send_cmd(dev, 0, &val, STB_FORCE_FLUSH_DATA, 1); + if (ret) + dev_dbg_once(dev->dev, "S2D force flush not supported: %d\n", ret); + + /* + * We have a custom stb size and the PMFW is supposed to give + * the enhanced dram size. Note that we land here only for the + * platforms that support enhanced dram size reporting. + */ + if (dump_custom_stb) + return amd_stb_handle_efr(filp); + + /* Get the num_samples to calculate the last push location */ + ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, dev->stb_arg.s2d_msg_id, true); + /* Clear msg_port for other SMU operation */ + dev->msg_port = MSG_PORT_PMC; + if (ret) { + dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret); + return ret; + } + + fsize = min(num_samples, S2D_TELEMETRY_BYTES_MAX); + stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL); + if (!stb_data_arr) + return -ENOMEM; + + stb_data_arr->size = fsize; + + /* + * Start capturing data from the last push location. + * This is for general cases, where the stb limits + * are meant for standard usage. + */ + if (num_samples > S2D_TELEMETRY_BYTES_MAX) { + /* First read oldest data starting 1 behind last write till end of ringbuffer */ + stb_rdptr_offset = num_samples % S2D_TELEMETRY_BYTES_MAX; + fsize = S2D_TELEMETRY_BYTES_MAX - stb_rdptr_offset; + + memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr + stb_rdptr_offset, fsize); + /* Second copy the newer samples from offset 0 - last write */ + memcpy_fromio(stb_data_arr->data + fsize, dev->stb_virt_addr, stb_rdptr_offset); + } else { + memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize); + } + + filp->private_data = stb_data_arr; + + return 0; +} + +static ssize_t amd_stb_debugfs_read_v2(struct file *filp, char __user *buf, size_t size, + loff_t *pos) +{ + struct amd_stb_v2_data *data = filp->private_data; + + return simple_read_from_buffer(buf, size, pos, data->data, data->size); +} + +static int amd_stb_debugfs_release_v2(struct inode *inode, struct file *filp) +{ + kfree(filp->private_data); + return 0; +} + +static const struct file_operations amd_stb_debugfs_fops_v2 = { + .owner = THIS_MODULE, + .open = amd_stb_debugfs_open_v2, + .read = amd_stb_debugfs_read_v2, + .release = amd_stb_debugfs_release_v2, +}; + +static void amd_stb_update_args(struct amd_pmc_dev *dev) +{ + if (cpu_feature_enabled(X86_FEATURE_ZEN5)) + switch (boot_cpu_data.x86_model) { + case 0x44: + dev->stb_arg.msg = AMD_GNR_REGISTER_MESSAGE; + dev->stb_arg.arg = AMD_GNR_REGISTER_ARGUMENT; + dev->stb_arg.resp = AMD_GNR_REGISTER_RESPONSE; + return; + default: + break; + } + + dev->stb_arg.msg = AMD_S2D_REGISTER_MESSAGE; + dev->stb_arg.arg = AMD_S2D_REGISTER_ARGUMENT; + dev->stb_arg.resp = AMD_S2D_REGISTER_RESPONSE; +} + +static bool amd_is_stb_supported(struct amd_pmc_dev *dev) +{ + switch (dev->cpu_id) { + case AMD_CPU_ID_YC: + case AMD_CPU_ID_CB: + if (boot_cpu_data.x86_model == 0x44) + dev->stb_arg.s2d_msg_id = 0x9B; + else + dev->stb_arg.s2d_msg_id = 0xBE; + break; + case AMD_CPU_ID_PS: + dev->stb_arg.s2d_msg_id = 0x85; + break; + case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: + case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: + if (boot_cpu_data.x86_model == 0x70) + dev->stb_arg.s2d_msg_id = 0xF1; + else + dev->stb_arg.s2d_msg_id = 0xDE; + break; + default: + return false; + } + + amd_stb_update_args(dev); + return true; +} + +int amd_stb_s2d_init(struct amd_pmc_dev *dev) +{ + u32 phys_addr_low, phys_addr_hi; + u64 stb_phys_addr; + u32 size = 0; + int ret; + + if (!enable_stb) + return 0; + + if (amd_is_stb_supported(dev)) { + debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev, + &amd_stb_debugfs_fops_v2); + } else { + debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev, + &amd_stb_debugfs_fops); + return 0; + } + + /* Spill to DRAM feature uses separate SMU message port */ + dev->msg_port = MSG_PORT_S2D; + + amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->stb_arg.s2d_msg_id, true); + if (size != S2D_TELEMETRY_BYTES_MAX) + return -EIO; + + /* Get DRAM size */ + ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->stb_arg.s2d_msg_id, true); + if (ret || !dev->dram_size) + dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX; + + /* Get STB DRAM address */ + amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, dev->stb_arg.s2d_msg_id, true); + amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, dev->stb_arg.s2d_msg_id, true); + + stb_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low); + + /* Clear msg_port for other SMU operation */ + dev->msg_port = MSG_PORT_PMC; + + dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size); + if (!dev->stb_virt_addr) + return -ENOMEM; + + return 0; +} diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index a254debb9256..e6124498b195 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -10,8 +10,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <asm/amd_nb.h> #include <linux/acpi.h> +#include <linux/array_size.h> #include <linux/bitfield.h> #include <linux/bits.h> #include <linux/debugfs.h> @@ -28,6 +28,8 @@ #include <linux/seq_file.h> #include <linux/uaccess.h> +#include <asm/amd_node.h> + #include "pmc.h" /* SMU communication registers */ @@ -40,24 +42,9 @@ #define AMD_PMC_SCRATCH_REG_1AH 0xF14 /* STB Registers */ -#define AMD_PMC_STB_PMI_0 0x03E30600 #define AMD_PMC_STB_S2IDLE_PREPARE 0xC6000001 #define AMD_PMC_STB_S2IDLE_RESTORE 0xC6000002 #define AMD_PMC_STB_S2IDLE_CHECK 0xC6000003 -#define AMD_PMC_STB_DUMMY_PC 0xC6000007 - -/* STB S2D(Spill to DRAM) has different message port offset */ -#define AMD_S2D_REGISTER_MESSAGE 0xA20 -#define AMD_S2D_REGISTER_RESPONSE 0xA80 -#define AMD_S2D_REGISTER_ARGUMENT 0xA88 - -/* STB Spill to DRAM Parameters */ -#define S2D_TELEMETRY_BYTES_MAX 0x100000U -#define S2D_RSVD_RAM_SPACE 0x100000 -#define S2D_TELEMETRY_DRAMBYTES_MAX 0x1000000 - -/* STB Spill to DRAM Message Definition */ -#define STB_FORCE_FLUSH_DATA 0xCF /* Base address of SMU for mapping physical address to virtual address */ #define AMD_PMC_MAPPING_SIZE 0x01000 @@ -97,7 +84,6 @@ #define DELAY_MIN_US 2000 #define DELAY_MAX_US 3000 -#define FIFO_SIZE 4096 enum amd_pmc_def { MSG_TEST = 0x01, @@ -105,24 +91,39 @@ enum amd_pmc_def { MSG_OS_HINT_RN, }; -enum s2d_arg { - S2D_TELEMETRY_SIZE = 0x01, - S2D_PHYS_ADDR_LOW, - S2D_PHYS_ADDR_HIGH, - S2D_NUM_SAMPLES, - S2D_DRAM_SIZE, -}; - -struct amd_pmc_stb_v2_data { - size_t size; - u8 data[] __counted_by(size); -}; - struct amd_pmc_bit_map { const char *name; u32 bit_mask; }; +static const struct amd_pmc_bit_map soc15_ip_blk_v2[] = { + {"DISPLAY", BIT(0)}, + {"CPU", BIT(1)}, + {"GFX", BIT(2)}, + {"VDD", BIT(3)}, + {"VDD_CCX", BIT(4)}, + {"ACP", BIT(5)}, + {"VCN_0", BIT(6)}, + {"VCN_1", BIT(7)}, + {"ISP", BIT(8)}, + {"NBIO", BIT(9)}, + {"DF", BIT(10)}, + {"USB3_0", BIT(11)}, + {"USB3_1", BIT(12)}, + {"LAPIC", BIT(13)}, + {"USB3_2", BIT(14)}, + {"USB4_RT0", BIT(15)}, + {"USB4_RT1", BIT(16)}, + {"USB4_0", BIT(17)}, + {"USB4_1", BIT(18)}, + {"MPM", BIT(19)}, + {"JPEG_0", BIT(20)}, + {"JPEG_1", BIT(21)}, + {"IPU", BIT(22)}, + {"UMSCH", BIT(23)}, + {"VPE", BIT(24)}, +}; + static const struct amd_pmc_bit_map soc15_ip_blk[] = { {"DISPLAY", BIT(0)}, {"CPU", BIT(1)}, @@ -146,25 +147,13 @@ static const struct amd_pmc_bit_map soc15_ip_blk[] = { {"IPU", BIT(19)}, {"UMSCH", BIT(20)}, {"VPE", BIT(21)}, - {} }; -static bool enable_stb; -module_param(enable_stb, bool, 0644); -MODULE_PARM_DESC(enable_stb, "Enable the STB debug mechanism"); - static bool disable_workarounds; module_param(disable_workarounds, bool, 0644); MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs"); -static bool dump_custom_stb; -module_param(dump_custom_stb, bool, 0644); -MODULE_PARM_DESC(dump_custom_stb, "Enable to dump full STB buffer"); - static struct amd_pmc_dev pmc; -static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret); -static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf); -static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data); static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset) { @@ -193,155 +182,6 @@ struct smu_metrics { u64 timecondition_notmet_totaltime[32]; } __packed; -static int amd_pmc_stb_debugfs_open(struct inode *inode, struct file *filp) -{ - struct amd_pmc_dev *dev = filp->f_inode->i_private; - u32 size = FIFO_SIZE * sizeof(u32); - u32 *buf; - int rc; - - buf = kzalloc(size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - rc = amd_pmc_read_stb(dev, buf); - if (rc) { - kfree(buf); - return rc; - } - - filp->private_data = buf; - return rc; -} - -static ssize_t amd_pmc_stb_debugfs_read(struct file *filp, char __user *buf, size_t size, - loff_t *pos) -{ - if (!filp->private_data) - return -EINVAL; - - return simple_read_from_buffer(buf, size, pos, filp->private_data, - FIFO_SIZE * sizeof(u32)); -} - -static int amd_pmc_stb_debugfs_release(struct inode *inode, struct file *filp) -{ - kfree(filp->private_data); - return 0; -} - -static const struct file_operations amd_pmc_stb_debugfs_fops = { - .owner = THIS_MODULE, - .open = amd_pmc_stb_debugfs_open, - .read = amd_pmc_stb_debugfs_read, - .release = amd_pmc_stb_debugfs_release, -}; - -/* Enhanced STB Firmware Reporting Mechanism */ -static int amd_pmc_stb_handle_efr(struct file *filp) -{ - struct amd_pmc_dev *dev = filp->f_inode->i_private; - struct amd_pmc_stb_v2_data *stb_data_arr; - u32 fsize; - - fsize = dev->dram_size - S2D_RSVD_RAM_SPACE; - stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL); - if (!stb_data_arr) - return -ENOMEM; - - stb_data_arr->size = fsize; - memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize); - filp->private_data = stb_data_arr; - - return 0; -} - -static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) -{ - struct amd_pmc_dev *dev = filp->f_inode->i_private; - u32 fsize, num_samples, val, stb_rdptr_offset = 0; - struct amd_pmc_stb_v2_data *stb_data_arr; - int ret; - - /* Write dummy postcode while reading the STB buffer */ - ret = amd_pmc_write_stb(dev, AMD_PMC_STB_DUMMY_PC); - if (ret) - dev_err(dev->dev, "error writing to STB: %d\n", ret); - - /* Spill to DRAM num_samples uses separate SMU message port */ - dev->msg_port = 1; - - ret = amd_pmc_send_cmd(dev, 0, &val, STB_FORCE_FLUSH_DATA, 1); - if (ret) - dev_dbg_once(dev->dev, "S2D force flush not supported: %d\n", ret); - - /* - * We have a custom stb size and the PMFW is supposed to give - * the enhanced dram size. Note that we land here only for the - * platforms that support enhanced dram size reporting. - */ - if (dump_custom_stb) - return amd_pmc_stb_handle_efr(filp); - - /* Get the num_samples to calculate the last push location */ - ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, dev->s2d_msg_id, true); - /* Clear msg_port for other SMU operation */ - dev->msg_port = 0; - if (ret) { - dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret); - return ret; - } - - fsize = min(num_samples, S2D_TELEMETRY_BYTES_MAX); - stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL); - if (!stb_data_arr) - return -ENOMEM; - - stb_data_arr->size = fsize; - - /* - * Start capturing data from the last push location. - * This is for general cases, where the stb limits - * are meant for standard usage. - */ - if (num_samples > S2D_TELEMETRY_BYTES_MAX) { - /* First read oldest data starting 1 behind last write till end of ringbuffer */ - stb_rdptr_offset = num_samples % S2D_TELEMETRY_BYTES_MAX; - fsize = S2D_TELEMETRY_BYTES_MAX - stb_rdptr_offset; - - memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr + stb_rdptr_offset, fsize); - /* Second copy the newer samples from offset 0 - last write */ - memcpy_fromio(stb_data_arr->data + fsize, dev->stb_virt_addr, stb_rdptr_offset); - } else { - memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize); - } - - filp->private_data = stb_data_arr; - - return 0; -} - -static ssize_t amd_pmc_stb_debugfs_read_v2(struct file *filp, char __user *buf, size_t size, - loff_t *pos) -{ - struct amd_pmc_stb_v2_data *data = filp->private_data; - - return simple_read_from_buffer(buf, size, pos, data->data, data->size); -} - -static int amd_pmc_stb_debugfs_release_v2(struct inode *inode, struct file *filp) -{ - kfree(filp->private_data); - return 0; -} - -static const struct file_operations amd_pmc_stb_debugfs_fops_v2 = { - .owner = THIS_MODULE, - .open = amd_pmc_stb_debugfs_open_v2, - .read = amd_pmc_stb_debugfs_read_v2, - .release = amd_pmc_stb_debugfs_release_v2, -}; - static void amd_pmc_get_ip_info(struct amd_pmc_dev *dev) { switch (dev->cpu_id) { @@ -350,18 +190,23 @@ static void amd_pmc_get_ip_info(struct amd_pmc_dev *dev) case AMD_CPU_ID_YC: case AMD_CPU_ID_CB: dev->num_ips = 12; - dev->s2d_msg_id = 0xBE; + dev->ips_ptr = soc15_ip_blk; dev->smu_msg = 0x538; break; case AMD_CPU_ID_PS: dev->num_ips = 21; - dev->s2d_msg_id = 0x85; + dev->ips_ptr = soc15_ip_blk; dev->smu_msg = 0x538; break; case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: - dev->num_ips = 22; - dev->s2d_msg_id = 0xDE; + if (boot_cpu_data.x86_model == 0x70) { + dev->num_ips = ARRAY_SIZE(soc15_ip_blk_v2); + dev->ips_ptr = soc15_ip_blk_v2; + } else { + dev->num_ips = ARRAY_SIZE(soc15_ip_blk); + dev->ips_ptr = soc15_ip_blk; + } dev->smu_msg = 0x938; break; } @@ -529,8 +374,8 @@ static int smu_fw_info_show(struct seq_file *s, void *unused) seq_puts(s, "\n=== Active time (in us) ===\n"); for (idx = 0 ; idx < dev->num_ips ; idx++) { - if (soc15_ip_blk[idx].bit_mask & dev->active_ips) - seq_printf(s, "%-8s : %lld\n", soc15_ip_blk[idx].name, + if (dev->ips_ptr[idx].bit_mask & dev->active_ips) + seq_printf(s, "%-8s : %lld\n", dev->ips_ptr[idx].name, table.timecondition_notmet_lastcapture[idx]); } @@ -625,20 +470,6 @@ static void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev) debugfs_remove_recursive(dev->dbgfs_dir); } -static bool amd_pmc_is_stb_supported(struct amd_pmc_dev *dev) -{ - switch (dev->cpu_id) { - case AMD_CPU_ID_YC: - case AMD_CPU_ID_CB: - case AMD_CPU_ID_PS: - case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: - case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: - return true; - default: - return false; - } -} - static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev) { dev->dbgfs_dir = debugfs_create_dir("amd_pmc", NULL); @@ -648,14 +479,17 @@ static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev) &s0ix_stats_fops); debugfs_create_file("amd_pmc_idlemask", 0644, dev->dbgfs_dir, dev, &amd_pmc_idlemask_fops); - /* Enable STB only when the module_param is set */ - if (enable_stb) { - if (amd_pmc_is_stb_supported(dev)) - debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev, - &amd_pmc_stb_debugfs_fops_v2); - else - debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev, - &amd_pmc_stb_debugfs_fops); +} + +static char *amd_pmc_get_msg_port(struct amd_pmc_dev *dev) +{ + switch (dev->msg_port) { + case MSG_PORT_PMC: + return "PMC"; + case MSG_PORT_S2D: + return "S2D"; + default: + return "Invalid message port"; } } @@ -663,10 +497,10 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev) { u32 value, message, argument, response; - if (dev->msg_port) { - message = AMD_S2D_REGISTER_MESSAGE; - argument = AMD_S2D_REGISTER_ARGUMENT; - response = AMD_S2D_REGISTER_RESPONSE; + if (dev->msg_port == MSG_PORT_S2D) { + message = dev->stb_arg.msg; + argument = dev->stb_arg.arg; + response = dev->stb_arg.resp; } else { message = dev->smu_msg; argument = AMD_PMC_REGISTER_ARGUMENT; @@ -674,26 +508,26 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev) } value = amd_pmc_reg_read(dev, response); - dev_dbg(dev->dev, "AMD_%s_REGISTER_RESPONSE:%x\n", dev->msg_port ? "S2D" : "PMC", value); + dev_dbg(dev->dev, "AMD_%s_REGISTER_RESPONSE:%x\n", amd_pmc_get_msg_port(dev), value); value = amd_pmc_reg_read(dev, argument); - dev_dbg(dev->dev, "AMD_%s_REGISTER_ARGUMENT:%x\n", dev->msg_port ? "S2D" : "PMC", value); + dev_dbg(dev->dev, "AMD_%s_REGISTER_ARGUMENT:%x\n", amd_pmc_get_msg_port(dev), value); value = amd_pmc_reg_read(dev, message); - dev_dbg(dev->dev, "AMD_%s_REGISTER_MESSAGE:%x\n", dev->msg_port ? "S2D" : "PMC", value); + dev_dbg(dev->dev, "AMD_%s_REGISTER_MESSAGE:%x\n", amd_pmc_get_msg_port(dev), value); } -static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret) +int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret) { int rc; u32 val, message, argument, response; - mutex_lock(&dev->lock); + guard(mutex)(&dev->lock); - if (dev->msg_port) { - message = AMD_S2D_REGISTER_MESSAGE; - argument = AMD_S2D_REGISTER_ARGUMENT; - response = AMD_S2D_REGISTER_RESPONSE; + if (dev->msg_port == MSG_PORT_S2D) { + message = dev->stb_arg.msg; + argument = dev->stb_arg.arg; + response = dev->stb_arg.resp; } else { message = dev->smu_msg; argument = AMD_PMC_REGISTER_ARGUMENT; @@ -706,7 +540,7 @@ static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, PMC_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX); if (rc) { dev_err(dev->dev, "failed to talk to SMU\n"); - goto out_unlock; + return rc; } /* Write zero to response register */ @@ -724,7 +558,7 @@ static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, PMC_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX); if (rc) { dev_err(dev->dev, "SMU response timed out\n"); - goto out_unlock; + return rc; } switch (val) { @@ -738,21 +572,19 @@ static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, case AMD_PMC_RESULT_CMD_REJECT_BUSY: dev_err(dev->dev, "SMU not ready. err: 0x%x\n", val); rc = -EBUSY; - goto out_unlock; + break; case AMD_PMC_RESULT_CMD_UNKNOWN: dev_err(dev->dev, "SMU cmd unknown. err: 0x%x\n", val); rc = -EINVAL; - goto out_unlock; + break; case AMD_PMC_RESULT_CMD_REJECT_PREREQ: case AMD_PMC_RESULT_FAILED: default: dev_err(dev->dev, "SMU cmd failed. err: 0x%x\n", val); rc = -EIO; - goto out_unlock; + break; } -out_unlock: - mutex_unlock(&dev->lock); amd_pmc_dump_registers(dev); return rc; } @@ -881,7 +713,7 @@ static void amd_pmc_s2idle_prepare(void) return; } - rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_PREPARE); + rc = amd_stb_write(pdev, AMD_PMC_STB_S2IDLE_PREPARE); if (rc) dev_err(pdev->dev, "error writing to STB: %d\n", rc); } @@ -900,7 +732,7 @@ static void amd_pmc_s2idle_check(void) /* Dump the IdleMask before we add to the STB */ amd_pmc_idlemask_read(pdev, pdev->dev, NULL); - rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_CHECK); + rc = amd_stb_write(pdev, AMD_PMC_STB_S2IDLE_CHECK); if (rc) dev_err(pdev->dev, "error writing to STB: %d\n", rc); } @@ -927,7 +759,7 @@ static void amd_pmc_s2idle_restore(void) /* Let SMU know that we are looking for stats */ amd_pmc_dump_data(pdev); - rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_RESTORE); + rc = amd_stb_write(pdev, AMD_PMC_STB_S2IDLE_RESTORE); if (rc) dev_err(pdev->dev, "error writing to STB: %d\n", rc); @@ -981,74 +813,6 @@ static const struct pci_device_id pmc_pci_ids[] = { { } }; -static int amd_pmc_s2d_init(struct amd_pmc_dev *dev) -{ - u32 phys_addr_low, phys_addr_hi; - u64 stb_phys_addr; - u32 size = 0; - int ret; - - /* Spill to DRAM feature uses separate SMU message port */ - dev->msg_port = 1; - - amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->s2d_msg_id, true); - if (size != S2D_TELEMETRY_BYTES_MAX) - return -EIO; - - /* Get DRAM size */ - ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->s2d_msg_id, true); - if (ret || !dev->dram_size) - dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX; - - /* Get STB DRAM address */ - amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, dev->s2d_msg_id, true); - amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, dev->s2d_msg_id, true); - - if (!phys_addr_hi && !phys_addr_low) { - dev_err(dev->dev, "STB is not enabled on the system; disable enable_stb or contact system vendor\n"); - return -EINVAL; - } - - stb_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low); - - /* Clear msg_port for other SMU operation */ - dev->msg_port = 0; - - dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size); - if (!dev->stb_virt_addr) - return -ENOMEM; - - return 0; -} - -static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data) -{ - int err; - - err = amd_smn_write(0, AMD_PMC_STB_PMI_0, data); - if (err) { - dev_err(dev->dev, "failed to write data in stb: 0x%X\n", AMD_PMC_STB_PMI_0); - return pcibios_err_to_errno(err); - } - - return 0; -} - -static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf) -{ - int i, err; - - for (i = 0; i < FIFO_SIZE; i++) { - err = amd_smn_read(0, AMD_PMC_STB_PMI_0, buf++); - if (err) { - dev_err(dev->dev, "error reading data from stb: 0x%X\n", AMD_PMC_STB_PMI_0); - return pcibios_err_to_errno(err); - } - } - - return 0; -} - static int amd_pmc_probe(struct platform_device *pdev) { struct amd_pmc_dev *dev = &pmc; @@ -1106,12 +870,6 @@ static int amd_pmc_probe(struct platform_device *pdev) /* Get num of IP blocks within the SoC */ amd_pmc_get_ip_info(dev); - if (enable_stb && amd_pmc_is_stb_supported(dev)) { - err = amd_pmc_s2d_init(dev); - if (err) - goto err_pci_dev_put; - } - platform_set_drvdata(pdev, dev); if (IS_ENABLED(CONFIG_SUSPEND)) { err = acpi_register_lps0_dev(&amd_pmc_s2idle_dev_ops); @@ -1122,6 +880,10 @@ static int amd_pmc_probe(struct platform_device *pdev) } amd_pmc_dbgfs_register(dev); + err = amd_stb_s2d_init(dev); + if (err) + goto err_pci_dev_put; + if (IS_ENABLED(CONFIG_AMD_MP2_STB)) amd_mp2_stb_init(dev); pm_report_max_hw_sleep(U64_MAX); diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index f1166d15c856..f43f0253b0f5 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -14,6 +14,11 @@ #include <linux/types.h> #include <linux/mutex.h> +enum s2d_msg_port { + MSG_PORT_PMC, + MSG_PORT_S2D, +}; + struct amd_mp2_dev { void __iomem *mmio; void __iomem *vslbase; @@ -25,24 +30,31 @@ struct amd_mp2_dev { bool is_stb_data; }; +struct stb_arg { + u32 s2d_msg_id; + u32 msg; + u32 arg; + u32 resp; +}; + struct amd_pmc_dev { void __iomem *regbase; void __iomem *smu_virt_addr; void __iomem *stb_virt_addr; void __iomem *fch_virt_addr; - bool msg_port; u32 base_addr; u32 cpu_id; - u32 active_ips; u32 dram_size; + u32 active_ips; + const struct amd_pmc_bit_map *ips_ptr; u32 num_ips; - u32 s2d_msg_id; u32 smu_msg; /* SMU version information */ u8 smu_program; u8 major; u8 minor; u8 rev; + u8 msg_port; struct device *dev; struct pci_dev *rdev; struct mutex lock; /* generic mutex lock */ @@ -50,6 +62,7 @@ struct amd_pmc_dev { struct quirk_entry *quirks; bool disable_8042_wakeup; struct amd_mp2_dev *mp2; + struct stb_arg stb_arg; }; void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev); @@ -70,4 +83,9 @@ void amd_mp2_stb_deinit(struct amd_pmc_dev *dev); #define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122 #define PCI_DEVICE_ID_AMD_MP2_STB 0x172c +int amd_stb_s2d_init(struct amd_pmc_dev *dev); +int amd_stb_read(struct amd_pmc_dev *dev, u32 *buf); +int amd_stb_write(struct amd_pmc_dev *dev, u32 data); +int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret); + #endif /* PMC_H */ diff --git a/drivers/platform/x86/amd/pmf/Kconfig b/drivers/platform/x86/amd/pmf/Kconfig index 99d67cdbd91e..25b8f7ae3abd 100644 --- a/drivers/platform/x86/amd/pmf/Kconfig +++ b/drivers/platform/x86/amd/pmf/Kconfig @@ -7,7 +7,7 @@ config AMD_PMF tristate "AMD Platform Management Framework" depends on ACPI && PCI depends on POWER_SUPPLY - depends on AMD_NB + depends on AMD_NODE select ACPI_PLATFORM_PROFILE depends on TEE && AMDTEE depends on AMD_SFH_HID diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile index 7d6079b02589..6b26e48ce8ad 100644 --- a/drivers/platform/x86/amd/pmf/Makefile +++ b/drivers/platform/x86/amd/pmf/Makefile @@ -7,4 +7,4 @@ obj-$(CONFIG_AMD_PMF) += amd-pmf.o amd-pmf-objs := core.o acpi.o sps.o \ auto-mode.o cnqf.o \ - tee-if.o spc.o pmf-quirks.o + tee-if.o spc.o diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c index 1b9c7acf0ddf..dd5780a1d06e 100644 --- a/drivers/platform/x86/amd/pmf/acpi.c +++ b/drivers/platform/x86/amd/pmf/acpi.c @@ -321,17 +321,29 @@ int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req req, sizeof(*req)); } +static void apmf_event_handler_v2(acpi_handle handle, u32 event, void *data) +{ + struct amd_pmf_dev *pmf_dev = data; + int ret; + + guard(mutex)(&pmf_dev->cb_mutex); + + ret = apmf_get_sbios_requests_v2(pmf_dev, &pmf_dev->req); + if (ret) + dev_err(pmf_dev->dev, "Failed to get v2 SBIOS requests: %d\n", ret); +} + static void apmf_event_handler(acpi_handle handle, u32 event, void *data) { struct amd_pmf_dev *pmf_dev = data; struct apmf_sbios_req req; int ret; - mutex_lock(&pmf_dev->update_mutex); + guard(mutex)(&pmf_dev->update_mutex); ret = apmf_get_sbios_requests(pmf_dev, &req); if (ret) { dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret); - goto out; + return; } if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) { @@ -353,8 +365,6 @@ static void apmf_event_handler(acpi_handle handle, u32 event, void *data) if (pmf_dev->amt_enabled) amd_pmf_update_2_cql(pmf_dev, req.cql_event); } -out: - mutex_unlock(&pmf_dev->update_mutex); } static int apmf_if_verify_interface(struct amd_pmf_dev *pdev) @@ -430,6 +440,15 @@ int apmf_install_handler(struct amd_pmf_dev *pmf_dev) apmf_event_handler(ahandle, 0, pmf_dev); } + if (pmf_dev->smart_pc_enabled && pmf_dev->pmf_if_version == PMF_IF_V2) { + status = acpi_install_notify_handler(ahandle, ACPI_ALL_NOTIFY, + apmf_event_handler_v2, pmf_dev); + if (ACPI_FAILURE(status)) { + dev_err(pmf_dev->dev, "failed to install notify handler for custom BIOS inputs\n"); + return -ENODEV; + } + } + return 0; } @@ -480,6 +499,9 @@ void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev) if (is_apmf_func_supported(pmf_dev, APMF_FUNC_AUTO_MODE) && is_apmf_func_supported(pmf_dev, APMF_FUNC_SBIOS_REQUESTS)) acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY, apmf_event_handler); + + if (pmf_dev->smart_pc_enabled && pmf_dev->pmf_if_version == PMF_IF_V2) + acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY, apmf_event_handler_v2); } int apmf_acpi_init(struct amd_pmf_dev *pmf_dev) diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c index 02ff68be10d0..a184922bba8d 100644 --- a/drivers/platform/x86/amd/pmf/auto-mode.c +++ b/drivers/platform/x86/amd/pmf/auto-mode.c @@ -120,9 +120,9 @@ static void amd_pmf_set_automode(struct amd_pmf_dev *dev, int idx, amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pwr_ctrl->sppt_apu_only, NULL); amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pwr_ctrl->stt_min, NULL); amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, - pwr_ctrl->stt_skin_temp[STT_TEMP_APU], NULL); + fixp_q88_fromint(pwr_ctrl->stt_skin_temp[STT_TEMP_APU]), NULL); amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, - pwr_ctrl->stt_skin_temp[STT_TEMP_HS2], NULL); + fixp_q88_fromint(pwr_ctrl->stt_skin_temp[STT_TEMP_HS2]), NULL); if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX)) apmf_update_fan_idx(dev, config_store.mode_set[idx].fan_control.manual, diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c index bc8899e15c91..207a0b33d8d3 100644 --- a/drivers/platform/x86/amd/pmf/cnqf.c +++ b/drivers/platform/x86/amd/pmf/cnqf.c @@ -81,10 +81,10 @@ static int amd_pmf_set_cnqf(struct amd_pmf_dev *dev, int src, int idx, amd_pmf_send_cmd(dev, SET_SPPT, false, pc->sppt, NULL); amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pc->sppt_apu_only, NULL); amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pc->stt_min, NULL); - amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, pc->stt_skin_temp[STT_TEMP_APU], - NULL); - amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, pc->stt_skin_temp[STT_TEMP_HS2], - NULL); + amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, + fixp_q88_fromint(pc->stt_skin_temp[STT_TEMP_APU]), NULL); + amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, + fixp_q88_fromint(pc->stt_skin_temp[STT_TEMP_HS2]), NULL); if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX)) apmf_update_fan_idx(dev, diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index 06a97c533cb8..96821101ec77 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -8,13 +8,13 @@ * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> */ -#include <asm/amd_nb.h> #include <linux/debugfs.h> #include <linux/iopoll.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/power_supply.h> +#include <asm/amd_node.h> #include "pmf.h" /* PMF-SMU communication registers */ @@ -127,7 +127,8 @@ static void amd_pmf_get_metrics(struct work_struct *work) ktime_t time_elapsed_ms; int socket_power; - mutex_lock(&dev->update_mutex); + guard(mutex)(&dev->update_mutex); + /* Transfer table contents */ memset(dev->buf, 0, sizeof(dev->m_table)); amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL); @@ -149,7 +150,6 @@ static void amd_pmf_get_metrics(struct work_struct *work) dev->start_time = ktime_to_ms(ktime_get()); schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms)); - mutex_unlock(&dev->update_mutex); } static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset) @@ -176,12 +176,26 @@ static void __maybe_unused amd_pmf_dump_registers(struct amd_pmf_dev *dev) dev_dbg(dev->dev, "AMD_PMF_REGISTER_MESSAGE:%x\n", value); } +/** + * fixp_q88_fromint: Convert integer to Q8.8 + * @val: input value + * + * Converts an integer into binary fixed point format where 8 bits + * are used for integer and 8 bits are used for the decimal. + * + * Return: unsigned integer converted to Q8.8 format + */ +u32 fixp_q88_fromint(u32 val) +{ + return val << 8; +} + int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data) { int rc; u32 val; - mutex_lock(&dev->lock); + guard(mutex)(&dev->lock); /* Wait until we get a valid response */ rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE, @@ -189,7 +203,7 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX); if (rc) { dev_err(dev->dev, "failed to talk to SMU\n"); - goto out_unlock; + return rc; } /* Write zero to response register */ @@ -207,7 +221,7 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX); if (rc) { dev_err(dev->dev, "SMU response timed out\n"); - goto out_unlock; + return rc; } switch (val) { @@ -221,21 +235,19 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 case AMD_PMF_RESULT_CMD_REJECT_BUSY: dev_err(dev->dev, "SMU not ready. err: 0x%x\n", val); rc = -EBUSY; - goto out_unlock; + break; case AMD_PMF_RESULT_CMD_UNKNOWN: dev_err(dev->dev, "SMU cmd unknown. err: 0x%x\n", val); rc = -EINVAL; - goto out_unlock; + break; case AMD_PMF_RESULT_CMD_REJECT_PREREQ: case AMD_PMF_RESULT_FAILED: default: dev_err(dev->dev, "SMU cmd failed. err: 0x%x\n", val); rc = -EIO; - goto out_unlock; + break; } -out_unlock: - mutex_unlock(&dev->lock); amd_pmf_dump_registers(dev); return rc; } @@ -373,7 +385,6 @@ static void amd_pmf_deinit_features(struct amd_pmf_dev *dev) if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR) || is_apmf_func_supported(dev, APMF_FUNC_OS_POWER_SLIDER_UPDATE)) { power_supply_unreg_notifier(&dev->pwr_src_notifier); - amd_pmf_deinit_sps(dev); } if (dev->smart_pc_enabled) { @@ -455,8 +466,8 @@ static int amd_pmf_probe(struct platform_device *pdev) mutex_init(&dev->lock); mutex_init(&dev->update_mutex); + mutex_init(&dev->cb_mutex); - amd_pmf_quirks_init(dev); apmf_acpi_init(dev); platform_set_drvdata(pdev, dev); amd_pmf_dbgfs_register(dev); @@ -481,6 +492,7 @@ static void amd_pmf_remove(struct platform_device *pdev) amd_pmf_dbgfs_unregister(dev); mutex_destroy(&dev->lock); mutex_destroy(&dev->update_mutex); + mutex_destroy(&dev->cb_mutex); kfree(dev->buf); } diff --git a/drivers/platform/x86/amd/pmf/pmf-quirks.c b/drivers/platform/x86/amd/pmf/pmf-quirks.c deleted file mode 100644 index 7cde5733b9ca..000000000000 --- a/drivers/platform/x86/amd/pmf/pmf-quirks.c +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * AMD Platform Management Framework Driver Quirks - * - * Copyright (c) 2024, Advanced Micro Devices, Inc. - * All Rights Reserved. - * - * Author: Mario Limonciello <mario.limonciello@amd.com> - */ - -#include <linux/dmi.h> - -#include "pmf.h" - -struct quirk_entry { - u32 supported_func; -}; - -static struct quirk_entry quirk_no_sps_bug = { - .supported_func = 0x4003, -}; - -static const struct dmi_system_id fwbug_list[] = { - { - .ident = "ROG Zephyrus G14", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "GA403U"), - }, - .driver_data = &quirk_no_sps_bug, - }, - { - .ident = "ROG Ally X", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "RC72LA"), - }, - .driver_data = &quirk_no_sps_bug, - }, - { - .ident = "ASUS TUF Gaming A14", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "FA401W"), - }, - .driver_data = &quirk_no_sps_bug, - }, - {} -}; - -void amd_pmf_quirks_init(struct amd_pmf_dev *dev) -{ - const struct dmi_system_id *dmi_id; - struct quirk_entry *quirks; - - dmi_id = dmi_first_match(fwbug_list); - if (!dmi_id) - return; - - quirks = dmi_id->driver_data; - if (quirks->supported_func) { - dev->supported_func = quirks->supported_func; - pr_info("Using supported funcs quirk to avoid %s platform firmware bug\n", - dmi_id->ident); - } -} diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index a79808fda1d8..45b60238d527 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -106,9 +106,12 @@ struct cookie_header { #define PMF_TA_IF_VERSION_MAJOR 1 #define TA_PMF_ACTION_MAX 32 #define TA_PMF_UNDO_MAX 8 -#define TA_OUTPUT_RESERVED_MEM 906 +#define TA_OUTPUT_RESERVED_MEM 922 #define MAX_OPERATION_PARAMS 4 +#define TA_ERROR_CRYPTO_INVALID_PARAM 0x20002 +#define TA_ERROR_CRYPTO_BIN_TOO_LARGE 0x2000d + #define PMF_IF_V1 1 #define PMF_IF_V2 2 @@ -338,7 +341,7 @@ struct amd_pmf_dev { struct mutex lock; /* protects the PMF interface */ u32 supported_func; enum platform_profile_option current_profile; - struct platform_profile_handler pprof; + struct device *ppdev; /* platform profile class device */ struct dentry *dbgfs_dir; int hb_interval; /* SBIOS heartbeat interval */ struct delayed_work heart_beat; @@ -370,6 +373,8 @@ struct amd_pmf_dev { struct input_dev *pmf_idev; size_t mtable_size; struct resource *res; + struct apmf_sbios_req_v2 req; /* To get custom bios pending request */ + struct mutex cb_mutex; }; struct apmf_sps_prop_granular_v2 { @@ -616,6 +621,30 @@ enum ta_slider { TA_MAX, }; +enum apmf_smartpc_custom_bios_inputs { + APMF_SMARTPC_CUSTOM_BIOS_INPUT1, + APMF_SMARTPC_CUSTOM_BIOS_INPUT2, +}; + +enum apmf_preq_smartpc { + NOTIFY_CUSTOM_BIOS_INPUT1 = 5, + NOTIFY_CUSTOM_BIOS_INPUT2, +}; + +enum platform_type { + PTYPE_UNKNOWN = 0, + LID_CLOSE, + CLAMSHELL, + FLAT, + TENT, + STAND, + TABLET, + BOOK, + PRESENTATION, + PULL_FWD, + PTYPE_INVALID = 0xf, +}; + /* Command ids for TA communication */ enum ta_pmf_command { TA_PMF_COMMAND_POLICY_BUILDER_INITIALIZE, @@ -657,7 +686,8 @@ struct ta_pmf_condition_info { u32 power_slider; u32 lid_state; bool user_present; - u32 rsvd1[2]; + u32 bios_input1; + u32 bios_input2; u32 monitor_count; u32 rsvd2[2]; u32 bat_design; @@ -667,7 +697,9 @@ struct ta_pmf_condition_info { u32 device_state; u32 socket_power; u32 skin_temperature; - u32 rsvd3[5]; + u32 rsvd3[2]; + u32 platform_type; + u32 rsvd3_1[2]; u32 ambient_light; u32 length; u32 avg_c0residency; @@ -745,13 +777,13 @@ int apmf_install_handler(struct amd_pmf_dev *pmf_dev); int apmf_os_power_slider_update(struct amd_pmf_dev *dev, u8 flag); int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer); int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag); +u32 fixp_q88_fromint(u32 val); /* SPS Layer */ int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf); void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx, struct amd_pmf_static_slider_granular *table); int amd_pmf_init_sps(struct amd_pmf_dev *dev); -void amd_pmf_deinit_sps(struct amd_pmf_dev *dev); int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev, struct apmf_static_slider_granular_output *output); bool is_pprof_balanced(struct amd_pmf_dev *pmf); @@ -797,7 +829,4 @@ int amd_pmf_smartpc_apply_bios_output(struct amd_pmf_dev *dev, u32 val, u32 preq void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in); void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in); -/* Quirk infrastructure */ -void amd_pmf_quirks_init(struct amd_pmf_dev *dev); - #endif /* PMF_H */ diff --git a/drivers/platform/x86/amd/pmf/spc.c b/drivers/platform/x86/amd/pmf/spc.c index 06226eb0eab3..1d90f9382024 100644 --- a/drivers/platform/x86/amd/pmf/spc.c +++ b/drivers/platform/x86/amd/pmf/spc.c @@ -16,6 +16,46 @@ #include "pmf.h" #ifdef CONFIG_AMD_PMF_DEBUG +static const char *platform_type_as_str(u16 platform_type) +{ + switch (platform_type) { + case CLAMSHELL: + return "CLAMSHELL"; + case FLAT: + return "FLAT"; + case TENT: + return "TENT"; + case STAND: + return "STAND"; + case TABLET: + return "TABLET"; + case BOOK: + return "BOOK"; + case PRESENTATION: + return "PRESENTATION"; + case PULL_FWD: + return "PULL_FWD"; + default: + return "UNKNOWN"; + } +} + +static const char *laptop_placement_as_str(u16 device_state) +{ + switch (device_state) { + case ON_TABLE: + return "ON_TABLE"; + case ON_LAP_MOTION: + return "ON_LAP_MOTION"; + case IN_BAG: + return "IN_BAG"; + case OUT_OF_BAG: + return "OUT_OF_BAG"; + default: + return "UNKNOWN"; + } +} + static const char *ta_slider_as_str(unsigned int state) { switch (state) { @@ -47,12 +87,38 @@ void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table * dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open"); dev_dbg(dev->dev, "User Presence: %s\n", in->ev_info.user_present ? "Present" : "Away"); dev_dbg(dev->dev, "Ambient Light: %d\n", in->ev_info.ambient_light); + dev_dbg(dev->dev, "Platform type: %s\n", platform_type_as_str(in->ev_info.platform_type)); + dev_dbg(dev->dev, "Laptop placement: %s\n", + laptop_placement_as_str(in->ev_info.device_state)); + dev_dbg(dev->dev, "Custom BIOS input1: %u\n", in->ev_info.bios_input1); + dev_dbg(dev->dev, "Custom BIOS input2: %u\n", in->ev_info.bios_input2); dev_dbg(dev->dev, "==== TA inputs END ====\n"); } #else void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {} #endif +static void amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev *pdev, + struct ta_pmf_enact_table *in) +{ + if (!pdev->req.pending_req) + return; + + switch (pdev->req.pending_req) { + case BIT(NOTIFY_CUSTOM_BIOS_INPUT1): + in->ev_info.bios_input1 = pdev->req.custom_policy[APMF_SMARTPC_CUSTOM_BIOS_INPUT1]; + break; + case BIT(NOTIFY_CUSTOM_BIOS_INPUT2): + in->ev_info.bios_input2 = pdev->req.custom_policy[APMF_SMARTPC_CUSTOM_BIOS_INPUT2]; + break; + default: + dev_dbg(pdev->dev, "Invalid preq for BIOS input: 0x%x\n", pdev->req.pending_req); + } + + /* Clear pending requests after handling */ + memset(&pdev->req, 0, sizeof(pdev->req)); +} + static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in) { u16 max, avg = 0; @@ -153,12 +219,14 @@ static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_ switch (dev->current_profile) { case PLATFORM_PROFILE_PERFORMANCE: + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: val = TA_BEST_PERFORMANCE; break; case PLATFORM_PROFILE_BALANCED: val = TA_BETTER_PERFORMANCE; break; case PLATFORM_PROFILE_LOW_POWER: + case PLATFORM_PROFILE_QUIET: val = TA_BEST_BATTERY; break; default: @@ -190,6 +258,14 @@ static void amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact } else { dev_dbg(dev->dev, "HPD is not enabled/detected\n"); } + + /* Get SRA (Secondary Accelerometer) data */ + if (!amd_get_sfh_info(&sfh_info, MT_SRA)) { + in->ev_info.platform_type = sfh_info.platform_type; + in->ev_info.device_state = sfh_info.laptop_placement; + } else { + dev_dbg(dev->dev, "SRA is not enabled/detected\n"); + } } void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) @@ -201,4 +277,5 @@ void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_tab amd_pmf_get_battery_info(dev, in); amd_pmf_get_slider_info(dev, in); amd_pmf_get_sensor_info(dev, in); + amd_pmf_get_custom_bios_inputs(dev, in); } diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c index 92f7fb22277d..49e14ca94a9e 100644 --- a/drivers/platform/x86/amd/pmf/sps.c +++ b/drivers/platform/x86/amd/pmf/sps.c @@ -198,9 +198,11 @@ static void amd_pmf_update_slider_v2(struct amd_pmf_dev *dev, int idx) amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, apts_config_store.val[idx].stt_min_limit, NULL); amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, - apts_config_store.val[idx].stt_skin_temp_limit_apu, NULL); + fixp_q88_fromint(apts_config_store.val[idx].stt_skin_temp_limit_apu), + NULL); amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, - apts_config_store.val[idx].stt_skin_temp_limit_hs2, NULL); + fixp_q88_fromint(apts_config_store.val[idx].stt_skin_temp_limit_hs2), + NULL); } void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx, @@ -217,9 +219,11 @@ void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx, amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, config_store.prop[src][idx].stt_min, NULL); amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, - config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU], NULL); + fixp_q88_fromint(config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU]), + NULL); amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, - config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2], NULL); + fixp_q88_fromint(config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2]), + NULL); } else if (op == SLIDER_OP_GET) { amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE, &table->prop[src][idx].spl); amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE, &table->prop[src][idx].fppt); @@ -282,10 +286,10 @@ bool is_pprof_balanced(struct amd_pmf_dev *pmf) return (pmf->current_profile == PLATFORM_PROFILE_BALANCED) ? true : false; } -static int amd_pmf_profile_get(struct platform_profile_handler *pprof, +static int amd_pmf_profile_get(struct device *dev, enum platform_profile_option *profile) { - struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof); + struct amd_pmf_dev *pmf = dev_get_drvdata(dev); *profile = pmf->current_profile; return 0; @@ -297,12 +301,14 @@ int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf) switch (pmf->current_profile) { case PLATFORM_PROFILE_PERFORMANCE: + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: mode = POWER_MODE_PERFORMANCE; break; case PLATFORM_PROFILE_BALANCED: mode = POWER_MODE_BALANCED_POWER; break; case PLATFORM_PROFILE_LOW_POWER: + case PLATFORM_PROFILE_QUIET: mode = POWER_MODE_POWER_SAVER; break; default: @@ -363,10 +369,10 @@ int amd_pmf_power_slider_update_event(struct amd_pmf_dev *dev) return 0; } -static int amd_pmf_profile_set(struct platform_profile_handler *pprof, +static int amd_pmf_profile_set(struct device *dev, enum platform_profile_option profile) { - struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof); + struct amd_pmf_dev *pmf = dev_get_drvdata(dev); int ret = 0; pmf->current_profile = profile; @@ -387,10 +393,32 @@ static int amd_pmf_profile_set(struct platform_profile_handler *pprof, return 0; } -int amd_pmf_init_sps(struct amd_pmf_dev *dev) +static int amd_pmf_hidden_choices(void *drvdata, unsigned long *choices) { - int err; + set_bit(PLATFORM_PROFILE_QUIET, choices); + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices); + + return 0; +} + +static int amd_pmf_profile_probe(void *drvdata, unsigned long *choices) +{ + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); + set_bit(PLATFORM_PROFILE_BALANCED, choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + + return 0; +} +static const struct platform_profile_ops amd_pmf_profile_ops = { + .probe = amd_pmf_profile_probe, + .hidden_choices = amd_pmf_hidden_choices, + .profile_get = amd_pmf_profile_get, + .profile_set = amd_pmf_profile_set, +}; + +int amd_pmf_init_sps(struct amd_pmf_dev *dev) +{ dev->current_profile = PLATFORM_PROFILE_BALANCED; if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) { @@ -405,24 +433,12 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev) amd_pmf_set_sps_power_limits(dev); } - dev->pprof.profile_get = amd_pmf_profile_get; - dev->pprof.profile_set = amd_pmf_profile_set; - - /* Setup supported modes */ - set_bit(PLATFORM_PROFILE_LOW_POWER, dev->pprof.choices); - set_bit(PLATFORM_PROFILE_BALANCED, dev->pprof.choices); - set_bit(PLATFORM_PROFILE_PERFORMANCE, dev->pprof.choices); - /* Create platform_profile structure and register */ - err = platform_profile_register(&dev->pprof); - if (err) - dev_err(dev->dev, "Failed to register SPS support, this is most likely an SBIOS bug: %d\n", - err); - - return err; -} + dev->ppdev = devm_platform_profile_register(dev->dev, "amd-pmf", dev, + &amd_pmf_profile_ops); + if (IS_ERR(dev->ppdev)) + dev_err(dev->dev, "Failed to register SPS support, this is most likely an SBIOS bug: %ld\n", + PTR_ERR(dev->ppdev)); -void amd_pmf_deinit_sps(struct amd_pmf_dev *dev) -{ - platform_profile_remove(); + return PTR_ERR_OR_ZERO(dev->ppdev); } diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index 8c88769ea1d8..14b99d8b63d2 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -27,8 +27,11 @@ module_param(pb_side_load, bool, 0444); MODULE_PARM_DESC(pb_side_load, "Sideload policy binaries debug policy failures"); #endif -static const uuid_t amd_pmf_ta_uuid = UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d, - 0xb1, 0x2d, 0xc5, 0x29, 0xb1, 0x3d, 0x85, 0x43); +static const uuid_t amd_pmf_ta_uuid[] = { UUID_INIT(0xd9b39bf2, 0x66bd, 0x4154, 0xaf, 0xb8, 0x8a, + 0xcc, 0x2b, 0x2b, 0x60, 0xd6), + UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d, 0xb1, 0x2d, 0xc5, + 0x29, 0xb1, 0x3d, 0x85, 0x43), + }; static const char *amd_pmf_uevent_as_str(unsigned int state) { @@ -120,7 +123,8 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_ case PMF_POLICY_STT_SKINTEMP_APU: if (dev->prev_data->stt_skintemp_apu != val) { - amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, val, NULL); + amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, + fixp_q88_fromint(val), NULL); dev_dbg(dev->dev, "update STT_SKINTEMP_APU: %u\n", val); dev->prev_data->stt_skintemp_apu = val; } @@ -128,7 +132,8 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_ case PMF_POLICY_STT_SKINTEMP_HS2: if (dev->prev_data->stt_skintemp_hs2 != val) { - amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, val, NULL); + amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, + fixp_q88_fromint(val), NULL); dev_dbg(dev->dev, "update STT_SKINTEMP_HS2: %u\n", val); dev->prev_data->stt_skintemp_hs2 = val; } @@ -321,9 +326,9 @@ static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev) */ schedule_delayed_work(&dev->pb_work, msecs_to_jiffies(pb_actions_ms * 3)); } else { - dev_err(dev->dev, "ta invoke cmd init failed err: %x\n", res); + dev_dbg(dev->dev, "ta invoke cmd init failed err: %x\n", res); dev->smart_pc_enabled = false; - return -EIO; + return res; } return 0; @@ -390,12 +395,12 @@ static int amd_pmf_amdtee_ta_match(struct tee_ioctl_version_data *ver, const voi return ver->impl_id == TEE_IMPL_ID_AMDTEE; } -static int amd_pmf_ta_open_session(struct tee_context *ctx, u32 *id) +static int amd_pmf_ta_open_session(struct tee_context *ctx, u32 *id, const uuid_t *uuid) { struct tee_ioctl_open_session_arg sess_arg = {}; int rc; - export_uuid(sess_arg.uuid, &amd_pmf_ta_uuid); + export_uuid(sess_arg.uuid, uuid); sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; sess_arg.num_params = 0; @@ -434,7 +439,7 @@ static int amd_pmf_register_input_device(struct amd_pmf_dev *dev) return 0; } -static int amd_pmf_tee_init(struct amd_pmf_dev *dev) +static int amd_pmf_tee_init(struct amd_pmf_dev *dev, const uuid_t *uuid) { u32 size; int ret; @@ -445,7 +450,7 @@ static int amd_pmf_tee_init(struct amd_pmf_dev *dev) return PTR_ERR(dev->tee_ctx); } - ret = amd_pmf_ta_open_session(dev->tee_ctx, &dev->session_id); + ret = amd_pmf_ta_open_session(dev->tee_ctx, &dev->session_id, uuid); if (ret) { dev_err(dev->dev, "Failed to open TA session (%d)\n", ret); ret = -EINVAL; @@ -489,7 +494,8 @@ static void amd_pmf_tee_deinit(struct amd_pmf_dev *dev) int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev) { - int ret; + bool status; + int ret, i; ret = apmf_check_smart_pc(dev); if (ret) { @@ -502,26 +508,22 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev) return -ENODEV; } - ret = amd_pmf_tee_init(dev); - if (ret) - return ret; - INIT_DELAYED_WORK(&dev->pb_work, amd_pmf_invoke_cmd); ret = amd_pmf_set_dram_addr(dev, true); if (ret) - goto error; + goto err_cancel_work; dev->policy_base = devm_ioremap_resource(dev->dev, dev->res); if (IS_ERR(dev->policy_base)) { ret = PTR_ERR(dev->policy_base); - goto error; + goto err_free_dram_buf; } dev->policy_buf = kzalloc(dev->policy_sz, GFP_KERNEL); if (!dev->policy_buf) { ret = -ENOMEM; - goto error; + goto err_free_dram_buf; } memcpy_fromio(dev->policy_buf, dev->policy_base, dev->policy_sz); @@ -531,24 +533,60 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev) dev->prev_data = kzalloc(sizeof(*dev->prev_data), GFP_KERNEL); if (!dev->prev_data) { ret = -ENOMEM; - goto error; + goto err_free_policy; } - ret = amd_pmf_start_policy_engine(dev); - if (ret) - goto error; + for (i = 0; i < ARRAY_SIZE(amd_pmf_ta_uuid); i++) { + ret = amd_pmf_tee_init(dev, &amd_pmf_ta_uuid[i]); + if (ret) + goto err_free_prev_data; + + ret = amd_pmf_start_policy_engine(dev); + switch (ret) { + case TA_PMF_TYPE_SUCCESS: + status = true; + break; + case TA_ERROR_CRYPTO_INVALID_PARAM: + case TA_ERROR_CRYPTO_BIN_TOO_LARGE: + amd_pmf_tee_deinit(dev); + status = false; + break; + default: + ret = -EINVAL; + amd_pmf_tee_deinit(dev); + goto err_free_prev_data; + } + + if (status) + break; + } + + if (!status && !pb_side_load) { + ret = -EINVAL; + goto err_free_prev_data; + } if (pb_side_load) amd_pmf_open_pb(dev, dev->dbgfs_dir); ret = amd_pmf_register_input_device(dev); if (ret) - goto error; + goto err_pmf_remove_pb; return 0; -error: - amd_pmf_deinit_smart_pc(dev); +err_pmf_remove_pb: + if (pb_side_load && dev->esbin) + amd_pmf_remove_pb(dev); + amd_pmf_tee_deinit(dev); +err_free_prev_data: + kfree(dev->prev_data); +err_free_policy: + kfree(dev->policy_buf); +err_free_dram_buf: + kfree(dev->buf); +err_cancel_work: + cancel_delayed_work_sync(&dev->pb_work); return ret; } diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index d460dd194f19..a0a411b4f2d6 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -426,11 +426,14 @@ static int asus_pega_lucid_set(struct asus_laptop *asus, int unit, bool enable) static int pega_acc_axis(struct asus_laptop *asus, int curr, char *method) { + unsigned long long val = (unsigned long long)curr; + acpi_status status; int i, delta; - unsigned long long val; - for (i = 0; i < PEGA_ACC_RETRIES; i++) { - acpi_evaluate_integer(asus->handle, method, NULL, &val); + for (i = 0; i < PEGA_ACC_RETRIES; i++) { + status = acpi_evaluate_integer(asus->handle, method, NULL, &val); + if (ACPI_FAILURE(status)) + continue; /* The output is noisy. From reading the ASL * dissassembly, timeout errors are returned with 1's * in the high word, and the lack of locking around diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index a5933980ade3..3f8b2a324efd 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -50,7 +50,8 @@ MODULE_PARM_DESC(tablet_mode_sw, "Tablet mode detect: -1:auto 0:disable 1:kbd-do static struct quirk_entry *quirks; static bool atkbd_reports_vol_keys; -static bool asus_i8042_filter(unsigned char data, unsigned char str, struct serio *port) +static bool asus_i8042_filter(unsigned char data, unsigned char str, struct serio *port, + void *context) { static bool extended_e0; static bool extended_e1; diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 8bd187e8b47f..38ef778e8c19 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -313,7 +313,7 @@ struct asus_wmi { bool mid_fan_curve_available; struct fan_curve_data custom_fan_curves[3]; - struct platform_profile_handler platform_profile_handler; + struct device *ppdev; bool platform_profile_support; // The RSOC controls the maximum charging percentage. @@ -3782,7 +3782,7 @@ static ssize_t throttle_thermal_policy_store(struct device *dev, * Ensure that platform_profile updates userspace with the change to ensure * that platform_profile and throttle_thermal_policy_mode are in sync. */ - platform_profile_notify(); + platform_profile_notify(asus->ppdev); return count; } @@ -3793,13 +3793,13 @@ static ssize_t throttle_thermal_policy_store(struct device *dev, static DEVICE_ATTR_RW(throttle_thermal_policy); /* Platform profile ***********************************************************/ -static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof, +static int asus_wmi_platform_profile_get(struct device *dev, enum platform_profile_option *profile) { struct asus_wmi *asus; int tp; - asus = container_of(pprof, struct asus_wmi, platform_profile_handler); + asus = dev_get_drvdata(dev); tp = asus->throttle_thermal_policy_mode; switch (tp) { @@ -3819,13 +3819,13 @@ static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof, return 0; } -static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof, +static int asus_wmi_platform_profile_set(struct device *dev, enum platform_profile_option profile) { struct asus_wmi *asus; int tp; - asus = container_of(pprof, struct asus_wmi, platform_profile_handler); + asus = dev_get_drvdata(dev); switch (profile) { case PLATFORM_PROFILE_PERFORMANCE: @@ -3845,6 +3845,21 @@ static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof, return throttle_thermal_policy_write(asus); } +static int asus_wmi_platform_profile_probe(void *drvdata, unsigned long *choices) +{ + set_bit(PLATFORM_PROFILE_QUIET, choices); + set_bit(PLATFORM_PROFILE_BALANCED, choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + + return 0; +} + +static const struct platform_profile_ops asus_wmi_platform_profile_ops = { + .probe = asus_wmi_platform_profile_probe, + .profile_get = asus_wmi_platform_profile_get, + .profile_set = asus_wmi_platform_profile_set, +}; + static int platform_profile_setup(struct asus_wmi *asus) { struct device *dev = &asus->platform_device->dev; @@ -3869,22 +3884,11 @@ static int platform_profile_setup(struct asus_wmi *asus) dev_info(dev, "Using throttle_thermal_policy for platform_profile support\n"); - asus->platform_profile_handler.profile_get = asus_wmi_platform_profile_get; - asus->platform_profile_handler.profile_set = asus_wmi_platform_profile_set; - - set_bit(PLATFORM_PROFILE_QUIET, asus->platform_profile_handler.choices); - set_bit(PLATFORM_PROFILE_BALANCED, - asus->platform_profile_handler.choices); - set_bit(PLATFORM_PROFILE_PERFORMANCE, - asus->platform_profile_handler.choices); - - err = platform_profile_register(&asus->platform_profile_handler); - if (err == -EEXIST) { - pr_warn("%s, a platform_profile handler is already registered\n", __func__); - return 0; - } else if (err) { - pr_err("%s, failed at platform_profile_register: %d\n", __func__, err); - return err; + asus->ppdev = devm_platform_profile_register(dev, "asus-wmi", asus, + &asus_wmi_platform_profile_ops); + if (IS_ERR(asus->ppdev)) { + dev_err(dev, "Failed to register a platform_profile class device\n"); + return PTR_ERR(asus->ppdev); } asus->platform_profile_support = true; @@ -4815,7 +4819,7 @@ static int asus_wmi_add(struct platform_device *pdev) } if (asus->driver->i8042_filter) { - err = i8042_install_filter(asus->driver->i8042_filter); + err = i8042_install_filter(asus->driver->i8042_filter, NULL); if (err) pr_warn("Unable to install key filter - %d\n", err); } @@ -4842,8 +4846,6 @@ fail_input: fail_sysfs: fail_custom_fan_curve: fail_platform_profile_setup: - if (asus->platform_profile_support) - platform_profile_remove(); fail_fan_boost_mode: fail_platform: kfree(asus); @@ -4869,9 +4871,6 @@ static void asus_wmi_remove(struct platform_device *device) throttle_thermal_policy_set_default(asus); asus_wmi_battery_exit(asus); - if (asus->platform_profile_support) - platform_profile_remove(); - kfree(asus); } diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index d02f15fd3482..018dfde4025e 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h @@ -73,8 +73,7 @@ struct asus_wmi_driver { void (*key_filter) (struct asus_wmi_driver *driver, int *code, unsigned int *value, bool *autorelease); /* Optional standard i8042 filter */ - bool (*i8042_filter)(unsigned char data, unsigned char str, - struct serio *serio); + i8042_filter_t i8042_filter; int (*probe) (struct platform_device *device); void (*detect_quirks) (struct asus_wmi_driver *driver); diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig index 2dddafb3f7fa..d09060aedd3f 100644 --- a/drivers/platform/x86/dell/Kconfig +++ b/drivers/platform/x86/dell/Kconfig @@ -152,6 +152,7 @@ config DELL_SMBIOS_SMM config DELL_SMO8800 tristate "Dell Latitude freefall driver (ACPI SMO88XX)" default m + depends on I2C depends on ACPI || COMPILE_TEST help Say Y here if you want to support SMO88XX freefall devices diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile index 79d60f1bf4c1..bb3cbd470a46 100644 --- a/drivers/platform/x86/dell/Makefile +++ b/drivers/platform/x86/dell/Makefile @@ -15,6 +15,7 @@ dell-smbios-objs := dell-smbios-base.o dell-smbios-$(CONFIG_DELL_SMBIOS_WMI) += dell-smbios-wmi.o dell-smbios-$(CONFIG_DELL_SMBIOS_SMM) += dell-smbios-smm.o obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o +obj-$(CONFIG_DELL_SMO8800) += dell-lis3lv02d.o obj-$(CONFIG_DELL_UART_BACKLIGHT) += dell-uart-backlight.o obj-$(CONFIG_DELL_WMI) += dell-wmi.o dell-wmi-objs := dell-wmi-base.o diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c index 341d01d3e3e4..1426ea8e4f19 100644 --- a/drivers/platform/x86/dell/alienware-wmi.c +++ b/drivers/platform/x86/dell/alienware-wmi.c @@ -216,6 +216,15 @@ static int __init dmi_matched(const struct dmi_system_id *dmi) static const struct dmi_system_id alienware_quirks[] __initconst = { { .callback = dmi_matched, + .ident = "Alienware Area-51m R2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware Area-51m R2"), + }, + .driver_data = &quirk_x_series, + }, + { + .callback = dmi_matched, .ident = "Alienware ASM100", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), @@ -243,12 +252,21 @@ static const struct dmi_system_id alienware_quirks[] __initconst = { }, { .callback = dmi_matched, + .ident = "Alienware m16 R1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R1"), + }, + .driver_data = &quirk_g_series, + }, + { + .callback = dmi_matched, .ident = "Alienware m16 R1 AMD", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R1 AMD"), }, - .driver_data = &quirk_x_series, + .driver_data = &quirk_g_series, }, { .callback = dmi_matched, @@ -261,6 +279,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = { }, { .callback = dmi_matched, + .ident = "Alienware m16 R2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R2"), + }, + .driver_data = &quirk_x_series, + }, + { + .callback = dmi_matched, .ident = "Alienware m18 R2", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), @@ -279,6 +306,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = { }, { .callback = dmi_matched, + .ident = "Alienware x15 R2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R2"), + }, + .driver_data = &quirk_x_series, + }, + { + .callback = dmi_matched, .ident = "Alienware x17 R2", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), @@ -342,6 +378,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = { }, { .callback = dmi_matched, + .ident = "Dell Inc. G16 7630", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell G16 7630"), + }, + .driver_data = &quirk_g_series, + }, + { + .callback = dmi_matched, .ident = "Dell Inc. G3 3500", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), @@ -369,6 +414,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = { }, { .callback = dmi_matched, + .ident = "Dell Inc. G5 5505", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "G5 5505"), + }, + .driver_data = &quirk_g_series, + }, + { + .callback = dmi_matched, .ident = "Dell Inc. Inspiron 5675", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), @@ -385,12 +439,6 @@ struct color_platform { u8 red; } __packed; -struct platform_zone { - u8 location; - struct device_attribute *attr; - struct color_platform colors; -}; - struct wmax_brightness_args { u32 led_mask; u32 percentage; @@ -420,22 +468,9 @@ struct wmax_u32_args { }; static struct platform_device *platform_device; -static struct device_attribute *zone_dev_attrs; -static struct attribute **zone_attrs; -static struct platform_zone *zone_data; -static struct platform_profile_handler pp_handler; +static struct color_platform colors[4]; static enum wmax_thermal_mode supported_thermal_profiles[PLATFORM_PROFILE_LAST]; -static struct platform_driver platform_driver = { - .driver = { - .name = "alienware-wmi", - } -}; - -static struct attribute_group zone_attribute_group = { - .name = "rgb_zones", -}; - static u8 interface; static u8 lighting_control_state; static u8 global_brightness; @@ -443,7 +478,7 @@ static u8 global_brightness; /* * Helpers used for zone control */ -static int parse_rgb(const char *buf, struct platform_zone *zone) +static int parse_rgb(const char *buf, struct color_platform *colors) { long unsigned int rgb; int ret; @@ -463,28 +498,14 @@ static int parse_rgb(const char *buf, struct platform_zone *zone) repackager.package = rgb & 0x0f0f0f0f; pr_debug("alienware-wmi: r: %d g:%d b: %d\n", repackager.cp.red, repackager.cp.green, repackager.cp.blue); - zone->colors = repackager.cp; + *colors = repackager.cp; return 0; } -static struct platform_zone *match_zone(struct device_attribute *attr) -{ - u8 zone; - - for (zone = 0; zone < quirks->num_zones; zone++) { - if ((struct device_attribute *)zone_data[zone].attr == attr) { - pr_debug("alienware-wmi: matched zone location: %d\n", - zone_data[zone].location); - return &zone_data[zone]; - } - } - return NULL; -} - /* * Individual RGB zone control */ -static int alienware_update_led(struct platform_zone *zone) +static int alienware_update_led(u8 location) { int method_id; acpi_status status; @@ -493,8 +514,8 @@ static int alienware_update_led(struct platform_zone *zone) struct legacy_led_args legacy_args; struct wmax_led_args wmax_basic_args; if (interface == WMAX) { - wmax_basic_args.led_mask = 1 << zone->location; - wmax_basic_args.colors = zone->colors; + wmax_basic_args.led_mask = 1 << location; + wmax_basic_args.colors = colors[location]; wmax_basic_args.state = lighting_control_state; guid = WMAX_CONTROL_GUID; method_id = WMAX_METHOD_ZONE_CONTROL; @@ -502,7 +523,7 @@ static int alienware_update_led(struct platform_zone *zone) input.length = sizeof(wmax_basic_args); input.pointer = &wmax_basic_args; } else { - legacy_args.colors = zone->colors; + legacy_args.colors = colors[location]; legacy_args.brightness = global_brightness; legacy_args.state = 0; if (lighting_control_state == LEGACY_BOOTING || @@ -511,7 +532,7 @@ static int alienware_update_led(struct platform_zone *zone) legacy_args.state = lighting_control_state; } else guid = LEGACY_CONTROL_GUID; - method_id = zone->location + 1; + method_id = location + 1; input.length = sizeof(legacy_args); input.pointer = &legacy_args; @@ -525,35 +546,153 @@ static int alienware_update_led(struct platform_zone *zone) } static ssize_t zone_show(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf, u8 location) { - struct platform_zone *target_zone; - target_zone = match_zone(attr); - if (target_zone == NULL) - return sprintf(buf, "red: -1, green: -1, blue: -1\n"); return sprintf(buf, "red: %d, green: %d, blue: %d\n", - target_zone->colors.red, - target_zone->colors.green, target_zone->colors.blue); + colors[location].red, colors[location].green, + colors[location].blue); } -static ssize_t zone_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t zone_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count, u8 location) { - struct platform_zone *target_zone; int ret; - target_zone = match_zone(attr); - if (target_zone == NULL) { - pr_err("alienware-wmi: invalid target zone\n"); - return 1; - } - ret = parse_rgb(buf, target_zone); + + ret = parse_rgb(buf, &colors[location]); if (ret) return ret; - ret = alienware_update_led(target_zone); + + ret = alienware_update_led(location); + return ret ? ret : count; } +static ssize_t zone00_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return zone_show(dev, attr, buf, 0); +} + +static ssize_t zone00_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return zone_store(dev, attr, buf, count, 0); +} + +static DEVICE_ATTR_RW(zone00); + +static ssize_t zone01_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return zone_show(dev, attr, buf, 1); +} + +static ssize_t zone01_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return zone_store(dev, attr, buf, count, 1); +} + +static DEVICE_ATTR_RW(zone01); + +static ssize_t zone02_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return zone_show(dev, attr, buf, 2); +} + +static ssize_t zone02_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return zone_store(dev, attr, buf, count, 2); +} + +static DEVICE_ATTR_RW(zone02); + +static ssize_t zone03_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return zone_show(dev, attr, buf, 3); +} + +static ssize_t zone03_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return zone_store(dev, attr, buf, count, 3); +} + +static DEVICE_ATTR_RW(zone03); + +/* + * Lighting control state device attribute (Global) + */ +static ssize_t lighting_control_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (lighting_control_state == LEGACY_BOOTING) + return sysfs_emit(buf, "[booting] running suspend\n"); + else if (lighting_control_state == LEGACY_SUSPEND) + return sysfs_emit(buf, "booting running [suspend]\n"); + + return sysfs_emit(buf, "booting [running] suspend\n"); +} + +static ssize_t lighting_control_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u8 val; + + if (strcmp(buf, "booting\n") == 0) + val = LEGACY_BOOTING; + else if (strcmp(buf, "suspend\n") == 0) + val = LEGACY_SUSPEND; + else if (interface == LEGACY) + val = LEGACY_RUNNING; + else + val = WMAX_RUNNING; + + lighting_control_state = val; + pr_debug("alienware-wmi: updated control state to %d\n", + lighting_control_state); + + return count; +} + +static DEVICE_ATTR_RW(lighting_control_state); + +static umode_t zone_attr_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + if (n < quirks->num_zones + 1) + return attr->mode; + + return 0; +} + +static bool zone_group_visible(struct kobject *kobj) +{ + return quirks->num_zones > 0; +} +DEFINE_SYSFS_GROUP_VISIBLE(zone); + +static struct attribute *zone_attrs[] = { + &dev_attr_lighting_control_state.attr, + &dev_attr_zone00.attr, + &dev_attr_zone01.attr, + &dev_attr_zone02.attr, + &dev_attr_zone03.attr, + NULL +}; + +static struct attribute_group zone_attribute_group = { + .name = "rgb_zones", + .is_visible = SYSFS_GROUP_VISIBLE(zone), + .attrs = zone_attrs, +}; + /* * LED Brightness (Global) */ @@ -582,7 +721,7 @@ static void global_led_set(struct led_classdev *led_cdev, if (interface == WMAX) ret = wmax_brightness(brightness); else - ret = alienware_update_led(&zone_data[0]); + ret = alienware_update_led(0); if (ret) pr_err("LED brightness update failed\n"); } @@ -598,46 +737,8 @@ static struct led_classdev global_led = { .name = "alienware::global_brightness", }; -/* - * Lighting control state device attribute (Global) - */ -static ssize_t show_control_state(struct device *dev, - struct device_attribute *attr, char *buf) -{ - if (lighting_control_state == LEGACY_BOOTING) - return sysfs_emit(buf, "[booting] running suspend\n"); - else if (lighting_control_state == LEGACY_SUSPEND) - return sysfs_emit(buf, "booting running [suspend]\n"); - return sysfs_emit(buf, "booting [running] suspend\n"); -} - -static ssize_t store_control_state(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - long unsigned int val; - if (strcmp(buf, "booting\n") == 0) - val = LEGACY_BOOTING; - else if (strcmp(buf, "suspend\n") == 0) - val = LEGACY_SUSPEND; - else if (interface == LEGACY) - val = LEGACY_RUNNING; - else - val = WMAX_RUNNING; - lighting_control_state = val; - pr_debug("alienware-wmi: updated control state to %d\n", - lighting_control_state); - return count; -} - -static DEVICE_ATTR(lighting_control_state, 0644, show_control_state, - store_control_state); - static int alienware_zone_init(struct platform_device *dev) { - u8 zone; - char *name; - if (interface == WMAX) { lighting_control_state = WMAX_RUNNING; } else if (interface == LEGACY) { @@ -646,68 +747,15 @@ static int alienware_zone_init(struct platform_device *dev) global_led.max_brightness = 0x0F; global_brightness = global_led.max_brightness; - /* - * - zone_dev_attrs num_zones + 1 is for individual zones and then - * null terminated - * - zone_attrs num_zones + 2 is for all attrs in zone_dev_attrs + - * the lighting control + null terminated - * - zone_data num_zones is for the distinct zones - */ - zone_dev_attrs = - kcalloc(quirks->num_zones + 1, sizeof(struct device_attribute), - GFP_KERNEL); - if (!zone_dev_attrs) - return -ENOMEM; - - zone_attrs = - kcalloc(quirks->num_zones + 2, sizeof(struct attribute *), - GFP_KERNEL); - if (!zone_attrs) - return -ENOMEM; - - zone_data = - kcalloc(quirks->num_zones, sizeof(struct platform_zone), - GFP_KERNEL); - if (!zone_data) - return -ENOMEM; - - for (zone = 0; zone < quirks->num_zones; zone++) { - name = kasprintf(GFP_KERNEL, "zone%02hhX", zone); - if (name == NULL) - return 1; - sysfs_attr_init(&zone_dev_attrs[zone].attr); - zone_dev_attrs[zone].attr.name = name; - zone_dev_attrs[zone].attr.mode = 0644; - zone_dev_attrs[zone].show = zone_show; - zone_dev_attrs[zone].store = zone_set; - zone_data[zone].location = zone; - zone_attrs[zone] = &zone_dev_attrs[zone].attr; - zone_data[zone].attr = &zone_dev_attrs[zone]; - } - zone_attrs[quirks->num_zones] = &dev_attr_lighting_control_state.attr; - zone_attribute_group.attrs = zone_attrs; - - led_classdev_register(&dev->dev, &global_led); - - return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group); + return led_classdev_register(&dev->dev, &global_led); } static void alienware_zone_exit(struct platform_device *dev) { - u8 zone; - if (!quirks->num_zones) return; - sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group); led_classdev_unregister(&global_led); - if (zone_dev_attrs) { - for (zone = 0; zone < quirks->num_zones; zone++) - kfree(zone_dev_attrs[zone].attr.name); - } - kfree(zone_dev_attrs); - kfree(zone_data); - kfree(zone_attrs); } static acpi_status alienware_wmax_command(void *in_args, size_t in_size, @@ -742,14 +790,15 @@ static acpi_status alienware_wmax_command(void *in_args, size_t in_size, * The HDMI mux sysfs node indicates the status of the HDMI input mux. * It can toggle between standard system GPU output and HDMI input. */ -static ssize_t show_hdmi_cable(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t cable_show(struct device *dev, struct device_attribute *attr, + char *buf) { - acpi_status status; - u32 out_data; struct wmax_basic_args in_args = { .arg = 0, }; + acpi_status status; + u32 out_data; + status = alienware_wmax_command(&in_args, sizeof(in_args), WMAX_METHOD_HDMI_CABLE, &out_data); @@ -763,14 +812,15 @@ static ssize_t show_hdmi_cable(struct device *dev, return sysfs_emit(buf, "unconnected connected [unknown]\n"); } -static ssize_t show_hdmi_source(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t source_show(struct device *dev, struct device_attribute *attr, + char *buf) { - acpi_status status; - u32 out_data; struct wmax_basic_args in_args = { .arg = 0, }; + acpi_status status; + u32 out_data; + status = alienware_wmax_command(&in_args, sizeof(in_args), WMAX_METHOD_HDMI_STATUS, &out_data); @@ -785,12 +835,12 @@ static ssize_t show_hdmi_source(struct device *dev, return sysfs_emit(buf, "input gpu [unknown]\n"); } -static ssize_t toggle_hdmi_source(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t source_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { - acpi_status status; struct wmax_basic_args args; + acpi_status status; + if (strcmp(buf, "gpu\n") == 0) args.arg = 1; else if (strcmp(buf, "input\n") == 0) @@ -808,9 +858,14 @@ static ssize_t toggle_hdmi_source(struct device *dev, return count; } -static DEVICE_ATTR(cable, S_IRUGO, show_hdmi_cable, NULL); -static DEVICE_ATTR(source, S_IRUGO | S_IWUSR, show_hdmi_source, - toggle_hdmi_source); +static DEVICE_ATTR_RO(cable); +static DEVICE_ATTR_RW(source); + +static bool hdmi_group_visible(struct kobject *kobj) +{ + return quirks->hdmi_mux; +} +DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(hdmi); static struct attribute *hdmi_attrs[] = { &dev_attr_cable.attr, @@ -820,38 +875,24 @@ static struct attribute *hdmi_attrs[] = { static const struct attribute_group hdmi_attribute_group = { .name = "hdmi", + .is_visible = SYSFS_GROUP_VISIBLE(hdmi), .attrs = hdmi_attrs, }; -static void remove_hdmi(struct platform_device *dev) -{ - if (quirks->hdmi_mux > 0) - sysfs_remove_group(&dev->dev.kobj, &hdmi_attribute_group); -} - -static int create_hdmi(struct platform_device *dev) -{ - int ret; - - ret = sysfs_create_group(&dev->dev.kobj, &hdmi_attribute_group); - if (ret) - remove_hdmi(dev); - return ret; -} - /* * Alienware GFX amplifier support * - Currently supports reading cable status * - Leaving expansion room to possibly support dock/undock events later */ -static ssize_t show_amplifier_status(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t status_show(struct device *dev, struct device_attribute *attr, + char *buf) { - acpi_status status; - u32 out_data; struct wmax_basic_args in_args = { .arg = 0, }; + acpi_status status; + u32 out_data; + status = alienware_wmax_command(&in_args, sizeof(in_args), WMAX_METHOD_AMPLIFIER_CABLE, &out_data); @@ -865,7 +906,13 @@ static ssize_t show_amplifier_status(struct device *dev, return sysfs_emit(buf, "unconnected connected [unknown]\n"); } -static DEVICE_ATTR(status, S_IRUGO, show_amplifier_status, NULL); +static DEVICE_ATTR_RO(status); + +static bool amplifier_group_visible(struct kobject *kobj) +{ + return quirks->amplifier; +} +DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(amplifier); static struct attribute *amplifier_attrs[] = { &dev_attr_status.attr, @@ -874,37 +921,23 @@ static struct attribute *amplifier_attrs[] = { static const struct attribute_group amplifier_attribute_group = { .name = "amplifier", + .is_visible = SYSFS_GROUP_VISIBLE(amplifier), .attrs = amplifier_attrs, }; -static void remove_amplifier(struct platform_device *dev) -{ - if (quirks->amplifier > 0) - sysfs_remove_group(&dev->dev.kobj, &lifier_attribute_group); -} - -static int create_amplifier(struct platform_device *dev) -{ - int ret; - - ret = sysfs_create_group(&dev->dev.kobj, &lifier_attribute_group); - if (ret) - remove_amplifier(dev); - return ret; -} - /* * Deep Sleep Control support * - Modifies BIOS setting for deep sleep control allowing extra wakeup events */ -static ssize_t show_deepsleep_status(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t deepsleep_show(struct device *dev, struct device_attribute *attr, + char *buf) { - acpi_status status; - u32 out_data; struct wmax_basic_args in_args = { .arg = 0, }; + acpi_status status; + u32 out_data; + status = alienware_wmax_command(&in_args, sizeof(in_args), WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data); if (ACPI_SUCCESS(status)) { @@ -919,12 +952,11 @@ static ssize_t show_deepsleep_status(struct device *dev, return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n"); } -static ssize_t toggle_deepsleep(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t deepsleep_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { - acpi_status status; struct wmax_basic_args args; + acpi_status status; if (strcmp(buf, "disabled\n") == 0) args.arg = 0; @@ -943,7 +975,13 @@ static ssize_t toggle_deepsleep(struct device *dev, return count; } -static DEVICE_ATTR(deepsleep, S_IRUGO | S_IWUSR, show_deepsleep_status, toggle_deepsleep); +static DEVICE_ATTR_RW(deepsleep); + +static bool deepsleep_group_visible(struct kobject *kobj) +{ + return quirks->deepslp; +} +DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(deepsleep); static struct attribute *deepsleep_attrs[] = { &dev_attr_deepsleep.attr, @@ -952,25 +990,10 @@ static struct attribute *deepsleep_attrs[] = { static const struct attribute_group deepsleep_attribute_group = { .name = "deepsleep", + .is_visible = SYSFS_GROUP_VISIBLE(deepsleep), .attrs = deepsleep_attrs, }; -static void remove_deepsleep(struct platform_device *dev) -{ - if (quirks->deepslp > 0) - sysfs_remove_group(&dev->dev.kobj, &deepsleep_attribute_group); -} - -static int create_deepsleep(struct platform_device *dev) -{ - int ret; - - ret = sysfs_create_group(&dev->dev.kobj, &deepsleep_attribute_group); - if (ret) - remove_deepsleep(dev); - return ret; -} - /* * Thermal Profile control * - Provides thermal profile control through the Platform Profile API @@ -1000,13 +1023,13 @@ static bool is_wmax_thermal_code(u32 code) static int wmax_thermal_information(u8 operation, u8 arg, u32 *out_data) { - acpi_status status; struct wmax_u32_args in_args = { .operation = operation, .arg1 = arg, .arg2 = 0, .arg3 = 0, }; + acpi_status status; status = alienware_wmax_command(&in_args, sizeof(in_args), WMAX_METHOD_THERMAL_INFORMATION, @@ -1023,13 +1046,13 @@ static int wmax_thermal_information(u8 operation, u8 arg, u32 *out_data) static int wmax_thermal_control(u8 profile) { - acpi_status status; struct wmax_u32_args in_args = { .operation = WMAX_OPERATION_ACTIVATE_PROFILE, .arg1 = profile, .arg2 = 0, .arg3 = 0, }; + acpi_status status; u32 out_data; status = alienware_wmax_command(&in_args, sizeof(in_args), @@ -1047,13 +1070,13 @@ static int wmax_thermal_control(u8 profile) static int wmax_game_shift_status(u8 operation, u32 *out_data) { - acpi_status status; struct wmax_u32_args in_args = { .operation = operation, .arg1 = 0, .arg2 = 0, .arg3 = 0, }; + acpi_status status; status = alienware_wmax_command(&in_args, sizeof(in_args), WMAX_METHOD_GAME_SHIFT_STATUS, @@ -1068,7 +1091,7 @@ static int wmax_game_shift_status(u8 operation, u32 *out_data) return 0; } -static int thermal_profile_get(struct platform_profile_handler *pprof, +static int thermal_profile_get(struct device *dev, enum platform_profile_option *profile) { u32 out_data; @@ -1094,7 +1117,7 @@ static int thermal_profile_get(struct platform_profile_handler *pprof, return 0; } -static int thermal_profile_set(struct platform_profile_handler *pprof, +static int thermal_profile_set(struct device *dev, enum platform_profile_option profile) { if (quirks->gmode) { @@ -1120,13 +1143,13 @@ static int thermal_profile_set(struct platform_profile_handler *pprof, return wmax_thermal_control(supported_thermal_profiles[profile]); } -static int create_thermal_profile(void) +static int thermal_profile_probe(void *drvdata, unsigned long *choices) { - u32 out_data; + enum platform_profile_option profile; + enum wmax_thermal_mode mode; u8 sys_desc[4]; u32 first_mode; - enum wmax_thermal_mode mode; - enum platform_profile_option profile; + u32 out_data; int ret; ret = wmax_thermal_information(WMAX_OPERATION_SYS_DESCRIPTION, @@ -1153,31 +1176,56 @@ static int create_thermal_profile(void) profile = wmax_mode_to_platform_profile[mode]; supported_thermal_profiles[profile] = out_data; - set_bit(profile, pp_handler.choices); + set_bit(profile, choices); } - if (bitmap_empty(pp_handler.choices, PLATFORM_PROFILE_LAST)) + if (bitmap_empty(choices, PLATFORM_PROFILE_LAST)) return -ENODEV; if (quirks->gmode) { supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] = WMAX_THERMAL_MODE_GMODE; - set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); } - pp_handler.profile_get = thermal_profile_get; - pp_handler.profile_set = thermal_profile_set; - - return platform_profile_register(&pp_handler); + return 0; } -static void remove_thermal_profile(void) +static const struct platform_profile_ops awcc_platform_profile_ops = { + .probe = thermal_profile_probe, + .profile_get = thermal_profile_get, + .profile_set = thermal_profile_set, +}; + +static int create_thermal_profile(struct platform_device *platform_device) { - if (quirks->thermal) - platform_profile_remove(); + struct device *ppdev; + + ppdev = devm_platform_profile_register(&platform_device->dev, "alienware-wmi", + NULL, &awcc_platform_profile_ops); + + return PTR_ERR_OR_ZERO(ppdev); } +/* + * Platform Driver + */ +static const struct attribute_group *alienfx_groups[] = { + &zone_attribute_group, + &hdmi_attribute_group, + &lifier_attribute_group, + &deepsleep_attribute_group, + NULL +}; + +static struct platform_driver platform_driver = { + .driver = { + .name = "alienware-wmi", + .dev_groups = alienfx_groups, + }, +}; + static int __init alienware_wmi_init(void) { int ret; @@ -1217,26 +1265,8 @@ static int __init alienware_wmi_init(void) if (ret) goto fail_platform_device2; - if (quirks->hdmi_mux > 0) { - ret = create_hdmi(platform_device); - if (ret) - goto fail_prep_hdmi; - } - - if (quirks->amplifier > 0) { - ret = create_amplifier(platform_device); - if (ret) - goto fail_prep_amplifier; - } - - if (quirks->deepslp > 0) { - ret = create_deepsleep(platform_device); - if (ret) - goto fail_prep_deepsleep; - } - if (quirks->thermal) { - ret = create_thermal_profile(); + ret = create_thermal_profile(platform_device); if (ret) goto fail_prep_thermal_profile; } @@ -1251,11 +1281,7 @@ static int __init alienware_wmi_init(void) fail_prep_zones: alienware_zone_exit(platform_device); - remove_thermal_profile(); fail_prep_thermal_profile: -fail_prep_deepsleep: -fail_prep_amplifier: -fail_prep_hdmi: platform_device_del(platform_device); fail_platform_device2: platform_device_put(platform_device); @@ -1269,13 +1295,9 @@ module_init(alienware_wmi_init); static void __exit alienware_wmi_exit(void) { - if (platform_device) { - alienware_zone_exit(platform_device); - remove_hdmi(platform_device); - remove_thermal_profile(); - platform_device_unregister(platform_device); - platform_driver_unregister(&platform_driver); - } + alienware_zone_exit(platform_device); + platform_device_unregister(platform_device); + platform_driver_unregister(&platform_driver); } module_exit(alienware_wmi_exit); diff --git a/drivers/platform/x86/dell/dcdbas.c b/drivers/platform/x86/dell/dcdbas.c index 0aeb8149c16b..8149be25fa26 100644 --- a/drivers/platform/x86/dell/dcdbas.c +++ b/drivers/platform/x86/dell/dcdbas.c @@ -163,7 +163,7 @@ static ssize_t smi_data_buf_size_store(struct device *dev, } static ssize_t smi_data_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { ssize_t ret; @@ -176,7 +176,7 @@ static ssize_t smi_data_read(struct file *filp, struct kobject *kobj, } static ssize_t smi_data_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { ssize_t ret; @@ -636,9 +636,9 @@ static struct notifier_block dcdbas_reboot_nb = { .priority = INT_MIN }; -static DCDBAS_BIN_ATTR_RW(smi_data); +static const BIN_ATTR_ADMIN_RW(smi_data, 0); -static struct bin_attribute *dcdbas_bin_attrs[] = { +static const struct bin_attribute *const dcdbas_bin_attrs[] = { &bin_attr_smi_data, NULL }; @@ -662,7 +662,7 @@ static struct attribute *dcdbas_dev_attrs[] = { static const struct attribute_group dcdbas_attr_group = { .attrs = dcdbas_dev_attrs, - .bin_attrs = dcdbas_bin_attrs, + .bin_attrs_new = dcdbas_bin_attrs, }; static int dcdbas_probe(struct platform_device *dev) diff --git a/drivers/platform/x86/dell/dcdbas.h b/drivers/platform/x86/dell/dcdbas.h index 942a23ddded0..a05d7f667586 100644 --- a/drivers/platform/x86/dell/dcdbas.h +++ b/drivers/platform/x86/dell/dcdbas.h @@ -56,14 +56,6 @@ #define DCDBAS_DEV_ATTR_WO(_name) \ DEVICE_ATTR(_name,0200,NULL,_name##_store); -#define DCDBAS_BIN_ATTR_RW(_name) \ -struct bin_attribute bin_attr_##_name = { \ - .attr = { .name = __stringify(_name), \ - .mode = 0600 }, \ - .read = _name##_read, \ - .write = _name##_write, \ -} - struct smi_cmd { __u32 magic; __u32 ebx; diff --git a/drivers/platform/x86/dell/dell-laptop.c b/drivers/platform/x86/dell/dell-laptop.c index 5671bd0deee7..57748c3ea24f 100644 --- a/drivers/platform/x86/dell/dell-laptop.c +++ b/drivers/platform/x86/dell/dell-laptop.c @@ -103,15 +103,15 @@ static bool mute_led_registered; struct battery_mode_info { int token; - const char *label; + enum power_supply_charge_type charge_type; }; static const struct battery_mode_info battery_modes[] = { - { BAT_PRI_AC_MODE_TOKEN, "Trickle" }, - { BAT_EXPRESS_MODE_TOKEN, "Fast" }, - { BAT_STANDARD_MODE_TOKEN, "Standard" }, - { BAT_ADAPTIVE_MODE_TOKEN, "Adaptive" }, - { BAT_CUSTOM_MODE_TOKEN, "Custom" }, + { BAT_PRI_AC_MODE_TOKEN, POWER_SUPPLY_CHARGE_TYPE_TRICKLE }, + { BAT_EXPRESS_MODE_TOKEN, POWER_SUPPLY_CHARGE_TYPE_FAST }, + { BAT_STANDARD_MODE_TOKEN, POWER_SUPPLY_CHARGE_TYPE_STANDARD }, + { BAT_ADAPTIVE_MODE_TOKEN, POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE }, + { BAT_CUSTOM_MODE_TOKEN, POWER_SUPPLY_CHARGE_TYPE_CUSTOM }, }; static u32 battery_supported_modes; @@ -725,8 +725,8 @@ static void dell_update_rfkill(struct work_struct *ignored) } static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill); -static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, - struct serio *port) +static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, struct serio *port, + void *context) { static bool extended; @@ -884,7 +884,7 @@ static int __init dell_setup_rfkill(void) pr_warn("Unable to register dell rbtn notifier\n"); goto err_filter; } else { - ret = i8042_install_filter(dell_laptop_i8042_filter); + ret = i8042_install_filter(dell_laptop_i8042_filter, NULL); if (ret) { pr_warn("Unable to install key filter\n"); goto err_filter; @@ -2261,46 +2261,42 @@ static ssize_t charge_types_show(struct device *dev, struct device_attribute *attr, char *buf) { - ssize_t count = 0; + enum power_supply_charge_type charge_type; int i; for (i = 0; i < ARRAY_SIZE(battery_modes); i++) { - bool active; + charge_type = battery_modes[i].charge_type; - if (!(battery_supported_modes & BIT(i))) + if (!(battery_supported_modes & BIT(charge_type))) continue; - active = dell_battery_mode_is_active(battery_modes[i].token); - count += sysfs_emit_at(buf, count, active ? "[%s] " : "%s ", - battery_modes[i].label); - } + if (!dell_battery_mode_is_active(battery_modes[i].token)) + continue; - /* convert the last space to a newline */ - if (count > 0) - count--; - count += sysfs_emit_at(buf, count, "\n"); + return power_supply_charge_types_show(dev, battery_supported_modes, + charge_type, buf); + } - return count; + /* No active mode found */ + return -EIO; } static ssize_t charge_types_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - bool matched = false; - int err, i; + int charge_type, err, i; - for (i = 0; i < ARRAY_SIZE(battery_modes); i++) { - if (!(battery_supported_modes & BIT(i))) - continue; + charge_type = power_supply_charge_types_parse(battery_supported_modes, buf); + if (charge_type < 0) + return charge_type; - if (sysfs_streq(battery_modes[i].label, buf)) { - matched = true; + for (i = 0; i < ARRAY_SIZE(battery_modes); i++) { + if (battery_modes[i].charge_type == charge_type) break; - } } - if (!matched) - return -EINVAL; + if (i == ARRAY_SIZE(battery_modes)) + return -ENOENT; err = dell_battery_set_mode(battery_modes[i].token); if (err) @@ -2430,7 +2426,7 @@ static u32 __init battery_get_supported_modes(void) for (i = 0; i < ARRAY_SIZE(battery_modes); i++) { if (dell_smbios_find_token(battery_modes[i].token)) - modes |= BIT(i); + modes |= BIT(battery_modes[i].charge_type); } return modes; diff --git a/drivers/platform/x86/dell/dell-lis3lv02d.c b/drivers/platform/x86/dell/dell-lis3lv02d.c new file mode 100644 index 000000000000..efe26d667973 --- /dev/null +++ b/drivers/platform/x86/dell/dell-lis3lv02d.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * lis3lv02d i2c-client instantiation for ACPI SMO88xx devices without I2C resources. + * + * Copyright (C) 2024 Hans de Goede <hansg@kernel.org> + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/device/bus.h> +#include <linux/dmi.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/notifier.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include "dell-smo8800-ids.h" + +#define LIS3_WHO_AM_I 0x0f + +#define DELL_LIS3LV02D_DMI_ENTRY(product_name, i2c_addr) \ + { \ + .matches = { \ + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), \ + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, product_name), \ + }, \ + .driver_data = (void *)(uintptr_t)(i2c_addr), \ + } + +/* + * Accelerometer's I2C address is not specified in DMI nor ACPI, + * so it is needed to define mapping table based on DMI product names. + */ +static const struct dmi_system_id lis3lv02d_devices[] __initconst = { + /* + * Dell platform team told us that these Latitude devices have + * ST microelectronics accelerometer at I2C address 0x29. + */ + DELL_LIS3LV02D_DMI_ENTRY("Latitude E5250", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Latitude E5450", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Latitude E5550", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Latitude E6440", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Latitude E6440 ATG", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Latitude E6540", 0x29), + /* + * Additional individual entries were added after verification. + */ + DELL_LIS3LV02D_DMI_ENTRY("Latitude 5480", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Latitude E6330", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Latitude E6430", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Precision 3540", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Precision M6800", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Vostro V131", 0x1d), + DELL_LIS3LV02D_DMI_ENTRY("Vostro 5568", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("XPS 15 7590", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("XPS 15 9550", 0x29), + { } +}; + +static u8 i2c_addr; +static struct i2c_client *i2c_dev; +static bool notifier_registered; + +static bool probe_i2c_addr; +module_param(probe_i2c_addr, bool, 0444); +MODULE_PARM_DESC(probe_i2c_addr, "Probe the i801 I2C bus for the accelerometer on models where the address is unknown, this may be dangerous."); + +static int detect_lis3lv02d(struct i2c_adapter *adap, unsigned short addr) +{ + union i2c_smbus_data smbus_data; + int err; + + dev_info(&adap->dev, "Probing for lis3lv02d on address 0x%02x\n", addr); + + err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, LIS3_WHO_AM_I, + I2C_SMBUS_BYTE_DATA, &smbus_data); + if (err < 0) + return 0; /* Not found */ + + /* valid who-am-i values are from drivers/misc/lis3lv02d/lis3lv02d.c */ + switch (smbus_data.byte) { + case 0x32: + case 0x33: + case 0x3a: + case 0x3b: + break; + default: + dev_warn(&adap->dev, "Unknown who-am-i register value 0x%02x\n", + smbus_data.byte); + return 0; /* Not found */ + } + + dev_info(&adap->dev, + "Detected lis3lv02d on address 0x%02x, please report this upstream to platform-driver-x86@vger.kernel.org so that a quirk can be added\n", + addr); + + return 1; /* Found */ +} + +static bool i2c_adapter_is_main_i801(struct i2c_adapter *adap) +{ + /* + * Only match the main I801 adapter and reject secondary adapters + * which names start with "SMBus I801 IDF adapter". + */ + return strstarts(adap->name, "SMBus I801 adapter"); +} + +static int find_i801(struct device *dev, void *data) +{ + struct i2c_adapter *adap, **adap_ret = data; + + adap = i2c_verify_adapter(dev); + if (!adap) + return 0; + + if (!i2c_adapter_is_main_i801(adap)) + return 0; + + *adap_ret = i2c_get_adapter(adap->nr); + return 1; +} + +static void instantiate_i2c_client(struct work_struct *work) +{ + struct i2c_board_info info = { }; + struct i2c_adapter *adap = NULL; + + if (i2c_dev) + return; + + /* + * bus_for_each_dev() and not i2c_for_each_dev() to avoid + * a deadlock when find_i801() calls i2c_get_adapter(). + */ + bus_for_each_dev(&i2c_bus_type, NULL, &adap, find_i801); + if (!adap) + return; + + strscpy(info.type, "lis3lv02d", I2C_NAME_SIZE); + + if (i2c_addr) { + info.addr = i2c_addr; + i2c_dev = i2c_new_client_device(adap, &info); + } else { + /* First try address 0x29 (most used) and then try 0x1d */ + static const unsigned short addr_list[] = { 0x29, 0x1d, I2C_CLIENT_END }; + + i2c_dev = i2c_new_scanned_device(adap, &info, addr_list, detect_lis3lv02d); + } + + if (IS_ERR(i2c_dev)) { + dev_err(&adap->dev, "error %ld registering i2c_client\n", PTR_ERR(i2c_dev)); + i2c_dev = NULL; + } else { + dev_dbg(&adap->dev, "registered lis3lv02d on address 0x%02x\n", info.addr); + } + + i2c_put_adapter(adap); +} +static DECLARE_WORK(i2c_work, instantiate_i2c_client); + +static int i2c_bus_notify(struct notifier_block *nb, unsigned long action, void *data) +{ + struct device *dev = data; + struct i2c_client *client; + struct i2c_adapter *adap; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + adap = i2c_verify_adapter(dev); + if (!adap) + break; + + if (i2c_adapter_is_main_i801(adap)) + queue_work(system_long_wq, &i2c_work); + break; + case BUS_NOTIFY_REMOVED_DEVICE: + client = i2c_verify_client(dev); + if (!client) + break; + + if (i2c_dev == client) { + dev_dbg(&client->adapter->dev, "lis3lv02d i2c_client removed\n"); + i2c_dev = NULL; + } + break; + default: + break; + } + + return 0; +} +static struct notifier_block i2c_nb = { .notifier_call = i2c_bus_notify }; + +static int __init match_acpi_device_ids(struct device *dev, const void *data) +{ + return acpi_match_device(data, dev) ? 1 : 0; +} + +static int __init dell_lis3lv02d_init(void) +{ + const struct dmi_system_id *lis3lv02d_dmi_id; + struct device *dev; + int err; + + /* + * First check for a matching platform_device. This protects against + * SMO88xx ACPI fwnodes which actually do have an I2C resource, which + * will already have an i2c_client instantiated (not a platform_device). + */ + dev = bus_find_device(&platform_bus_type, NULL, smo8800_ids, match_acpi_device_ids); + if (!dev) { + pr_debug("No SMO88xx platform-device found\n"); + return 0; + } + put_device(dev); + + lis3lv02d_dmi_id = dmi_first_match(lis3lv02d_devices); + if (!lis3lv02d_dmi_id && !probe_i2c_addr) { + pr_warn("accelerometer is present on SMBus but its address is unknown, skipping registration\n"); + pr_info("Pass dell_lis3lv02d.probe_i2c_addr=1 on the kernel command line to probe, this may be dangerous!\n"); + return 0; + } + + if (lis3lv02d_dmi_id) + i2c_addr = (long)lis3lv02d_dmi_id->driver_data; + + /* + * Register i2c-bus notifier + queue initial scan for lis3lv02d + * i2c_client instantiation. + */ + err = bus_register_notifier(&i2c_bus_type, &i2c_nb); + if (err) + return err; + + notifier_registered = true; + + queue_work(system_long_wq, &i2c_work); + return 0; +} +module_init(dell_lis3lv02d_init); + +static void __exit dell_lis3lv02d_module_exit(void) +{ + if (!notifier_registered) + return; + + bus_unregister_notifier(&i2c_bus_type, &i2c_nb); + cancel_work_sync(&i2c_work); + i2c_unregister_device(i2c_dev); +} +module_exit(dell_lis3lv02d_module_exit); + +MODULE_DESCRIPTION("lis3lv02d i2c-client instantiation for ACPI SMO88xx devices"); +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/dell/dell-pc.c b/drivers/platform/x86/dell/dell-pc.c index 972385ca1990..483240bb36e7 100644 --- a/drivers/platform/x86/dell/dell-pc.c +++ b/drivers/platform/x86/dell/dell-pc.c @@ -18,10 +18,14 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_profile.h> +#include <linux/platform_device.h> #include <linux/slab.h> #include "dell-smbios.h" +static struct platform_device *platform_device; +static int supported_modes; + static const struct dmi_system_id dell_device_table[] __initconst = { { .ident = "Dell Inc.", @@ -105,8 +109,6 @@ MODULE_DEVICE_TABLE(dmi, dell_device_table); #define DELL_ACC_SET_FIELD GENMASK(11, 8) #define DELL_THERMAL_SUPPORTED GENMASK(3, 0) -static struct platform_profile_handler *thermal_handler; - enum thermal_mode_bits { DELL_BALANCED = BIT(0), DELL_COOL_BOTTOM = BIT(1), @@ -182,7 +184,7 @@ static int thermal_set_mode(enum thermal_mode_bits state) return dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT); } -static int thermal_platform_profile_set(struct platform_profile_handler *pprof, +static int thermal_platform_profile_set(struct device *dev, enum platform_profile_option profile) { switch (profile) { @@ -199,7 +201,7 @@ static int thermal_platform_profile_set(struct platform_profile_handler *pprof, } } -static int thermal_platform_profile_get(struct platform_profile_handler *pprof, +static int thermal_platform_profile_get(struct device *dev, enum platform_profile_option *profile) { int ret; @@ -228,10 +230,30 @@ static int thermal_platform_profile_get(struct platform_profile_handler *pprof, return 0; } +static int thermal_platform_profile_probe(void *drvdata, unsigned long *choices) +{ + if (supported_modes & DELL_QUIET) + set_bit(PLATFORM_PROFILE_QUIET, choices); + if (supported_modes & DELL_COOL_BOTTOM) + set_bit(PLATFORM_PROFILE_COOL, choices); + if (supported_modes & DELL_BALANCED) + set_bit(PLATFORM_PROFILE_BALANCED, choices); + if (supported_modes & DELL_PERFORMANCE) + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + + return 0; +} + +static const struct platform_profile_ops dell_pc_platform_profile_ops = { + .probe = thermal_platform_profile_probe, + .profile_get = thermal_platform_profile_get, + .profile_set = thermal_platform_profile_set, +}; + static int thermal_init(void) { + struct device *ppdev; int ret; - int supported_modes; /* If thermal commands are not supported, exit without error */ if (!dell_smbios_class_is_supported(CLASS_INFO)) @@ -244,37 +266,28 @@ static int thermal_init(void) if (!supported_modes) return 0; - thermal_handler = kzalloc(sizeof(*thermal_handler), GFP_KERNEL); - if (!thermal_handler) - return -ENOMEM; - thermal_handler->profile_get = thermal_platform_profile_get; - thermal_handler->profile_set = thermal_platform_profile_set; + platform_device = platform_device_register_simple("dell-pc", PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(platform_device)) + return PTR_ERR(platform_device); - if (supported_modes & DELL_QUIET) - set_bit(PLATFORM_PROFILE_QUIET, thermal_handler->choices); - if (supported_modes & DELL_COOL_BOTTOM) - set_bit(PLATFORM_PROFILE_COOL, thermal_handler->choices); - if (supported_modes & DELL_BALANCED) - set_bit(PLATFORM_PROFILE_BALANCED, thermal_handler->choices); - if (supported_modes & DELL_PERFORMANCE) - set_bit(PLATFORM_PROFILE_PERFORMANCE, thermal_handler->choices); - - /* Clean up if failed */ - ret = platform_profile_register(thermal_handler); - if (ret) { - kfree(thermal_handler); - thermal_handler = NULL; + ppdev = devm_platform_profile_register(&platform_device->dev, "dell-pc", + NULL, &dell_pc_platform_profile_ops); + if (IS_ERR(ppdev)) { + ret = PTR_ERR(ppdev); + goto cleanup_platform_device; } + return 0; + +cleanup_platform_device: + platform_device_unregister(platform_device); + return ret; } static void thermal_cleanup(void) { - if (thermal_handler) { - platform_profile_remove(); - kfree(thermal_handler); - } + platform_device_unregister(platform_device); } static int __init dell_init(void) diff --git a/drivers/platform/x86/dell/dell-smo8800-ids.h b/drivers/platform/x86/dell/dell-smo8800-ids.h new file mode 100644 index 000000000000..ec58e229ba7a --- /dev/null +++ b/drivers/platform/x86/dell/dell-smo8800-ids.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * ACPI SMO88XX lis3lv02d freefall / accelerometer device-ids. + * + * Copyright (C) 2012 Sonal Santan <sonal.santan@gmail.com> + * Copyright (C) 2014 Pali Rohár <pali@kernel.org> + */ +#ifndef _DELL_SMO8800_IDS_H_ +#define _DELL_SMO8800_IDS_H_ + +#include <linux/mod_devicetable.h> +#include <linux/module.h> + +static const struct acpi_device_id smo8800_ids[] = { + { "SMO8800" }, + { "SMO8801" }, + { "SMO8810" }, + { "SMO8811" }, + { "SMO8820" }, + { "SMO8821" }, + { "SMO8830" }, + { "SMO8831" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, smo8800_ids); + +#endif diff --git a/drivers/platform/x86/dell/dell-smo8800.c b/drivers/platform/x86/dell/dell-smo8800.c index 87fe03f23f24..8872f9b57fce 100644 --- a/drivers/platform/x86/dell/dell-smo8800.c +++ b/drivers/platform/x86/dell/dell-smo8800.c @@ -14,10 +14,10 @@ #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/miscdevice.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/uaccess.h> +#include "dell-smo8800-ids.h" struct smo8800_device { u32 irq; /* acpi device irq */ @@ -163,20 +163,6 @@ static void smo8800_remove(struct platform_device *device) dev_dbg(&device->dev, "device /dev/freefall unregistered\n"); } -/* NOTE: Keep this list in sync with drivers/i2c/busses/i2c-i801.c */ -static const struct acpi_device_id smo8800_ids[] = { - { "SMO8800", 0 }, - { "SMO8801", 0 }, - { "SMO8810", 0 }, - { "SMO8811", 0 }, - { "SMO8820", 0 }, - { "SMO8821", 0 }, - { "SMO8830", 0 }, - { "SMO8831", 0 }, - { "", 0 }, -}; -MODULE_DEVICE_TABLE(acpi, smo8800_ids); - static struct platform_driver smo8800_driver = { .probe = smo8800_probe, .remove = smo8800_remove, diff --git a/drivers/platform/x86/dell/dell-uart-backlight.c b/drivers/platform/x86/dell/dell-uart-backlight.c index bcc5c0f3bb4d..8f868f845350 100644 --- a/drivers/platform/x86/dell/dell-uart-backlight.c +++ b/drivers/platform/x86/dell/dell-uart-backlight.c @@ -159,7 +159,7 @@ static int dell_uart_set_bl_power(struct dell_uart_backlight *dell_bl, int power set_power[0] = DELL_SOF(SET_CMD_LEN); set_power[1] = CMD_SET_BL_POWER; - set_power[2] = (power == FB_BLANK_UNBLANK) ? 1 : 0; + set_power[2] = (power == BACKLIGHT_POWER_ON) ? 1 : 0; set_power[3] = dell_uart_checksum(set_power, 3); ret = dell_uart_bl_command(dell_bl, set_power, SET_CMD_LEN, resp, SET_RESP_LEN); @@ -325,7 +325,7 @@ static int dell_uart_bl_serdev_probe(struct serdev_device *serdev) return PTR_ERR_OR_ZERO(dell_bl->bl); } -struct serdev_device_driver dell_uart_bl_serdev_driver = { +static struct serdev_device_driver dell_uart_bl_serdev_driver = { .probe = dell_uart_bl_serdev_probe, .driver = { .name = KBUILD_MODNAME, diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c index e75cd6e1efe6..ab5f7d3ab824 100644 --- a/drivers/platform/x86/dell/dell-wmi-ddv.c +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c @@ -665,8 +665,10 @@ static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char if (ret < 0) return ret; - /* Use 2731 instead of 2731.5 to avoid unnecessary rounding */ - return sysfs_emit(buf, "%d\n", value - 2731); + /* Use 2732 instead of 2731.5 to avoid unnecessary rounding and to emulate + * the behaviour of the OEM application which seems to round down the result. + */ + return sysfs_emit(buf, "%d\n", value - 2732); } static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, char *buf) diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c index 40ddc6eb7562..d00389b860e4 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c @@ -25,7 +25,6 @@ struct wmi_sysman_priv wmi_priv = { /* reset bios to defaults */ static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"}; static int reset_option = -1; -static const struct class *fw_attr_class; /** @@ -541,15 +540,11 @@ static int __init sysman_init(void) goto err_exit_bios_attr_pass_interface; } - ret = fw_attributes_class_get(&fw_attr_class); - if (ret) - goto err_exit_bios_attr_pass_interface; - - wmi_priv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0), + wmi_priv.class_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0), NULL, "%s", DRIVER_NAME); if (IS_ERR(wmi_priv.class_dev)) { ret = PTR_ERR(wmi_priv.class_dev); - goto err_unregister_class; + goto err_exit_bios_attr_pass_interface; } wmi_priv.main_dir_kset = kset_create_and_add("attributes", NULL, @@ -602,10 +597,7 @@ err_release_attributes_data: release_attributes_data(); err_destroy_classdev: - device_destroy(fw_attr_class, MKDEV(0, 0)); - -err_unregister_class: - fw_attributes_class_put(); + device_destroy(&firmware_attributes_class, MKDEV(0, 0)); err_exit_bios_attr_pass_interface: exit_bios_attr_pass_interface(); @@ -619,8 +611,7 @@ err_exit_bios_attr_set_interface: static void __exit sysman_exit(void) { release_attributes_data(); - device_destroy(fw_attr_class, MKDEV(0, 0)); - fw_attributes_class_put(); + device_destroy(&firmware_attributes_class, MKDEV(0, 0)); exit_bios_attr_set_interface(); exit_bios_attr_pass_interface(); } diff --git a/drivers/platform/x86/dell/dell_rbu.c b/drivers/platform/x86/dell/dell_rbu.c index 9f51e0fcab04..e30ca325938c 100644 --- a/drivers/platform/x86/dell/dell_rbu.c +++ b/drivers/platform/x86/dell/dell_rbu.c @@ -475,7 +475,7 @@ static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count) } static ssize_t data_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { ssize_t ret_count = 0; @@ -492,7 +492,7 @@ static ssize_t data_read(struct file *filp, struct kobject *kobj, spin_unlock(&rbu_data.lock); return ret_count; } -static BIN_ATTR_RO(data, 0); +static const BIN_ATTR_RO(data, 0); static void callbackfn_rbu(const struct firmware *fw, void *context) { @@ -530,7 +530,7 @@ static void callbackfn_rbu(const struct firmware *fw, void *context) } static ssize_t image_type_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { int size = 0; @@ -540,7 +540,7 @@ static ssize_t image_type_read(struct file *filp, struct kobject *kobj, } static ssize_t image_type_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { int rc = count; @@ -597,10 +597,10 @@ static ssize_t image_type_write(struct file *filp, struct kobject *kobj, return rc; } -static BIN_ATTR_RW(image_type, 0); +static const BIN_ATTR_RW(image_type, 0); static ssize_t packet_size_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { int size = 0; @@ -613,7 +613,7 @@ static ssize_t packet_size_read(struct file *filp, struct kobject *kobj, } static ssize_t packet_size_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { unsigned long temp; @@ -626,9 +626,9 @@ static ssize_t packet_size_write(struct file *filp, struct kobject *kobj, spin_unlock(&rbu_data.lock); return count; } -static BIN_ATTR_RW(packet_size, 0); +static const BIN_ATTR_RW(packet_size, 0); -static struct bin_attribute *rbu_bin_attrs[] = { +static const struct bin_attribute *const rbu_bin_attrs[] = { &bin_attr_data, &bin_attr_image_type, &bin_attr_packet_size, @@ -636,7 +636,7 @@ static struct bin_attribute *rbu_bin_attrs[] = { }; static const struct attribute_group rbu_group = { - .bin_attrs = rbu_bin_attrs, + .bin_attrs_new = rbu_bin_attrs, }; static int __init dcdrbu_init(void) diff --git a/drivers/platform/x86/firmware_attributes_class.c b/drivers/platform/x86/firmware_attributes_class.c index 182a07d8ae3d..736e96c186d9 100644 --- a/drivers/platform/x86/firmware_attributes_class.c +++ b/drivers/platform/x86/firmware_attributes_class.c @@ -2,51 +2,25 @@ /* Firmware attributes class helper module */ -#include <linux/mutex.h> -#include <linux/device/class.h> #include <linux/module.h> #include "firmware_attributes_class.h" -static DEFINE_MUTEX(fw_attr_lock); -static int fw_attr_inuse; - -static const struct class firmware_attributes_class = { +const struct class firmware_attributes_class = { .name = "firmware-attributes", }; +EXPORT_SYMBOL_GPL(firmware_attributes_class); -int fw_attributes_class_get(const struct class **fw_attr_class) +static __init int fw_attributes_class_init(void) { - int err; - - mutex_lock(&fw_attr_lock); - if (!fw_attr_inuse) { /*first time class is being used*/ - err = class_register(&firmware_attributes_class); - if (err) { - mutex_unlock(&fw_attr_lock); - return err; - } - } - fw_attr_inuse++; - *fw_attr_class = &firmware_attributes_class; - mutex_unlock(&fw_attr_lock); - return 0; + return class_register(&firmware_attributes_class); } -EXPORT_SYMBOL_GPL(fw_attributes_class_get); +module_init(fw_attributes_class_init); -int fw_attributes_class_put(void) +static __exit void fw_attributes_class_exit(void) { - mutex_lock(&fw_attr_lock); - if (!fw_attr_inuse) { - mutex_unlock(&fw_attr_lock); - return -EINVAL; - } - fw_attr_inuse--; - if (!fw_attr_inuse) /* No more consumers */ - class_unregister(&firmware_attributes_class); - mutex_unlock(&fw_attr_lock); - return 0; + class_unregister(&firmware_attributes_class); } -EXPORT_SYMBOL_GPL(fw_attributes_class_put); +module_exit(fw_attributes_class_exit); MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>"); MODULE_DESCRIPTION("Firmware attributes class helper module"); diff --git a/drivers/platform/x86/firmware_attributes_class.h b/drivers/platform/x86/firmware_attributes_class.h index 363c75f1ac1b..d27abe54fcf9 100644 --- a/drivers/platform/x86/firmware_attributes_class.h +++ b/drivers/platform/x86/firmware_attributes_class.h @@ -5,7 +5,8 @@ #ifndef FW_ATTR_CLASS_H #define FW_ATTR_CLASS_H -int fw_attributes_class_get(const struct class **fw_attr_class); -int fw_attributes_class_put(void); +#include <linux/device/class.h> + +extern const struct class firmware_attributes_class; #endif /* FW_ATTR_CLASS_H */ diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index ae992ac1ab4a..a0eae24ca9e6 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -505,8 +505,8 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device) return -ENOMEM; fujitsu_bl = priv; - strcpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); + strscpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME); + strscpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); device->driver_data = priv; pr_info("ACPI: %s [%s]\n", @@ -891,8 +891,8 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) WARN_ONCE(fext, "More than one FUJ02E3 ACPI device was found. Driver may not work as intended."); fext = device; - strcpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); + strscpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME); + strscpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); device->driver_data = priv; /* kfifo */ diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c index 2dc50152158a..0b277b7e37dd 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c @@ -24,8 +24,6 @@ struct bioscfg_priv bioscfg_drv = { .mutex = __MUTEX_INITIALIZER(bioscfg_drv.mutex), }; -static const struct class *fw_attr_class; - ssize_t display_name_language_code_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -972,11 +970,7 @@ static int __init hp_init(void) if (ret) return ret; - ret = fw_attributes_class_get(&fw_attr_class); - if (ret) - goto err_unregister_class; - - bioscfg_drv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0), + bioscfg_drv.class_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0), NULL, "%s", DRIVER_NAME); if (IS_ERR(bioscfg_drv.class_dev)) { ret = PTR_ERR(bioscfg_drv.class_dev); @@ -1043,10 +1037,9 @@ err_release_attributes_data: release_attributes_data(); err_destroy_classdev: - device_destroy(fw_attr_class, MKDEV(0, 0)); + device_destroy(&firmware_attributes_class, MKDEV(0, 0)); err_unregister_class: - fw_attributes_class_put(); hp_exit_attr_set_interface(); return ret; @@ -1055,9 +1048,8 @@ err_unregister_class: static void __exit hp_exit(void) { release_attributes_data(); - device_destroy(fw_attr_class, MKDEV(0, 0)); + device_destroy(&firmware_attributes_class, MKDEV(0, 0)); - fw_attributes_class_put(); hp_exit_attr_set_interface(); } diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 20c55bab3b8c..db5fdee2109c 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -45,6 +45,10 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4"); #define HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET 0x63 #define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95 +#define HP_FAN_SPEED_AUTOMATIC 0x00 +#define HP_POWER_LIMIT_DEFAULT 0x00 +#define HP_POWER_LIMIT_NO_CHANGE 0xFF + #define ACPI_AC_CLASS "ac_adapter" #define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required @@ -83,11 +87,16 @@ static const char * const omen_timed_thermal_profile_boards[] = { "8BAD", "8A42", "8A15" }; -/* DMI Board names of Victus laptops */ +/* DMI Board names of Victus 16-d1xxx laptops */ static const char * const victus_thermal_profile_boards[] = { "8A25" }; +/* DMI Board names of Victus 16-s1000 laptops */ +static const char * const victus_s_thermal_profile_boards[] = { + "8C9C" +}; + enum hp_wmi_radio { HPWMI_WIFI = 0x0, HPWMI_BLUETOOTH = 0x1, @@ -147,12 +156,32 @@ enum hp_wmi_commandtype { HPWMI_THERMAL_PROFILE_QUERY = 0x4c, }; +struct victus_power_limits { + u8 pl1; + u8 pl2; + u8 pl4; + u8 cpu_gpu_concurrent_limit; +}; + +struct victus_gpu_power_modes { + u8 ctgp_enable; + u8 ppab_enable; + u8 dstate; + u8 gpu_slowdown_temp; +}; + enum hp_wmi_gm_commandtype { - HPWMI_FAN_SPEED_GET_QUERY = 0x11, - HPWMI_SET_PERFORMANCE_MODE = 0x1A, - HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26, - HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27, - HPWMI_GET_SYSTEM_DESIGN_DATA = 0x28, + HPWMI_FAN_SPEED_GET_QUERY = 0x11, + HPWMI_SET_PERFORMANCE_MODE = 0x1A, + HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26, + HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27, + HPWMI_GET_SYSTEM_DESIGN_DATA = 0x28, + HPWMI_FAN_COUNT_GET_QUERY = 0x10, + HPWMI_GET_GPU_THERMAL_MODES_QUERY = 0x21, + HPWMI_SET_GPU_THERMAL_MODES_QUERY = 0x22, + HPWMI_SET_POWER_LIMITS_QUERY = 0x29, + HPWMI_VICTUS_S_FAN_SPEED_GET_QUERY = 0x2D, + HPWMI_FAN_SPEED_SET_QUERY = 0x2E, }; enum hp_wmi_command { @@ -211,6 +240,11 @@ enum hp_thermal_profile_victus { HP_VICTUS_THERMAL_PROFILE_QUIET = 0x03, }; +enum hp_thermal_profile_victus_s { + HP_VICTUS_S_THERMAL_PROFILE_DEFAULT = 0x00, + HP_VICTUS_S_THERMAL_PROFILE_PERFORMANCE = 0x01, +}; + enum hp_thermal_profile { HP_THERMAL_PROFILE_PERFORMANCE = 0x00, HP_THERMAL_PROFILE_DEFAULT = 0x01, @@ -273,7 +307,7 @@ static DEFINE_MUTEX(active_platform_profile_lock); static struct input_dev *hp_wmi_input_dev; static struct input_dev *camera_shutter_input_dev; static struct platform_device *hp_wmi_platform_dev; -static struct platform_profile_handler platform_profile_handler; +static struct device *platform_profile_device; static struct notifier_block platform_power_source_nb; static enum platform_profile_option active_platform_profile; static bool platform_profile_support; @@ -411,6 +445,26 @@ out_free: return ret; } +/* + * Calling this hp_wmi_get_fan_count_userdefine_trigger function also enables + * and/or maintains the laptop in user defined thermal and fan states, instead + * of using a fallback state. After a 120 seconds timeout however, the laptop + * goes back to its fallback state. + */ +static int hp_wmi_get_fan_count_userdefine_trigger(void) +{ + u8 fan_data[4] = {}; + int ret; + + ret = hp_wmi_perform_query(HPWMI_FAN_COUNT_GET_QUERY, HPWMI_GM, + &fan_data, sizeof(u8), + sizeof(fan_data)); + if (ret != 0) + return -EINVAL; + + return fan_data[0]; /* Others bytes aren't providing fan count */ +} + static int hp_wmi_get_fan_speed(int fan) { u8 fsh, fsl; @@ -429,6 +483,23 @@ static int hp_wmi_get_fan_speed(int fan) return (fsh << 8) | fsl; } +static int hp_wmi_get_fan_speed_victus_s(int fan) +{ + u8 fan_data[128] = {}; + int ret; + + if (fan < 0 || fan >= sizeof(fan_data)) + return -EINVAL; + + ret = hp_wmi_perform_query(HPWMI_VICTUS_S_FAN_SPEED_GET_QUERY, + HPWMI_GM, &fan_data, sizeof(u8), + sizeof(fan_data)); + if (ret != 0) + return -EINVAL; + + return fan_data[fan] * 100; +} + static int hp_wmi_read_int(int query) { int val = 0, ret; @@ -557,6 +628,30 @@ static int hp_wmi_fan_speed_max_set(int enabled) return enabled; } +static int hp_wmi_fan_speed_reset(void) +{ + u8 fan_speed[2] = { HP_FAN_SPEED_AUTOMATIC, HP_FAN_SPEED_AUTOMATIC }; + int ret; + + ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_SET_QUERY, HPWMI_GM, + &fan_speed, sizeof(fan_speed), 0); + + return ret; +} + +static int hp_wmi_fan_speed_max_reset(void) +{ + int ret; + + ret = hp_wmi_fan_speed_max_set(0); + if (ret) + return ret; + + /* Disabling max fan speed on Victus s1xxx laptops needs a 2nd step: */ + ret = hp_wmi_fan_speed_reset(); + return ret; +} + static int hp_wmi_fan_speed_max_get(void) { int val = 0, ret; @@ -1221,7 +1316,7 @@ static int platform_profile_omen_get_ec(enum platform_profile_option *profile) return 0; } -static int platform_profile_omen_get(struct platform_profile_handler *pprof, +static int platform_profile_omen_get(struct device *dev, enum platform_profile_option *profile) { /* @@ -1318,7 +1413,7 @@ static int platform_profile_omen_set_ec(enum platform_profile_option profile) return 0; } -static int platform_profile_omen_set(struct platform_profile_handler *pprof, +static int platform_profile_omen_set(struct device *dev, enum platform_profile_option profile) { int err; @@ -1345,7 +1440,7 @@ static int thermal_profile_set(int thermal_profile) sizeof(thermal_profile), 0); } -static int hp_wmi_platform_profile_get(struct platform_profile_handler *pprof, +static int hp_wmi_platform_profile_get(struct device *dev, enum platform_profile_option *profile) { int tp; @@ -1374,7 +1469,7 @@ static int hp_wmi_platform_profile_get(struct platform_profile_handler *pprof, return 0; } -static int hp_wmi_platform_profile_set(struct platform_profile_handler *pprof, +static int hp_wmi_platform_profile_set(struct device *dev, enum platform_profile_option profile) { int err, tp; @@ -1440,11 +1535,11 @@ static int platform_profile_victus_get_ec(enum platform_profile_option *profile) return 0; } -static int platform_profile_victus_get(struct platform_profile_handler *pprof, +static int platform_profile_victus_get(struct device *dev, enum platform_profile_option *profile) { /* Same behaviour as platform_profile_omen_get */ - return platform_profile_omen_get(pprof, profile); + return platform_profile_omen_get(dev, profile); } static int platform_profile_victus_set_ec(enum platform_profile_option profile) @@ -1472,7 +1567,162 @@ static int platform_profile_victus_set_ec(enum platform_profile_option profile) return 0; } -static int platform_profile_victus_set(struct platform_profile_handler *pprof, +static bool is_victus_s_thermal_profile(void) +{ + const char *board_name; + + board_name = dmi_get_system_info(DMI_BOARD_NAME); + if (!board_name) + return false; + + return match_string(victus_s_thermal_profile_boards, + ARRAY_SIZE(victus_s_thermal_profile_boards), + board_name) >= 0; +} + +static int victus_s_gpu_thermal_profile_get(bool *ctgp_enable, + bool *ppab_enable, + u8 *dstate, + u8 *gpu_slowdown_temp) +{ + struct victus_gpu_power_modes gpu_power_modes; + int ret; + + ret = hp_wmi_perform_query(HPWMI_GET_GPU_THERMAL_MODES_QUERY, HPWMI_GM, + &gpu_power_modes, sizeof(gpu_power_modes), + sizeof(gpu_power_modes)); + if (ret == 0) { + *ctgp_enable = gpu_power_modes.ctgp_enable ? true : false; + *ppab_enable = gpu_power_modes.ppab_enable ? true : false; + *dstate = gpu_power_modes.dstate; + *gpu_slowdown_temp = gpu_power_modes.gpu_slowdown_temp; + } + + return ret; +} + +static int victus_s_gpu_thermal_profile_set(bool ctgp_enable, + bool ppab_enable, + u8 dstate) +{ + struct victus_gpu_power_modes gpu_power_modes; + int ret; + + bool current_ctgp_state, current_ppab_state; + u8 current_dstate, current_gpu_slowdown_temp; + + /* Retrieving GPU slowdown temperature, in order to keep it unchanged */ + ret = victus_s_gpu_thermal_profile_get(¤t_ctgp_state, + ¤t_ppab_state, + ¤t_dstate, + ¤t_gpu_slowdown_temp); + if (ret < 0) { + pr_warn("GPU modes not updated, unable to get slowdown temp\n"); + return ret; + } + + gpu_power_modes.ctgp_enable = ctgp_enable ? 0x01 : 0x00; + gpu_power_modes.ppab_enable = ppab_enable ? 0x01 : 0x00; + gpu_power_modes.dstate = dstate; + gpu_power_modes.gpu_slowdown_temp = current_gpu_slowdown_temp; + + + ret = hp_wmi_perform_query(HPWMI_SET_GPU_THERMAL_MODES_QUERY, HPWMI_GM, + &gpu_power_modes, sizeof(gpu_power_modes), 0); + + return ret; +} + +/* Note: HP_POWER_LIMIT_DEFAULT can be used to restore default PL1 and PL2 */ +static int victus_s_set_cpu_pl1_pl2(u8 pl1, u8 pl2) +{ + struct victus_power_limits power_limits; + int ret; + + /* We need to know both PL1 and PL2 values in order to check them */ + if (pl1 == HP_POWER_LIMIT_NO_CHANGE || pl2 == HP_POWER_LIMIT_NO_CHANGE) + return -EINVAL; + + /* PL2 is not supposed to be lower than PL1 */ + if (pl2 < pl1) + return -EINVAL; + + power_limits.pl1 = pl1; + power_limits.pl2 = pl2; + power_limits.pl4 = HP_POWER_LIMIT_NO_CHANGE; + power_limits.cpu_gpu_concurrent_limit = HP_POWER_LIMIT_NO_CHANGE; + + ret = hp_wmi_perform_query(HPWMI_SET_POWER_LIMITS_QUERY, HPWMI_GM, + &power_limits, sizeof(power_limits), 0); + + return ret; +} + +static int platform_profile_victus_s_set_ec(enum platform_profile_option profile) +{ + bool gpu_ctgp_enable, gpu_ppab_enable; + u8 gpu_dstate; /* Test shows 1 = 100%, 2 = 50%, 3 = 25%, 4 = 12.5% */ + int err, tp; + + switch (profile) { + case PLATFORM_PROFILE_PERFORMANCE: + tp = HP_VICTUS_S_THERMAL_PROFILE_PERFORMANCE; + gpu_ctgp_enable = true; + gpu_ppab_enable = true; + gpu_dstate = 1; + break; + case PLATFORM_PROFILE_BALANCED: + tp = HP_VICTUS_S_THERMAL_PROFILE_DEFAULT; + gpu_ctgp_enable = false; + gpu_ppab_enable = true; + gpu_dstate = 1; + break; + case PLATFORM_PROFILE_LOW_POWER: + tp = HP_VICTUS_S_THERMAL_PROFILE_DEFAULT; + gpu_ctgp_enable = false; + gpu_ppab_enable = false; + gpu_dstate = 1; + break; + default: + return -EOPNOTSUPP; + } + + hp_wmi_get_fan_count_userdefine_trigger(); + + err = omen_thermal_profile_set(tp); + if (err < 0) { + pr_err("Failed to set platform profile %d: %d\n", profile, err); + return err; + } + + err = victus_s_gpu_thermal_profile_set(gpu_ctgp_enable, + gpu_ppab_enable, + gpu_dstate); + if (err < 0) { + pr_err("Failed to set GPU profile %d: %d\n", profile, err); + return err; + } + + return 0; +} + +static int platform_profile_victus_s_set(struct device *dev, + enum platform_profile_option profile) +{ + int err; + + guard(mutex)(&active_platform_profile_lock); + + err = platform_profile_victus_s_set_ec(profile); + if (err < 0) + return err; + + active_platform_profile = profile; + + return 0; +} + +static int platform_profile_victus_set(struct device *dev, enum platform_profile_option profile) { int err; @@ -1488,6 +1738,26 @@ static int platform_profile_victus_set(struct platform_profile_handler *pprof, return 0; } +static int hp_wmi_platform_profile_probe(void *drvdata, unsigned long *choices) +{ + if (is_omen_thermal_profile()) { + set_bit(PLATFORM_PROFILE_COOL, choices); + } else if (is_victus_thermal_profile()) { + set_bit(PLATFORM_PROFILE_QUIET, choices); + } else if (is_victus_s_thermal_profile()) { + /* Adding an equivalent to HP Omen software ECO mode: */ + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); + } else { + set_bit(PLATFORM_PROFILE_QUIET, choices); + set_bit(PLATFORM_PROFILE_COOL, choices); + } + + set_bit(PLATFORM_PROFILE_BALANCED, choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + + return 0; +} + static int omen_powersource_event(struct notifier_block *nb, unsigned long value, void *data) @@ -1545,6 +1815,39 @@ static int omen_powersource_event(struct notifier_block *nb, return NOTIFY_OK; } +static int victus_s_powersource_event(struct notifier_block *nb, + unsigned long value, + void *data) +{ + struct acpi_bus_event *event_entry = data; + int err; + + if (strcmp(event_entry->device_class, ACPI_AC_CLASS) != 0) + return NOTIFY_DONE; + + pr_debug("Received power source device event\n"); + + /* + * Switching to battery power source while Performance mode is active + * needs manual triggering of CPU power limits. Same goes when switching + * to AC power source while Performance mode is active. Other modes + * however are automatically behaving without any manual action. + * Seen on HP 16-s1034nf (board 8C9C) with F.11 and F.13 BIOS versions. + */ + + if (active_platform_profile == PLATFORM_PROFILE_PERFORMANCE) { + pr_debug("Triggering CPU PL1/PL2 actualization\n"); + err = victus_s_set_cpu_pl1_pl2(HP_POWER_LIMIT_DEFAULT, + HP_POWER_LIMIT_DEFAULT); + if (err) + pr_warn("Failed to actualize power limits: %d\n", err); + + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + static int omen_register_powersource_event_handler(void) { int err; @@ -1560,13 +1863,57 @@ static int omen_register_powersource_event_handler(void) return 0; } +static int victus_s_register_powersource_event_handler(void) +{ + int err; + + platform_power_source_nb.notifier_call = victus_s_powersource_event; + err = register_acpi_notifier(&platform_power_source_nb); + if (err < 0) { + pr_warn("Failed to install ACPI power source notify handler\n"); + return err; + } + + return 0; +} + static inline void omen_unregister_powersource_event_handler(void) { unregister_acpi_notifier(&platform_power_source_nb); } -static int thermal_profile_setup(void) +static inline void victus_s_unregister_powersource_event_handler(void) { + unregister_acpi_notifier(&platform_power_source_nb); +} + +static const struct platform_profile_ops platform_profile_omen_ops = { + .probe = hp_wmi_platform_profile_probe, + .profile_get = platform_profile_omen_get, + .profile_set = platform_profile_omen_set, +}; + +static const struct platform_profile_ops platform_profile_victus_ops = { + .probe = hp_wmi_platform_profile_probe, + .profile_get = platform_profile_victus_get, + .profile_set = platform_profile_victus_set, +}; + +static const struct platform_profile_ops platform_profile_victus_s_ops = { + .probe = hp_wmi_platform_profile_probe, + .profile_get = platform_profile_omen_get, + .profile_set = platform_profile_victus_s_set, +}; + +static const struct platform_profile_ops hp_wmi_platform_profile_ops = { + .probe = hp_wmi_platform_profile_probe, + .profile_get = hp_wmi_platform_profile_get, + .profile_set = hp_wmi_platform_profile_set, +}; + +static int thermal_profile_setup(struct platform_device *device) +{ + const struct platform_profile_ops *ops; int err, tp; if (is_omen_thermal_profile()) { @@ -1582,10 +1929,7 @@ static int thermal_profile_setup(void) if (err < 0) return err; - platform_profile_handler.profile_get = platform_profile_omen_get; - platform_profile_handler.profile_set = platform_profile_omen_set; - - set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices); + ops = &platform_profile_omen_ops; } else if (is_victus_thermal_profile()) { err = platform_profile_victus_get_ec(&active_platform_profile); if (err < 0) @@ -1599,10 +1943,19 @@ static int thermal_profile_setup(void) if (err < 0) return err; - platform_profile_handler.profile_get = platform_profile_victus_get; - platform_profile_handler.profile_set = platform_profile_victus_set; + ops = &platform_profile_victus_ops; + } else if (is_victus_s_thermal_profile()) { + /* + * Being unable to retrieve laptop's current thermal profile, + * during this setup, we set it to Balanced by default. + */ + active_platform_profile = PLATFORM_PROFILE_BALANCED; + + err = platform_profile_victus_s_set_ec(active_platform_profile); + if (err < 0) + return err; - set_bit(PLATFORM_PROFILE_QUIET, platform_profile_handler.choices); + ops = &platform_profile_victus_s_ops; } else { tp = thermal_profile_get(); @@ -1617,20 +1970,15 @@ static int thermal_profile_setup(void) if (err) return err; - platform_profile_handler.profile_get = hp_wmi_platform_profile_get; - platform_profile_handler.profile_set = hp_wmi_platform_profile_set; - - set_bit(PLATFORM_PROFILE_QUIET, platform_profile_handler.choices); - set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices); + ops = &hp_wmi_platform_profile_ops; } - set_bit(PLATFORM_PROFILE_BALANCED, platform_profile_handler.choices); - set_bit(PLATFORM_PROFILE_PERFORMANCE, platform_profile_handler.choices); - - err = platform_profile_register(&platform_profile_handler); - if (err) - return err; + platform_profile_device = devm_platform_profile_register(&device->dev, "hp-wmi", + NULL, ops); + if (IS_ERR(platform_profile_device)) + return PTR_ERR(platform_profile_device); + pr_info("Registered as platform profile handler\n"); platform_profile_support = true; return 0; @@ -1663,7 +2011,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) if (err < 0) return err; - thermal_profile_setup(); + thermal_profile_setup(device); return 0; } @@ -1689,9 +2037,6 @@ static void __exit hp_wmi_bios_remove(struct platform_device *device) rfkill_unregister(wwan_rfkill); rfkill_destroy(wwan_rfkill); } - - if (platform_profile_support) - platform_profile_remove(); } static int hp_wmi_resume_handler(struct device *device) @@ -1759,8 +2104,13 @@ static umode_t hp_wmi_hwmon_is_visible(const void *data, case hwmon_pwm: return 0644; case hwmon_fan: - if (hp_wmi_get_fan_speed(channel) >= 0) - return 0444; + if (is_victus_s_thermal_profile()) { + if (hp_wmi_get_fan_speed_victus_s(channel) >= 0) + return 0444; + } else { + if (hp_wmi_get_fan_speed(channel) >= 0) + return 0444; + } break; default: return 0; @@ -1776,8 +2126,10 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, switch (type) { case hwmon_fan: - ret = hp_wmi_get_fan_speed(channel); - + if (is_victus_s_thermal_profile()) + ret = hp_wmi_get_fan_speed_victus_s(channel); + else + ret = hp_wmi_get_fan_speed(channel); if (ret < 0) return ret; *val = ret; @@ -1810,11 +2162,17 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, case hwmon_pwm: switch (val) { case 0: + if (is_victus_s_thermal_profile()) + hp_wmi_get_fan_count_userdefine_trigger(); /* 0 is no fan speed control (max), which is 1 for us */ return hp_wmi_fan_speed_max_set(1); case 2: /* 2 is automatic speed control, which is 0 for us */ - return hp_wmi_fan_speed_max_set(0); + if (is_victus_s_thermal_profile()) { + hp_wmi_get_fan_count_userdefine_trigger(); + return hp_wmi_fan_speed_max_reset(); + } else + return hp_wmi_fan_speed_max_set(0); default: /* we don't support manual fan speed control */ return -EINVAL; @@ -1893,6 +2251,10 @@ static int __init hp_wmi_init(void) err = omen_register_powersource_event_handler(); if (err) goto err_unregister_device; + } else if (is_victus_s_thermal_profile()) { + err = victus_s_register_powersource_event_handler(); + if (err) + goto err_unregister_device; } return 0; @@ -1912,6 +2274,9 @@ static void __exit hp_wmi_exit(void) if (is_omen_thermal_profile() || is_victus_thermal_profile()) omen_unregister_powersource_event_handler(); + if (is_victus_s_thermal_profile()) + victus_s_unregister_powersource_event_handler(); + if (wmi_has_guid(HPWMI_EVENT_GUID)) hp_wmi_input_destroy(); diff --git a/drivers/platform/x86/hp/hp_accel.c b/drivers/platform/x86/hp/hp_accel.c index 39a6530f5072..10d5af18d639 100644 --- a/drivers/platform/x86/hp/hp_accel.c +++ b/drivers/platform/x86/hp/hp_accel.c @@ -267,7 +267,7 @@ static struct delayed_led_classdev hpled_led = { }; static bool hp_accel_i8042_filter(unsigned char data, unsigned char str, - struct serio *port) + struct serio *port, void *context) { static bool extended; @@ -326,7 +326,7 @@ static int lis3lv02d_probe(struct platform_device *device) /* filter to remove HPQ6000 accelerometer data * from keyboard bus stream */ if (strstr(dev_name(&device->dev), "HPQ6000")) - i8042_install_filter(hp_accel_i8042_filter); + i8042_install_filter(hp_accel_i8042_filter, NULL); INIT_WORK(&hpled_led.work, delayed_set_status_worker); ret = led_classdev_register(NULL, &hpled_led.led_classdev); diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index e980dd18e5f6..30bd366d7b58 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -142,7 +142,7 @@ enum { struct ideapad_dytc_priv { enum platform_profile_option current_profile; - struct platform_profile_handler pprof; + struct device *ppdev; /* platform profile device */ struct mutex mutex; /* protects the DYTC interface */ struct ideapad_private *priv; }; @@ -933,10 +933,10 @@ static int convert_profile_to_dytc(enum platform_profile_option profile, int *pe * dytc_profile_get: Function to register with platform_profile * handler. Returns current platform profile. */ -static int dytc_profile_get(struct platform_profile_handler *pprof, +static int dytc_profile_get(struct device *dev, enum platform_profile_option *profile) { - struct ideapad_dytc_priv *dytc = container_of(pprof, struct ideapad_dytc_priv, pprof); + struct ideapad_dytc_priv *dytc = dev_get_drvdata(dev); *profile = dytc->current_profile; return 0; @@ -986,10 +986,10 @@ static int dytc_cql_command(struct ideapad_private *priv, unsigned long cmd, * dytc_profile_set: Function to register with platform_profile * handler. Sets current platform profile. */ -static int dytc_profile_set(struct platform_profile_handler *pprof, +static int dytc_profile_set(struct device *dev, enum platform_profile_option profile) { - struct ideapad_dytc_priv *dytc = container_of(pprof, struct ideapad_dytc_priv, pprof); + struct ideapad_dytc_priv *dytc = dev_get_drvdata(dev); struct ideapad_private *priv = dytc->priv; unsigned long output; int err; @@ -1023,6 +1023,15 @@ static int dytc_profile_set(struct platform_profile_handler *pprof, return -EINTR; } +static int dytc_profile_probe(void *drvdata, unsigned long *choices) +{ + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); + set_bit(PLATFORM_PROFILE_BALANCED, choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + + return 0; +} + static void dytc_profile_refresh(struct ideapad_private *priv) { enum platform_profile_option profile; @@ -1041,7 +1050,7 @@ static void dytc_profile_refresh(struct ideapad_private *priv) if (profile != priv->dytc->current_profile) { priv->dytc->current_profile = profile; - platform_profile_notify(); + platform_profile_notify(priv->dytc->ppdev); } } @@ -1063,6 +1072,12 @@ static const struct dmi_system_id ideapad_dytc_v4_allow_table[] = { {} }; +static const struct platform_profile_ops dytc_profile_ops = { + .probe = dytc_profile_probe, + .profile_get = dytc_profile_get, + .profile_set = dytc_profile_set, +}; + static int ideapad_dytc_profile_init(struct ideapad_private *priv) { int err, dytc_version; @@ -1103,18 +1118,15 @@ static int ideapad_dytc_profile_init(struct ideapad_private *priv) mutex_init(&priv->dytc->mutex); priv->dytc->priv = priv; - priv->dytc->pprof.profile_get = dytc_profile_get; - priv->dytc->pprof.profile_set = dytc_profile_set; - - /* Setup supported modes */ - set_bit(PLATFORM_PROFILE_LOW_POWER, priv->dytc->pprof.choices); - set_bit(PLATFORM_PROFILE_BALANCED, priv->dytc->pprof.choices); - set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->dytc->pprof.choices); /* Create platform_profile structure and register */ - err = platform_profile_register(&priv->dytc->pprof); - if (err) + priv->dytc->ppdev = devm_platform_profile_register(&priv->platform_device->dev, + "ideapad-laptop", priv->dytc, + &dytc_profile_ops); + if (IS_ERR(priv->dytc->ppdev)) { + err = PTR_ERR(priv->dytc->ppdev); goto pp_reg_failed; + } /* Ensure initial values are correct */ dytc_profile_refresh(priv); @@ -1134,7 +1146,6 @@ static void ideapad_dytc_profile_exit(struct ideapad_private *priv) if (!priv->dytc) return; - platform_profile_remove(); mutex_destroy(&priv->dytc->mutex); kfree(priv->dytc); diff --git a/drivers/platform/x86/inspur_platform_profile.c b/drivers/platform/x86/inspur_platform_profile.c index 8440defa6788..e02f5a55a6c5 100644 --- a/drivers/platform/x86/inspur_platform_profile.c +++ b/drivers/platform/x86/inspur_platform_profile.c @@ -32,7 +32,7 @@ enum inspur_tmp_profile { struct inspur_wmi_priv { struct wmi_device *wdev; - struct platform_profile_handler handler; + struct device *ppdev; }; static int inspur_wmi_perform_query(struct wmi_device *wdev, @@ -84,11 +84,10 @@ out_free: * 0x0: No Error * 0x1: Error */ -static int inspur_platform_profile_set(struct platform_profile_handler *pprof, +static int inspur_platform_profile_set(struct device *dev, enum platform_profile_option profile) { - struct inspur_wmi_priv *priv = container_of(pprof, struct inspur_wmi_priv, - handler); + struct inspur_wmi_priv *priv = dev_get_drvdata(dev); u8 ret_code[4] = {0, 0, 0, 0}; int ret; @@ -132,11 +131,10 @@ static int inspur_platform_profile_set(struct platform_profile_handler *pprof, * 0x1: Performance Mode * 0x2: Power Saver Mode */ -static int inspur_platform_profile_get(struct platform_profile_handler *pprof, +static int inspur_platform_profile_get(struct device *dev, enum platform_profile_option *profile) { - struct inspur_wmi_priv *priv = container_of(pprof, struct inspur_wmi_priv, - handler); + struct inspur_wmi_priv *priv = dev_get_drvdata(dev); u8 ret_code[4] = {0, 0, 0, 0}; int ret; @@ -166,6 +164,21 @@ static int inspur_platform_profile_get(struct platform_profile_handler *pprof, return 0; } +static int inspur_platform_profile_probe(void *drvdata, unsigned long *choices) +{ + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); + set_bit(PLATFORM_PROFILE_BALANCED, choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + + return 0; +} + +static const struct platform_profile_ops inspur_platform_profile_ops = { + .probe = inspur_platform_profile_probe, + .profile_get = inspur_platform_profile_get, + .profile_set = inspur_platform_profile_set, +}; + static int inspur_wmi_probe(struct wmi_device *wdev, const void *context) { struct inspur_wmi_priv *priv; @@ -177,19 +190,10 @@ static int inspur_wmi_probe(struct wmi_device *wdev, const void *context) priv->wdev = wdev; dev_set_drvdata(&wdev->dev, priv); - priv->handler.profile_get = inspur_platform_profile_get; - priv->handler.profile_set = inspur_platform_profile_set; - - set_bit(PLATFORM_PROFILE_LOW_POWER, priv->handler.choices); - set_bit(PLATFORM_PROFILE_BALANCED, priv->handler.choices); - set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->handler.choices); + priv->ppdev = devm_platform_profile_register(&wdev->dev, "inspur-wmi", priv, + &inspur_platform_profile_ops); - return platform_profile_register(&priv->handler); -} - -static void inspur_wmi_remove(struct wmi_device *wdev) -{ - platform_profile_remove(); + return PTR_ERR_OR_ZERO(priv->ppdev); } static const struct wmi_device_id inspur_wmi_id_table[] = { @@ -206,7 +210,6 @@ static struct wmi_driver inspur_wmi_driver = { }, .id_table = inspur_wmi_id_table, .probe = inspur_wmi_probe, - .remove = inspur_wmi_remove, .no_singleton = true, }; diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig index eb698dcb9af9..19a2246f2770 100644 --- a/drivers/platform/x86/intel/Kconfig +++ b/drivers/platform/x86/intel/Kconfig @@ -83,6 +83,7 @@ config INTEL_BXTWC_PMIC_TMU config INTEL_BYTCRC_PWRSRC tristate "Intel Bay Trail Crystal Cove power source driver" depends on INTEL_SOC_PMIC + depends on POWER_SUPPLY help This option adds a power source driver for Crystal Cove PMICs on Intel Bay Trail devices. diff --git a/drivers/platform/x86/intel/bytcrc_pwrsrc.c b/drivers/platform/x86/intel/bytcrc_pwrsrc.c index 3edc2a9dab38..68ac040082df 100644 --- a/drivers/platform/x86/intel/bytcrc_pwrsrc.c +++ b/drivers/platform/x86/intel/bytcrc_pwrsrc.c @@ -8,13 +8,22 @@ * Copyright (C) 2013 Intel Corporation */ +#include <linux/array_size.h> +#include <linux/bits.h> #include <linux/debugfs.h> +#include <linux/interrupt.h> #include <linux/mfd/intel_soc_pmic.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/property.h> #include <linux/regmap.h> +#define CRYSTALCOVE_PWRSRC_IRQ 0x03 #define CRYSTALCOVE_SPWRSRC_REG 0x1E +#define CRYSTALCOVE_SPWRSRC_USB BIT(0) +#define CRYSTALCOVE_SPWRSRC_DC BIT(1) +#define CRYSTALCOVE_SPWRSRC_BATTERY BIT(2) #define CRYSTALCOVE_RESETSRC0_REG 0x20 #define CRYSTALCOVE_RESETSRC1_REG 0x21 #define CRYSTALCOVE_WAKESRC_REG 0x22 @@ -22,6 +31,7 @@ struct crc_pwrsrc_data { struct regmap *regmap; struct dentry *debug_dentry; + struct power_supply *psy; unsigned int resetsrc0; unsigned int resetsrc1; unsigned int wakesrc; @@ -118,13 +128,60 @@ static int crc_pwrsrc_read_and_clear(struct crc_pwrsrc_data *data, return regmap_write(data->regmap, reg, *val); } +static irqreturn_t crc_pwrsrc_irq_handler(int irq, void *_data) +{ + struct crc_pwrsrc_data *data = _data; + unsigned int irq_mask; + + if (regmap_read(data->regmap, CRYSTALCOVE_PWRSRC_IRQ, &irq_mask)) + return IRQ_NONE; + + regmap_write(data->regmap, CRYSTALCOVE_PWRSRC_IRQ, irq_mask); + + power_supply_changed(data->psy); + return IRQ_HANDLED; +} + +static int crc_pwrsrc_psy_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct crc_pwrsrc_data *data = power_supply_get_drvdata(psy); + unsigned int pwrsrc; + int ret; + + if (psp != POWER_SUPPLY_PROP_ONLINE) + return -EINVAL; + + ret = regmap_read(data->regmap, CRYSTALCOVE_SPWRSRC_REG, &pwrsrc); + if (ret) + return ret; + + val->intval = !!(pwrsrc & (CRYSTALCOVE_SPWRSRC_USB | + CRYSTALCOVE_SPWRSRC_DC)); + return 0; +} + +static const enum power_supply_property crc_pwrsrc_psy_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static const struct power_supply_desc crc_pwrsrc_psy_desc = { + .name = "crystal_cove_pwrsrc", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = crc_pwrsrc_psy_props, + .num_properties = ARRAY_SIZE(crc_pwrsrc_psy_props), + .get_property = crc_pwrsrc_psy_get_property, +}; + static int crc_pwrsrc_probe(struct platform_device *pdev) { struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; struct crc_pwrsrc_data *data; - int ret; + int irq, ret; - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -149,6 +206,24 @@ static int crc_pwrsrc_probe(struct platform_device *pdev) if (ret) return ret; + if (device_property_read_bool(dev->parent, "linux,register-pwrsrc-power_supply")) { + struct power_supply_config psy_cfg = { .drv_data = data }; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + data->psy = devm_power_supply_register(dev, &crc_pwrsrc_psy_desc, &psy_cfg); + if (IS_ERR(data->psy)) + return dev_err_probe(dev, PTR_ERR(data->psy), "registering power-supply\n"); + + ret = devm_request_threaded_irq(dev, irq, NULL, + crc_pwrsrc_irq_handler, + IRQF_ONESHOT, KBUILD_MODNAME, data); + if (ret) + return dev_err_probe(dev, ret, "requesting IRQ\n"); + } + data->debug_dentry = debugfs_create_dir(KBUILD_MODNAME, NULL); debugfs_create_file("pwrsrc", 0444, data->debug_dentry, data, &pwrsrc_fops); debugfs_create_file("resetsrc", 0444, data->debug_dentry, data, &resetsrc_fops); diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c index 927a2993f616..88a1a9ff2f34 100644 --- a/drivers/platform/x86/intel/hid.c +++ b/drivers/platform/x86/intel/hid.c @@ -139,6 +139,13 @@ static const struct dmi_system_id button_array_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go 3"), }, }, + { + .ident = "Microsoft Surface Go 4", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go 4"), + }, + }, { } }; diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index 5c3c0dfa1bf8..f369fb0d3d82 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -23,12 +23,14 @@ * IFS Image * --------- * - * Intel provides a firmware file containing the scan tests via - * github [#f1]_. Similar to microcode there is a separate file for each + * Intel provides firmware files containing the scan tests via the webpage [#f1]_. + * Look under "In-Field Scan Test Images Download" section towards the + * end of the page. Similar to microcode, there are separate files for each * family-model-stepping. IFS Images are not applicable for some test types. * Wherever applicable the sysfs directory would provide a "current_batch" file * (see below) for loading the image. * + * .. [#f1] https://intel.com/InFieldScan * * IFS Image Loading * ----------------- @@ -125,9 +127,6 @@ * 2) Hardware allows for some number of cores to be tested in parallel. * The driver does not make use of this, it only tests one core at a time. * - * .. [#f1] https://github.com/intel/TBD - * - * * Structural Based Functional Test at Field (SBAF): * ------------------------------------------------- * diff --git a/drivers/platform/x86/intel/int0002_vgpio.c b/drivers/platform/x86/intel/int0002_vgpio.c index 0cc80603a8a9..3b48cd7a4075 100644 --- a/drivers/platform/x86/intel/int0002_vgpio.c +++ b/drivers/platform/x86/intel/int0002_vgpio.c @@ -83,8 +83,12 @@ static void int0002_irq_ack(struct irq_data *data) static void int0002_irq_unmask(struct irq_data *data) { + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + irq_hw_number_t hwirq = irqd_to_hwirq(data); u32 gpe_en_reg; + gpiochip_enable_irq(gc, hwirq); + gpe_en_reg = inl(GPE0A_EN_PORT); gpe_en_reg |= GPE0A_PME_B0_EN_BIT; outl(gpe_en_reg, GPE0A_EN_PORT); @@ -92,11 +96,15 @@ static void int0002_irq_unmask(struct irq_data *data) static void int0002_irq_mask(struct irq_data *data) { + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + irq_hw_number_t hwirq = irqd_to_hwirq(data); u32 gpe_en_reg; gpe_en_reg = inl(GPE0A_EN_PORT); gpe_en_reg &= ~GPE0A_PME_B0_EN_BIT; outl(gpe_en_reg, GPE0A_EN_PORT); + + gpiochip_disable_irq(gc, hwirq); } static int int0002_irq_set_wake(struct irq_data *data, unsigned int on) @@ -140,12 +148,14 @@ static bool int0002_check_wake(void *data) return (gpe_sts_reg & GPE0A_PME_B0_STS_BIT); } -static struct irq_chip int0002_irqchip = { +static const struct irq_chip int0002_irqchip = { .name = DRV_NAME, .irq_ack = int0002_irq_ack, .irq_mask = int0002_irq_mask, .irq_unmask = int0002_irq_unmask, .irq_set_wake = int0002_irq_set_wake, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static void int0002_init_irq_valid_mask(struct gpio_chip *chip, @@ -203,7 +213,7 @@ static int int0002_probe(struct platform_device *pdev) } girq = &chip->irq; - girq->chip = &int0002_irqchip; + gpio_irq_chip_set_chip(girq, &int0002_irqchip); /* This let us handle the parent IRQ in the driver */ girq->parent_handler = NULL; girq->num_parents = 0; diff --git a/drivers/platform/x86/intel/int3472/common.c b/drivers/platform/x86/intel/int3472/common.c index b3a2578e06c1..1638be8fa71e 100644 --- a/drivers/platform/x86/intel/int3472/common.c +++ b/drivers/platform/x86/intel/int3472/common.c @@ -70,6 +70,8 @@ int skl_int3472_get_sensor_adev_and_name(struct device *dev, return -ENODEV; } + dev_dbg(dev, "Sensor name %s\n", acpi_dev_name(sensor)); + *name_ret = devm_kasprintf(dev, GFP_KERNEL, I2C_DEV_NAME_FORMAT, acpi_dev_name(sensor)); if (!*name_ret) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 09fff213b091..092252eb95a8 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -2,6 +2,7 @@ /* Author: Dan Scally <djrscally@gmail.com> */ #include <linux/acpi.h> +#include <linux/array_size.h> #include <linux/bitfield.h> #include <linux/device.h> #include <linux/gpio/consumer.h> @@ -55,7 +56,7 @@ static void skl_int3472_log_sensor_module_name(struct int3472_discrete_device *i static int skl_int3472_fill_gpiod_lookup(struct gpiod_lookup *table_entry, struct acpi_resource_gpio *agpio, - const char *func, u32 polarity) + const char *func, unsigned long gpio_flags) { char *path = agpio->resource_source.string_ptr; struct acpi_device *adev; @@ -70,14 +71,14 @@ static int skl_int3472_fill_gpiod_lookup(struct gpiod_lookup *table_entry, if (!adev) return -ENODEV; - *table_entry = GPIO_LOOKUP(acpi_dev_name(adev), agpio->pin_table[0], func, polarity); + *table_entry = GPIO_LOOKUP(acpi_dev_name(adev), agpio->pin_table[0], func, gpio_flags); return 0; } static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int3472, struct acpi_resource_gpio *agpio, - const char *func, u32 polarity) + const char *func, unsigned long gpio_flags) { int ret; @@ -87,7 +88,7 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347 } ret = skl_int3472_fill_gpiod_lookup(&int3472->gpios.table[int3472->n_sensor_gpios], - agpio, func, polarity); + agpio, func, gpio_flags); if (ret) return ret; @@ -100,7 +101,7 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347 static struct gpio_desc * skl_int3472_gpiod_get_from_temp_lookup(struct int3472_discrete_device *int3472, struct acpi_resource_gpio *agpio, - const char *func, u32 polarity) + const char *func, unsigned long gpio_flags) { struct gpio_desc *desc; int ret; @@ -111,7 +112,7 @@ skl_int3472_gpiod_get_from_temp_lookup(struct int3472_discrete_device *int3472, return ERR_PTR(-ENOMEM); lookup->dev_id = dev_name(int3472->dev); - ret = skl_int3472_fill_gpiod_lookup(&lookup->table[0], agpio, func, polarity); + ret = skl_int3472_fill_gpiod_lookup(&lookup->table[0], agpio, func, gpio_flags); if (ret) return ERR_PTR(ret); @@ -122,32 +123,76 @@ skl_int3472_gpiod_get_from_temp_lookup(struct int3472_discrete_device *int3472, return desc; } -static void int3472_get_func_and_polarity(u8 type, const char **func, u32 *polarity) +/** + * struct int3472_gpio_map - Map GPIOs to whatever is expected by the + * sensor driver (as in DT bindings) + * @hid: The ACPI HID of the device without the instance number e.g. INT347E + * @type_from: The GPIO type from ACPI ?SDT + * @type_to: The assigned GPIO type, typically same as @type_from + * @func: The function, e.g. "enable" + * @polarity_low: GPIO_ACTIVE_LOW true if the @polarity_low is true, + * GPIO_ACTIVE_HIGH otherwise + */ +struct int3472_gpio_map { + const char *hid; + u8 type_from; + u8 type_to; + bool polarity_low; + const char *func; +}; + +static const struct int3472_gpio_map int3472_gpio_map[] = { + { "INT347E", INT3472_GPIO_TYPE_RESET, INT3472_GPIO_TYPE_RESET, false, "enable" }, +}; + +static void int3472_get_func_and_polarity(struct acpi_device *adev, u8 *type, + const char **func, unsigned long *gpio_flags) { - switch (type) { + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(int3472_gpio_map); i++) { + /* + * Map the firmware-provided GPIO to whatever a driver expects + * (as in DT bindings). First check if the type matches with the + * GPIO map, then further check that the device _HID matches. + */ + if (*type != int3472_gpio_map[i].type_from) + continue; + + if (!acpi_dev_hid_uid_match(adev, int3472_gpio_map[i].hid, NULL)) + continue; + + *type = int3472_gpio_map[i].type_to; + *gpio_flags = int3472_gpio_map[i].polarity_low ? + GPIO_ACTIVE_LOW : GPIO_ACTIVE_HIGH; + *func = int3472_gpio_map[i].func; + return; + } + + switch (*type) { case INT3472_GPIO_TYPE_RESET: *func = "reset"; - *polarity = GPIO_ACTIVE_LOW; + *gpio_flags = GPIO_ACTIVE_LOW; break; case INT3472_GPIO_TYPE_POWERDOWN: *func = "powerdown"; - *polarity = GPIO_ACTIVE_LOW; + *gpio_flags = GPIO_ACTIVE_LOW; break; case INT3472_GPIO_TYPE_CLK_ENABLE: *func = "clk-enable"; - *polarity = GPIO_ACTIVE_HIGH; + *gpio_flags = GPIO_ACTIVE_HIGH; break; case INT3472_GPIO_TYPE_PRIVACY_LED: *func = "privacy-led"; - *polarity = GPIO_ACTIVE_HIGH; + *gpio_flags = GPIO_ACTIVE_HIGH; break; case INT3472_GPIO_TYPE_POWER_ENABLE: *func = "power-enable"; - *polarity = GPIO_ACTIVE_HIGH; + *gpio_flags = GPIO_ACTIVE_HIGH; break; default: *func = "unknown"; - *polarity = GPIO_ACTIVE_HIGH; + *gpio_flags = GPIO_ACTIVE_HIGH; break; } } @@ -178,11 +223,11 @@ static void int3472_get_func_and_polarity(u8 type, const char **func, u32 *polar * to create clocks and regulators via the usual frameworks. * * Return: - * * 1 - To continue the loop - * * 0 - When all resources found are handled properly. - * * -EINVAL - If the resource is not a GPIO IO resource - * * -ENODEV - If the resource has no corresponding _DSM entry - * * -Other - Errors propagated from one of the sub-functions. + * * 1 - Continue the loop without adding a copy of the resource to + * * the list passed to acpi_dev_get_resources() + * * 0 - Continue the loop after adding a copy of the resource to + * * the list passed to acpi_dev_get_resources() + * * -errno - Error, break loop */ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, void *data) @@ -194,7 +239,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, struct gpio_desc *gpio; const char *err_msg; const char *func; - u32 polarity; + unsigned long gpio_flags; int ret; if (!acpi_gpio_get_io_resource(ares, &agpio)) @@ -217,26 +262,26 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, type = FIELD_GET(INT3472_GPIO_DSM_TYPE, obj->integer.value); - int3472_get_func_and_polarity(type, &func, &polarity); + int3472_get_func_and_polarity(int3472->sensor, &type, &func, &gpio_flags); pin = FIELD_GET(INT3472_GPIO_DSM_PIN, obj->integer.value); - if (pin != agpio->pin_table[0]) - dev_warn(int3472->dev, "%s %s pin number mismatch _DSM %d resource %d\n", - func, agpio->resource_source.string_ptr, pin, - agpio->pin_table[0]); + /* Pin field is not really used under Windows and wraps around at 8 bits */ + if (pin != (agpio->pin_table[0] & 0xff)) + dev_dbg(int3472->dev, FW_BUG "%s %s pin number mismatch _DSM %d resource %d\n", + func, agpio->resource_source.string_ptr, pin, agpio->pin_table[0]); active_value = FIELD_GET(INT3472_GPIO_DSM_SENSOR_ON_VAL, obj->integer.value); if (!active_value) - polarity ^= GPIO_ACTIVE_LOW; + gpio_flags ^= GPIO_ACTIVE_LOW; dev_dbg(int3472->dev, "%s %s pin %d active-%s\n", func, agpio->resource_source.string_ptr, agpio->pin_table[0], - str_high_low(polarity == GPIO_ACTIVE_HIGH)); + str_high_low(gpio_flags == GPIO_ACTIVE_HIGH)); switch (type) { case INT3472_GPIO_TYPE_RESET: case INT3472_GPIO_TYPE_POWERDOWN: - ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, func, polarity); + ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, func, gpio_flags); if (ret) err_msg = "Failed to map GPIO pin to sensor\n"; @@ -244,7 +289,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, case INT3472_GPIO_TYPE_CLK_ENABLE: case INT3472_GPIO_TYPE_PRIVACY_LED: case INT3472_GPIO_TYPE_POWER_ENABLE: - gpio = skl_int3472_gpiod_get_from_temp_lookup(int3472, agpio, func, polarity); + gpio = skl_int3472_gpiod_get_from_temp_lookup(int3472, agpio, func, gpio_flags); if (IS_ERR(gpio)) { ret = PTR_ERR(gpio); err_msg = "Failed to get GPIO\n"; @@ -289,7 +334,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, if (ret < 0) return dev_err_probe(int3472->dev, ret, err_msg); - return ret; + /* Tell acpi_dev_get_resources() to not make a copy of the resource */ + return 1; } static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472) diff --git a/drivers/platform/x86/intel/plr_tpmi.c b/drivers/platform/x86/intel/plr_tpmi.c index 691d43c3592c..2b55347a5a93 100644 --- a/drivers/platform/x86/intel/plr_tpmi.c +++ b/drivers/platform/x86/intel/plr_tpmi.c @@ -262,7 +262,7 @@ static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxilia struct resource *res; struct tpmi_plr *plr; void __iomem *base; - char name[16]; + char name[17]; int err; plat_info = tpmi_get_platform_data(auxdev); diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 3e7f99ac8c94..1ee0fb5f8250 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -22,6 +22,7 @@ #include <linux/suspend.h> #include <linux/units.h> +#include <asm/cpuid.h> #include <asm/cpu_device_id.h> #include <asm/intel-family.h> #include <asm/msr.h> @@ -625,8 +626,8 @@ static u32 convert_ltr_scale(u32 val) static int pmc_core_ltr_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; - u64 decoded_snoop_ltr, decoded_non_snoop_ltr; - u32 ltr_raw_data, scale, val; + u64 decoded_snoop_ltr, decoded_non_snoop_ltr, val; + u32 ltr_raw_data, scale; u16 snoop_ltr, nonsnoop_ltr; unsigned int i, index, ltr_index = 0; @@ -935,13 +936,13 @@ static unsigned int pmc_core_get_crystal_freq(void) { unsigned int eax_denominator, ebx_numerator, ecx_hz, edx; - if (boot_cpu_data.cpuid_level < 0x15) + if (boot_cpu_data.cpuid_level < CPUID_LEAF_TSC) return 0; eax_denominator = ebx_numerator = ecx_hz = edx = 0; - /* CPUID 15H TSC/Crystal ratio, plus optionally Crystal Hz */ - cpuid(0x15, &eax_denominator, &ebx_numerator, &ecx_hz, &edx); + /* TSC/Crystal ratio, plus optionally Crystal Hz */ + cpuid(CPUID_LEAF_TSC, &eax_denominator, &ebx_numerator, &ecx_hz, &edx); if (ebx_numerator == 0 || eax_denominator == 0) return 0; diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 8ed54b7a3333..7233b654bbad 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -81,7 +81,7 @@ EXPORT_SYMBOL_NS_GPL(pmt_telem_read_mmio, "INTEL_PMT"); */ static ssize_t intel_pmt_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct intel_pmt_entry *entry = container_of(attr, @@ -308,7 +308,7 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry, entry->pmt_bin_attr.attr.name = ns->name; entry->pmt_bin_attr.attr.mode = 0440; entry->pmt_bin_attr.mmap = intel_pmt_mmap; - entry->pmt_bin_attr.read = intel_pmt_read; + entry->pmt_bin_attr.read_new = intel_pmt_read; entry->pmt_bin_attr.size = entry->size; ret = sysfs_create_bin_file(&dev->kobj, &entry->pmt_bin_attr); diff --git a/drivers/platform/x86/intel/punit_ipc.c b/drivers/platform/x86/intel/punit_ipc.c index cd0ba84cc8e4..bafac8aa2baf 100644 --- a/drivers/platform/x86/intel/punit_ipc.c +++ b/drivers/platform/x86/intel/punit_ipc.c @@ -131,39 +131,6 @@ static int intel_punit_ipc_check_status(IPC_DEV *ipcdev, IPC_TYPE type) } /** - * intel_punit_ipc_simple_command() - Simple IPC command - * @cmd: IPC command code. - * @para1: First 8bit parameter, set 0 if not used. - * @para2: Second 8bit parameter, set 0 if not used. - * - * Send a IPC command to P-Unit when there is no data transaction - * - * Return: IPC error code or 0 on success. - */ -int intel_punit_ipc_simple_command(int cmd, int para1, int para2) -{ - IPC_DEV *ipcdev = punit_ipcdev; - IPC_TYPE type; - u32 val; - int ret; - - mutex_lock(&ipcdev->lock); - - reinit_completion(&ipcdev->cmd_complete); - type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET; - - val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK; - val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 << CMD_PARA1_SHIFT; - ipc_write_cmd(ipcdev, type, val); - ret = intel_punit_ipc_check_status(ipcdev, type); - - mutex_unlock(&ipcdev->lock); - - return ret; -} -EXPORT_SYMBOL(intel_punit_ipc_simple_command); - -/** * intel_punit_ipc_command() - IPC command with data and pointers * @cmd: IPC command code. * @para1: First 8bit parameter, set 0 if not used. diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c index 33f33b1070fd..30d1c2caf984 100644 --- a/drivers/platform/x86/intel/sdsi.c +++ b/drivers/platform/x86/intel/sdsi.c @@ -398,8 +398,8 @@ free_payload: } static ssize_t provision_akc_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, - size_t count) + const struct bin_attribute *attr, char *buf, + loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); struct sdsi_priv *priv = dev_get_drvdata(dev); @@ -409,11 +409,11 @@ static ssize_t provision_akc_write(struct file *filp, struct kobject *kobj, return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_AKC); } -static BIN_ATTR_WO(provision_akc, SDSI_SIZE_WRITE_MSG); +static const BIN_ATTR_WO(provision_akc, SDSI_SIZE_WRITE_MSG); static ssize_t provision_cap_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, - size_t count) + const struct bin_attribute *attr, char *buf, + loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); struct sdsi_priv *priv = dev_get_drvdata(dev); @@ -423,7 +423,7 @@ static ssize_t provision_cap_write(struct file *filp, struct kobject *kobj, return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_CAP); } -static BIN_ATTR_WO(provision_cap, SDSI_SIZE_WRITE_MSG); +static const BIN_ATTR_WO(provision_cap, SDSI_SIZE_WRITE_MSG); static ssize_t certificate_read(u64 command, u64 control_flags, struct sdsi_priv *priv, @@ -469,7 +469,7 @@ free_buffer: static ssize_t state_certificate_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -477,11 +477,11 @@ state_certificate_read(struct file *filp, struct kobject *kobj, return certificate_read(SDSI_CMD_READ_STATE, 0, priv, buf, off, count); } -static BIN_ATTR_ADMIN_RO(state_certificate, SDSI_SIZE_READ_MSG); +static const BIN_ATTR_ADMIN_RO(state_certificate, SDSI_SIZE_READ_MSG); static ssize_t meter_certificate_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -489,11 +489,11 @@ meter_certificate_read(struct file *filp, struct kobject *kobj, return certificate_read(SDSI_CMD_READ_METER, 0, priv, buf, off, count); } -static BIN_ATTR_ADMIN_RO(meter_certificate, SDSI_SIZE_READ_MSG); +static const BIN_ATTR_ADMIN_RO(meter_certificate, SDSI_SIZE_READ_MSG); static ssize_t meter_current_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -502,11 +502,11 @@ meter_current_read(struct file *filp, struct kobject *kobj, return certificate_read(SDSI_CMD_READ_METER, CTRL_METER_ENABLE_DRAM, priv, buf, off, count); } -static BIN_ATTR_ADMIN_RO(meter_current, SDSI_SIZE_READ_MSG); +static const BIN_ATTR_ADMIN_RO(meter_current, SDSI_SIZE_READ_MSG); static ssize_t registers_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, - size_t count) + const struct bin_attribute *attr, char *buf, + loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); struct sdsi_priv *priv = dev_get_drvdata(dev); @@ -528,9 +528,9 @@ static ssize_t registers_read(struct file *filp, struct kobject *kobj, return count; } -static BIN_ATTR_ADMIN_RO(registers, SDSI_SIZE_REGS); +static const BIN_ATTR_ADMIN_RO(registers, SDSI_SIZE_REGS); -static struct bin_attribute *sdsi_bin_attrs[] = { +static const struct bin_attribute *const sdsi_bin_attrs[] = { &bin_attr_registers, &bin_attr_state_certificate, &bin_attr_meter_certificate, @@ -576,7 +576,7 @@ static struct attribute *sdsi_attrs[] = { static const struct attribute_group sdsi_group = { .attrs = sdsi_attrs, - .bin_attrs = sdsi_bin_attrs, + .bin_attrs_new = sdsi_bin_attrs, .is_bin_visible = sdsi_battr_is_visible, }; __ATTRIBUTE_GROUPS(sdsi); diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c index dbcd3087aaa4..31239a93dd71 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c @@ -84,7 +84,7 @@ static DECLARE_HASHTABLE(isst_hash, 8); static DEFINE_MUTEX(isst_hash_lock); static int isst_store_new_cmd(int cmd, u32 cpu, int mbox_cmd_type, u32 param, - u32 data) + u64 data) { struct isst_cmd *sst_cmd; diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 8272f1dd0fbc..db3c031d1757 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -404,6 +404,11 @@ static const struct intel_vsec_platform_info oobmsm_info = { .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_SDSI | VSEC_CAP_TPMI, }; +/* DMR OOBMSM info */ +static const struct intel_vsec_platform_info dmr_oobmsm_info = { + .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_TPMI, +}; + /* TGL info */ static const struct intel_vsec_platform_info tgl_info = { .caps = VSEC_CAP_TELEMETRY, @@ -420,6 +425,7 @@ static const struct intel_vsec_platform_info lnl_info = { #define PCI_DEVICE_ID_INTEL_VSEC_MTL_M 0x7d0d #define PCI_DEVICE_ID_INTEL_VSEC_MTL_S 0xad0d #define PCI_DEVICE_ID_INTEL_VSEC_OOBMSM 0x09a7 +#define PCI_DEVICE_ID_INTEL_VSEC_OOBMSM_DMR 0x09a1 #define PCI_DEVICE_ID_INTEL_VSEC_RPL 0xa77d #define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d #define PCI_DEVICE_ID_INTEL_VSEC_LNL_M 0x647d @@ -430,6 +436,7 @@ static const struct pci_device_id intel_vsec_pci_ids[] = { { PCI_DEVICE_DATA(INTEL, VSEC_MTL_M, &mtl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_MTL_S, &mtl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &oobmsm_info) }, + { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM_DMR, &dmr_oobmsm_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_RPL, &tgl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_LNL_M, &lnl_info) }, diff --git a/drivers/platform/x86/lenovo-wmi-camera.c b/drivers/platform/x86/lenovo-wmi-camera.c index 0c0bedaf7407..eb60fb9a5b3f 100644 --- a/drivers/platform/x86/lenovo-wmi-camera.c +++ b/drivers/platform/x86/lenovo-wmi-camera.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/wmi.h> +#include <linux/cleanup.h> #define WMI_LENOVO_CAMERABUTTON_EVENT_GUID "50C76F1F-D8E4-D895-0A3D-62F4EA400013" @@ -26,10 +27,38 @@ enum { SW_CAMERA_ON = 1, }; +static int camera_shutter_input_setup(struct wmi_device *wdev, u8 camera_mode) +{ + struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev); + int err; + + priv->idev = input_allocate_device(); + if (!priv->idev) + return -ENOMEM; + + priv->idev->name = "Lenovo WMI Camera Button"; + priv->idev->phys = "wmi/input0"; + priv->idev->id.bustype = BUS_HOST; + priv->idev->dev.parent = &wdev->dev; + + input_set_capability(priv->idev, EV_SW, SW_CAMERA_LENS_COVER); + + input_report_switch(priv->idev, SW_CAMERA_LENS_COVER, + camera_mode == SW_CAMERA_ON ? 0 : 1); + input_sync(priv->idev); + + err = input_register_device(priv->idev); + if (err) { + input_free_device(priv->idev); + priv->idev = NULL; + } + + return err; +} + static void lenovo_wmi_notify(struct wmi_device *wdev, union acpi_object *obj) { struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev); - unsigned int keycode; u8 camera_mode; if (obj->type != ACPI_TYPE_BUFFER) { @@ -53,22 +82,24 @@ static void lenovo_wmi_notify(struct wmi_device *wdev, union acpi_object *obj) return; } - mutex_lock(&priv->notify_lock); + guard(mutex)(&priv->notify_lock); - keycode = camera_mode == SW_CAMERA_ON ? - KEY_CAMERA_ACCESS_ENABLE : KEY_CAMERA_ACCESS_DISABLE; - input_report_key(priv->idev, keycode, 1); - input_sync(priv->idev); - input_report_key(priv->idev, keycode, 0); - input_sync(priv->idev); + if (!priv->idev) { + if (camera_shutter_input_setup(wdev, camera_mode)) + dev_warn(&wdev->dev, "Failed to register input device\n"); + return; + } - mutex_unlock(&priv->notify_lock); + if (camera_mode == SW_CAMERA_ON) + input_report_switch(priv->idev, SW_CAMERA_LENS_COVER, 0); + else + input_report_switch(priv->idev, SW_CAMERA_LENS_COVER, 1); + input_sync(priv->idev); } static int lenovo_wmi_probe(struct wmi_device *wdev, const void *context) { struct lenovo_wmi_priv *priv; - int ret; priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -76,21 +107,6 @@ static int lenovo_wmi_probe(struct wmi_device *wdev, const void *context) dev_set_drvdata(&wdev->dev, priv); - priv->idev = devm_input_allocate_device(&wdev->dev); - if (!priv->idev) - return -ENOMEM; - - priv->idev->name = "Lenovo WMI Camera Button"; - priv->idev->phys = "wmi/input0"; - priv->idev->id.bustype = BUS_HOST; - priv->idev->dev.parent = &wdev->dev; - input_set_capability(priv->idev, EV_KEY, KEY_CAMERA_ACCESS_ENABLE); - input_set_capability(priv->idev, EV_KEY, KEY_CAMERA_ACCESS_DISABLE); - - ret = input_register_device(priv->idev); - if (ret) - return ret; - mutex_init(&priv->notify_lock); return 0; @@ -100,6 +116,9 @@ static void lenovo_wmi_remove(struct wmi_device *wdev) { struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev); + if (priv->idev) + input_unregister_device(priv->idev); + mutex_destroy(&priv->notify_lock); } diff --git a/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c b/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c index a96b215cd2c5..25933cd018d1 100644 --- a/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c +++ b/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c @@ -219,7 +219,7 @@ static int yt2_1380_fc_serdev_probe(struct serdev_device *serdev) return 0; } -struct serdev_device_driver yt2_1380_fc_serdev_driver = { +static struct serdev_device_driver yt2_1380_fc_serdev_driver = { .probe = yt2_1380_fc_serdev_probe, .driver = { .name = KBUILD_MODNAME, diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index e5391a37014d..c4b150fa093f 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -806,8 +806,8 @@ static void msi_send_touchpad_key(struct work_struct *ignored) } static DECLARE_DELAYED_WORK(msi_touchpad_dwork, msi_send_touchpad_key); -static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, - struct serio *port) +static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, struct serio *port, + void *context) { static bool extended; @@ -996,7 +996,7 @@ static int __init load_scm_model_init(struct platform_device *sdev) if (result) goto fail_input; - result = i8042_install_filter(msi_laptop_i8042_filter); + result = i8042_install_filter(msi_laptop_i8042_filter, NULL); if (result) { pr_err("Unable to install key filter\n"); goto fail_filter; diff --git a/drivers/platform/x86/msi-wmi-platform.c b/drivers/platform/x86/msi-wmi-platform.c index 9b5c7f8c79b0..dc5e9878cb68 100644 --- a/drivers/platform/x86/msi-wmi-platform.c +++ b/drivers/platform/x86/msi-wmi-platform.c @@ -10,6 +10,7 @@ #include <linux/acpi.h> #include <linux/bits.h> #include <linux/bitfield.h> +#include <linux/cleanup.h> #include <linux/debugfs.h> #include <linux/device.h> #include <linux/device/driver.h> @@ -17,6 +18,7 @@ #include <linux/hwmon.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/printk.h> #include <linux/rwsem.h> #include <linux/types.h> @@ -76,8 +78,13 @@ enum msi_wmi_platform_method { MSI_PLATFORM_GET_WMI = 0x1d, }; -struct msi_wmi_platform_debugfs_data { +struct msi_wmi_platform_data { struct wmi_device *wdev; + struct mutex wmi_lock; /* Necessary when calling WMI methods */ +}; + +struct msi_wmi_platform_debugfs_data { + struct msi_wmi_platform_data *data; enum msi_wmi_platform_method method; struct rw_semaphore buffer_lock; /* Protects debugfs buffer */ size_t length; @@ -132,8 +139,9 @@ static int msi_wmi_platform_parse_buffer(union acpi_object *obj, u8 *output, siz return 0; } -static int msi_wmi_platform_query(struct wmi_device *wdev, enum msi_wmi_platform_method method, - u8 *input, size_t input_length, u8 *output, size_t output_length) +static int msi_wmi_platform_query(struct msi_wmi_platform_data *data, + enum msi_wmi_platform_method method, u8 *input, + size_t input_length, u8 *output, size_t output_length) { struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer in = { @@ -147,9 +155,15 @@ static int msi_wmi_platform_query(struct wmi_device *wdev, enum msi_wmi_platform if (!input_length || !output_length) return -EINVAL; - status = wmidev_evaluate_method(wdev, 0x0, method, &in, &out); - if (ACPI_FAILURE(status)) - return -EIO; + /* + * The ACPI control method responsible for handling the WMI method calls + * is not thread-safe. Because of this we have to do the locking ourself. + */ + scoped_guard(mutex, &data->wmi_lock) { + status = wmidev_evaluate_method(data->wdev, 0x0, method, &in, &out); + if (ACPI_FAILURE(status)) + return -EIO; + } obj = out.pointer; if (!obj) @@ -170,22 +184,22 @@ static umode_t msi_wmi_platform_is_visible(const void *drvdata, enum hwmon_senso static int msi_wmi_platform_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { - struct wmi_device *wdev = dev_get_drvdata(dev); + struct msi_wmi_platform_data *data = dev_get_drvdata(dev); u8 input[32] = { 0 }; u8 output[32]; - u16 data; + u16 value; int ret; - ret = msi_wmi_platform_query(wdev, MSI_PLATFORM_GET_FAN, input, sizeof(input), output, + ret = msi_wmi_platform_query(data, MSI_PLATFORM_GET_FAN, input, sizeof(input), output, sizeof(output)); if (ret < 0) return ret; - data = get_unaligned_be16(&output[channel * 2 + 1]); - if (!data) + value = get_unaligned_be16(&output[channel * 2 + 1]); + if (!value) *val = 0; else - *val = 480000 / data; + *val = 480000 / value; return 0; } @@ -231,7 +245,7 @@ static ssize_t msi_wmi_platform_write(struct file *fp, const char __user *input, return ret; down_write(&data->buffer_lock); - ret = msi_wmi_platform_query(data->wdev, data->method, payload, data->length, data->buffer, + ret = msi_wmi_platform_query(data->data, data->method, payload, data->length, data->buffer, data->length); up_write(&data->buffer_lock); @@ -277,17 +291,17 @@ static void msi_wmi_platform_debugfs_remove(void *data) debugfs_remove_recursive(dir); } -static void msi_wmi_platform_debugfs_add(struct wmi_device *wdev, struct dentry *dir, +static void msi_wmi_platform_debugfs_add(struct msi_wmi_platform_data *drvdata, struct dentry *dir, const char *name, enum msi_wmi_platform_method method) { struct msi_wmi_platform_debugfs_data *data; struct dentry *entry; - data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(&drvdata->wdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return; - data->wdev = wdev; + data->data = drvdata; data->method = method; init_rwsem(&data->buffer_lock); @@ -298,82 +312,82 @@ static void msi_wmi_platform_debugfs_add(struct wmi_device *wdev, struct dentry entry = debugfs_create_file(name, 0600, dir, data, &msi_wmi_platform_debugfs_fops); if (IS_ERR(entry)) - devm_kfree(&wdev->dev, data); + devm_kfree(&drvdata->wdev->dev, data); } -static void msi_wmi_platform_debugfs_init(struct wmi_device *wdev) +static void msi_wmi_platform_debugfs_init(struct msi_wmi_platform_data *data) { struct dentry *dir; char dir_name[64]; int ret, method; - scnprintf(dir_name, ARRAY_SIZE(dir_name), "%s-%s", DRIVER_NAME, dev_name(&wdev->dev)); + scnprintf(dir_name, ARRAY_SIZE(dir_name), "%s-%s", DRIVER_NAME, dev_name(&data->wdev->dev)); dir = debugfs_create_dir(dir_name, NULL); if (IS_ERR(dir)) return; - ret = devm_add_action_or_reset(&wdev->dev, msi_wmi_platform_debugfs_remove, dir); + ret = devm_add_action_or_reset(&data->wdev->dev, msi_wmi_platform_debugfs_remove, dir); if (ret < 0) return; for (method = MSI_PLATFORM_GET_PACKAGE; method <= MSI_PLATFORM_GET_WMI; method++) - msi_wmi_platform_debugfs_add(wdev, dir, msi_wmi_platform_debugfs_names[method - 1], + msi_wmi_platform_debugfs_add(data, dir, msi_wmi_platform_debugfs_names[method - 1], method); } -static int msi_wmi_platform_hwmon_init(struct wmi_device *wdev) +static int msi_wmi_platform_hwmon_init(struct msi_wmi_platform_data *data) { struct device *hdev; - hdev = devm_hwmon_device_register_with_info(&wdev->dev, "msi_wmi_platform", wdev, + hdev = devm_hwmon_device_register_with_info(&data->wdev->dev, "msi_wmi_platform", data, &msi_wmi_platform_chip_info, NULL); return PTR_ERR_OR_ZERO(hdev); } -static int msi_wmi_platform_ec_init(struct wmi_device *wdev) +static int msi_wmi_platform_ec_init(struct msi_wmi_platform_data *data) { u8 input[32] = { 0 }; u8 output[32]; u8 flags; int ret; - ret = msi_wmi_platform_query(wdev, MSI_PLATFORM_GET_EC, input, sizeof(input), output, + ret = msi_wmi_platform_query(data, MSI_PLATFORM_GET_EC, input, sizeof(input), output, sizeof(output)); if (ret < 0) return ret; flags = output[MSI_PLATFORM_EC_FLAGS_OFFSET]; - dev_dbg(&wdev->dev, "EC RAM version %lu.%lu\n", + dev_dbg(&data->wdev->dev, "EC RAM version %lu.%lu\n", FIELD_GET(MSI_PLATFORM_EC_MAJOR_MASK, flags), FIELD_GET(MSI_PLATFORM_EC_MINOR_MASK, flags)); - dev_dbg(&wdev->dev, "EC firmware version %.28s\n", + dev_dbg(&data->wdev->dev, "EC firmware version %.28s\n", &output[MSI_PLATFORM_EC_VERSION_OFFSET]); if (!(flags & MSI_PLATFORM_EC_IS_TIGERLAKE)) { if (!force) return -ENODEV; - dev_warn(&wdev->dev, "Loading on a non-Tigerlake platform\n"); + dev_warn(&data->wdev->dev, "Loading on a non-Tigerlake platform\n"); } return 0; } -static int msi_wmi_platform_init(struct wmi_device *wdev) +static int msi_wmi_platform_init(struct msi_wmi_platform_data *data) { u8 input[32] = { 0 }; u8 output[32]; int ret; - ret = msi_wmi_platform_query(wdev, MSI_PLATFORM_GET_WMI, input, sizeof(input), output, + ret = msi_wmi_platform_query(data, MSI_PLATFORM_GET_WMI, input, sizeof(input), output, sizeof(output)); if (ret < 0) return ret; - dev_dbg(&wdev->dev, "WMI interface version %u.%u\n", + dev_dbg(&data->wdev->dev, "WMI interface version %u.%u\n", output[MSI_PLATFORM_WMI_MAJOR_OFFSET], output[MSI_PLATFORM_WMI_MINOR_OFFSET]); @@ -381,7 +395,8 @@ static int msi_wmi_platform_init(struct wmi_device *wdev) if (!force) return -ENODEV; - dev_warn(&wdev->dev, "Loading despite unsupported WMI interface version (%u.%u)\n", + dev_warn(&data->wdev->dev, + "Loading despite unsupported WMI interface version (%u.%u)\n", output[MSI_PLATFORM_WMI_MAJOR_OFFSET], output[MSI_PLATFORM_WMI_MINOR_OFFSET]); } @@ -391,19 +406,31 @@ static int msi_wmi_platform_init(struct wmi_device *wdev) static int msi_wmi_platform_probe(struct wmi_device *wdev, const void *context) { + struct msi_wmi_platform_data *data; int ret; - ret = msi_wmi_platform_init(wdev); + data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->wdev = wdev; + dev_set_drvdata(&wdev->dev, data); + + ret = devm_mutex_init(&wdev->dev, &data->wmi_lock); + if (ret < 0) + return ret; + + ret = msi_wmi_platform_init(data); if (ret < 0) return ret; - ret = msi_wmi_platform_ec_init(wdev); + ret = msi_wmi_platform_ec_init(data); if (ret < 0) return ret; - msi_wmi_platform_debugfs_init(wdev); + msi_wmi_platform_debugfs_init(data); - return msi_wmi_platform_hwmon_init(wdev); + return msi_wmi_platform_hwmon_init(data); } static const struct wmi_device_id msi_wmi_platform_id_table[] = { diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 22ca70eb8227..2987b4db6009 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -260,7 +260,7 @@ struct pcc_acpi { * keypress events over the PS/2 kbd interface, filter these out. */ static bool panasonic_i8042_filter(unsigned char data, unsigned char str, - struct serio *port) + struct serio *port, void *context) { static bool extended; @@ -1100,7 +1100,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) pcc->platform = NULL; } - i8042_install_filter(panasonic_i8042_filter); + i8042_install_filter(panasonic_i8042_filter, NULL); return 0; out_platform: diff --git a/drivers/platform/x86/quickstart.c b/drivers/platform/x86/quickstart.c index 8d540a1c8602..c332c7cdaff5 100644 --- a/drivers/platform/x86/quickstart.c +++ b/drivers/platform/x86/quickstart.c @@ -20,7 +20,6 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/platform_device.h> -#include <linux/pm_wakeup.h> #include <linux/printk.h> #include <linux/slab.h> #include <linux/sysfs.h> diff --git a/drivers/platform/x86/serdev_helpers.h b/drivers/platform/x86/serdev_helpers.h index 3bc7fd8e1e19..57eac75805e2 100644 --- a/drivers/platform/x86/serdev_helpers.h +++ b/drivers/platform/x86/serdev_helpers.h @@ -22,32 +22,14 @@ #include <linux/string.h> static inline struct device * -get_serdev_controller(const char *serial_ctrl_hid, - const char *serial_ctrl_uid, - int serial_ctrl_port, - const char *serdev_ctrl_name) +get_serdev_controller_from_parent(struct device *ctrl_dev, + int serial_ctrl_port, + const char *serdev_ctrl_name) { - struct device *ctrl_dev, *child; - struct acpi_device *ctrl_adev; + struct device *child; char name[32]; int i; - ctrl_adev = acpi_dev_get_first_match_dev(serial_ctrl_hid, serial_ctrl_uid, -1); - if (!ctrl_adev) { - pr_err("error could not get %s/%s serial-ctrl adev\n", - serial_ctrl_hid, serial_ctrl_uid ?: "*"); - return ERR_PTR(-ENODEV); - } - - /* get_first_physical_node() returns a weak ref */ - ctrl_dev = get_device(acpi_get_first_physical_node(ctrl_adev)); - if (!ctrl_dev) { - pr_err("error could not get %s/%s serial-ctrl physical node\n", - serial_ctrl_hid, serial_ctrl_uid ?: "*"); - ctrl_dev = ERR_PTR(-ENODEV); - goto put_ctrl_adev; - } - /* Walk host -> uart-ctrl -> port -> serdev-ctrl */ for (i = 0; i < 3; i++) { switch (i) { @@ -67,14 +49,40 @@ get_serdev_controller(const char *serial_ctrl_hid, put_device(ctrl_dev); if (!child) { pr_err("error could not find '%s' device\n", name); - ctrl_dev = ERR_PTR(-ENODEV); - goto put_ctrl_adev; + return ERR_PTR(-ENODEV); } ctrl_dev = child; } -put_ctrl_adev: - acpi_dev_put(ctrl_adev); return ctrl_dev; } + +static inline struct device * +get_serdev_controller(const char *serial_ctrl_hid, + const char *serial_ctrl_uid, + int serial_ctrl_port, + const char *serdev_ctrl_name) +{ + struct acpi_device *adev; + struct device *parent; + + adev = acpi_dev_get_first_match_dev(serial_ctrl_hid, serial_ctrl_uid, -1); + if (!adev) { + pr_err("error could not get %s/%s serial-ctrl adev\n", + serial_ctrl_hid, serial_ctrl_uid ?: "*"); + return ERR_PTR(-ENODEV); + } + + /* get_first_physical_node() returns a weak ref */ + parent = get_device(acpi_get_first_physical_node(adev)); + acpi_dev_put(adev); + if (!parent) { + pr_err("error could not get %s/%s serial-ctrl physical node\n", + serial_ctrl_hid, serial_ctrl_uid ?: "*"); + return ERR_PTR(-ENODEV); + } + + /* This puts our reference on parent and returns a ref on the ctrl */ + return get_serdev_controller_from_parent(parent, serial_ctrl_port, serdev_ctrl_name); +} diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index ed6b28505cd6..db030b0f176a 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -384,6 +384,17 @@ static const struct smi_node cs35l57_hda = { .bus_type = SMI_AUTO_DETECT, }; +static const struct smi_node tas2781_hda = { + .instances = { + { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 }, + { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 }, + { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 }, + { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 }, + {} + }, + .bus_type = SMI_AUTO_DETECT, +}; + /* * Note new device-ids must also be added to ignore_serial_bus_ids in * drivers/acpi/scan.c: acpi_device_enumeration_by_parent(). @@ -396,6 +407,7 @@ static const struct acpi_device_id smi_acpi_ids[] = { { "CSC3556", (unsigned long)&cs35l56_hda }, { "CSC3557", (unsigned long)&cs35l57_hda }, { "INT3515", (unsigned long)&int3515_data }, + { "TXNW2781", (unsigned long)&tas2781_hda }, /* Non-conforming _HID for Cirrus Logic already released */ { "CLSA0100", (unsigned long)&cs35l41_hda }, { "CLSA0101", (unsigned long)&cs35l41_hda }, diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index 38de0cb20d77..323316ac6783 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -194,7 +194,6 @@ static const char * const level_options[] = { [TLMI_LEVEL_MASTER] = "master", }; static struct think_lmi tlmi_priv; -static const struct class *fw_attr_class; static DEFINE_MUTEX(tlmi_mutex); static inline struct tlmi_pwd_setting *to_tlmi_pwd_setting(struct kobject *kobj) @@ -1446,11 +1445,7 @@ static int tlmi_sysfs_init(void) { int i, ret; - ret = fw_attributes_class_get(&fw_attr_class); - if (ret) - return ret; - - tlmi_priv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0), + tlmi_priv.class_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0), NULL, "%s", "thinklmi"); if (IS_ERR(tlmi_priv.class_dev)) { ret = PTR_ERR(tlmi_priv.class_dev); @@ -1563,9 +1558,8 @@ static int tlmi_sysfs_init(void) fail_create_attr: tlmi_release_attr(); fail_device_created: - device_destroy(fw_attr_class, MKDEV(0, 0)); + device_destroy(&firmware_attributes_class, MKDEV(0, 0)); fail_class_created: - fw_attributes_class_put(); return ret; } @@ -1788,8 +1782,7 @@ fail_clear_attr: static void tlmi_remove(struct wmi_device *wdev) { tlmi_release_attr(); - device_destroy(fw_attr_class, MKDEV(0, 0)); - fw_attributes_class_put(); + device_destroy(&firmware_attributes_class, MKDEV(0, 0)); } static int tlmi_probe(struct wmi_device *wdev, const void *context) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 84dcd7da7319..2ff38ca9ddb4 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -963,6 +963,7 @@ static const struct proc_ops dispatch_proc_ops = { static struct platform_device *tpacpi_pdev; static struct platform_device *tpacpi_sensors_pdev; static struct device *tpacpi_hwmon; +static struct device *tpacpi_pprof; static struct input_dev *tpacpi_inputdev; static struct mutex tpacpi_inputdev_send_mutex; static LIST_HEAD(tpacpi_all_drivers); @@ -3275,6 +3276,7 @@ static const struct key_entry keymap_lenovo[] __initconst = { * scancodes to preserve uAPI compatibility, see tpacpi_input_send_key(). */ { KE_KEY, 0x131d, { KEY_VENDOR } }, /* System debug info, similar to old ThinkPad key */ + { KE_KEY, 0x1320, { KEY_LINK_PHONE } }, { KE_KEY, TP_HKEY_EV_TRACK_DOUBLETAP /* 0x8036 */, { KEY_PROG4 } }, { KE_END } }; @@ -7883,6 +7885,7 @@ static struct ibm_struct volume_driver_data = { #define FAN_NS_CTRL_STATUS BIT(2) /* Bit which determines control is enabled or not */ #define FAN_NS_CTRL BIT(4) /* Bit which determines control is by host or EC */ +#define FAN_CLOCK_TPM (22500*60) /* Ticks per minute for a 22.5 kHz clock */ enum { /* Fan control constants */ fan_status_offset = 0x2f, /* EC register 0x2f */ @@ -7938,6 +7941,7 @@ static int fan_watchdog_maxinterval; static bool fan_with_ns_addr; static bool ecfw_with_fan_dec_rpm; +static bool fan_speed_in_tpr; static struct mutex fan_mutex; @@ -8140,8 +8144,11 @@ static int fan_get_speed(unsigned int *speed) !acpi_ec_read(fan_rpm_offset + 1, &hi))) return -EIO; - if (likely(speed)) + if (likely(speed)) { *speed = (hi << 8) | lo; + if (fan_speed_in_tpr && *speed != 0) + *speed = FAN_CLOCK_TPM / *speed; + } break; case TPACPI_FAN_RD_TPEC_NS: if (!acpi_ec_read(fan_rpm_status_ns, &lo)) @@ -8174,8 +8181,11 @@ static int fan2_get_speed(unsigned int *speed) if (rc) return -EIO; - if (likely(speed)) + if (likely(speed)) { *speed = (hi << 8) | lo; + if (fan_speed_in_tpr && *speed != 0) + *speed = FAN_CLOCK_TPM / *speed; + } break; case TPACPI_FAN_RD_TPEC_NS: @@ -8786,6 +8796,8 @@ static const struct attribute_group fan_driver_attr_group = { #define TPACPI_FAN_NOFAN 0x0008 /* no fan available */ #define TPACPI_FAN_NS 0x0010 /* For EC with non-Standard register addresses */ #define TPACPI_FAN_DECRPM 0x0020 /* For ECFW's with RPM in register as decimal */ +#define TPACPI_FAN_TPR 0x0040 /* Fan speed is in Ticks Per Revolution */ +#define TPACPI_FAN_NOACPI 0x0080 /* Don't use ACPI methods even if detected */ static const struct tpacpi_quirk fan_quirk_table[] __initconst = { TPACPI_QEC_IBM('1', 'Y', TPACPI_FAN_Q1), @@ -8815,6 +8827,10 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = { TPACPI_Q_LNV3('R', '0', 'V', TPACPI_FAN_NS), /* 11e Gen5 KL-Y */ TPACPI_Q_LNV3('N', '1', 'O', TPACPI_FAN_NOFAN), /* X1 Tablet (2nd gen) */ TPACPI_Q_LNV3('R', '0', 'Q', TPACPI_FAN_DECRPM),/* L480 */ + TPACPI_Q_LNV('8', 'F', TPACPI_FAN_TPR), /* ThinkPad x120e */ + TPACPI_Q_LNV3('R', '0', '0', TPACPI_FAN_NOACPI),/* E560 */ + TPACPI_Q_LNV3('R', '1', '2', TPACPI_FAN_NOACPI),/* T495 */ + TPACPI_Q_LNV3('R', '1', '3', TPACPI_FAN_NOACPI),/* T495s */ }; static int __init fan_init(struct ibm_init_struct *iibm) @@ -8866,6 +8882,13 @@ static int __init fan_init(struct ibm_init_struct *iibm) tp_features.fan_ctrl_status_undef = 1; } + if (quirks & TPACPI_FAN_NOACPI) { + /* E560, T495, T495s */ + pr_info("Ignoring buggy ACPI fan access method\n"); + fang_handle = NULL; + fanw_handle = NULL; + } + if (gfan_handle) { /* 570, 600e/x, 770e, 770x */ fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN; @@ -8885,6 +8908,8 @@ static int __init fan_init(struct ibm_init_struct *iibm) if (quirks & TPACPI_FAN_Q1) fan_quirk1_setup(); + if (quirks & TPACPI_FAN_TPR) + fan_speed_in_tpr = true; /* Try and probe the 2nd fan */ tp_features.second_fan = 1; /* needed for get_speed to work */ res = fan2_get_speed(&speed); @@ -10318,6 +10343,10 @@ static struct ibm_struct proxsensor_driver_data = { #define DYTC_MODE_PSC_BALANCE 5 /* Default mode aka balanced */ #define DYTC_MODE_PSC_PERFORM 7 /* High power mode aka performance */ +#define DYTC_MODE_PSCV9_LOWPOWER 1 /* Low power mode */ +#define DYTC_MODE_PSCV9_BALANCE 3 /* Default mode aka balanced */ +#define DYTC_MODE_PSCV9_PERFORM 4 /* High power mode aka performance */ + #define DYTC_ERR_MASK 0xF /* Bits 0-3 in cmd result are the error result */ #define DYTC_ERR_SUCCESS 1 /* CMD completed successful */ @@ -10338,6 +10367,10 @@ static int dytc_capabilities; static bool dytc_mmc_get_available; static int profile_force; +static int platform_psc_profile_lowpower = DYTC_MODE_PSC_LOWPOWER; +static int platform_psc_profile_balanced = DYTC_MODE_PSC_BALANCE; +static int platform_psc_profile_performance = DYTC_MODE_PSC_PERFORM; + static int convert_dytc_to_profile(int funcmode, int dytcmode, enum platform_profile_option *profile) { @@ -10359,19 +10392,15 @@ static int convert_dytc_to_profile(int funcmode, int dytcmode, } return 0; case DYTC_FUNCTION_PSC: - switch (dytcmode) { - case DYTC_MODE_PSC_LOWPOWER: + if (dytcmode == platform_psc_profile_lowpower) *profile = PLATFORM_PROFILE_LOW_POWER; - break; - case DYTC_MODE_PSC_BALANCE: + else if (dytcmode == platform_psc_profile_balanced) *profile = PLATFORM_PROFILE_BALANCED; - break; - case DYTC_MODE_PSC_PERFORM: + else if (dytcmode == platform_psc_profile_performance) *profile = PLATFORM_PROFILE_PERFORMANCE; - break; - default: /* Unknown mode */ + else return -EINVAL; - } + return 0; case DYTC_FUNCTION_AMT: /* For now return balanced. It's the closest we have to 'auto' */ @@ -10392,19 +10421,19 @@ static int convert_profile_to_dytc(enum platform_profile_option profile, int *pe if (dytc_capabilities & BIT(DYTC_FC_MMC)) *perfmode = DYTC_MODE_MMC_LOWPOWER; else if (dytc_capabilities & BIT(DYTC_FC_PSC)) - *perfmode = DYTC_MODE_PSC_LOWPOWER; + *perfmode = platform_psc_profile_lowpower; break; case PLATFORM_PROFILE_BALANCED: if (dytc_capabilities & BIT(DYTC_FC_MMC)) *perfmode = DYTC_MODE_MMC_BALANCE; else if (dytc_capabilities & BIT(DYTC_FC_PSC)) - *perfmode = DYTC_MODE_PSC_BALANCE; + *perfmode = platform_psc_profile_balanced; break; case PLATFORM_PROFILE_PERFORMANCE: if (dytc_capabilities & BIT(DYTC_FC_MMC)) *perfmode = DYTC_MODE_MMC_PERFORM; else if (dytc_capabilities & BIT(DYTC_FC_PSC)) - *perfmode = DYTC_MODE_PSC_PERFORM; + *perfmode = platform_psc_profile_performance; break; default: /* Unknown profile */ return -EOPNOTSUPP; @@ -10416,7 +10445,7 @@ static int convert_profile_to_dytc(enum platform_profile_option profile, int *pe * dytc_profile_get: Function to register with platform_profile * handler. Returns current platform profile. */ -static int dytc_profile_get(struct platform_profile_handler *pprof, +static int dytc_profile_get(struct device *dev, enum platform_profile_option *profile) { *profile = dytc_current_profile; @@ -10491,7 +10520,7 @@ static int dytc_cql_command(int command, int *output) * dytc_profile_set: Function to register with platform_profile * handler. Sets current platform profile. */ -static int dytc_profile_set(struct platform_profile_handler *pprof, +static int dytc_profile_set(struct device *dev, enum platform_profile_option profile) { int perfmode; @@ -10540,6 +10569,21 @@ unlock: return err; } +static int dytc_profile_probe(void *drvdata, unsigned long *choices) +{ + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); + set_bit(PLATFORM_PROFILE_BALANCED, choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + + return 0; +} + +static const struct platform_profile_ops dytc_profile_ops = { + .probe = dytc_profile_probe, + .profile_get = dytc_profile_get, + .profile_set = dytc_profile_set, +}; + static void dytc_profile_refresh(void) { enum platform_profile_option profile; @@ -10568,24 +10612,14 @@ static void dytc_profile_refresh(void) err = convert_dytc_to_profile(funcmode, perfmode, &profile); if (!err && profile != dytc_current_profile) { dytc_current_profile = profile; - platform_profile_notify(); + platform_profile_notify(tpacpi_pprof); } } -static struct platform_profile_handler dytc_profile = { - .profile_get = dytc_profile_get, - .profile_set = dytc_profile_set, -}; - static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) { int err, output; - /* Setup supported modes */ - set_bit(PLATFORM_PROFILE_LOW_POWER, dytc_profile.choices); - set_bit(PLATFORM_PROFILE_BALANCED, dytc_profile.choices); - set_bit(PLATFORM_PROFILE_PERFORMANCE, dytc_profile.choices); - err = dytc_command(DYTC_CMD_QUERY, &output); if (err) return err; @@ -10593,6 +10627,7 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) if (output & BIT(DYTC_QUERY_ENABLE_BIT)) dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF; + dbg_printk(TPACPI_DBG_INIT, "DYTC version %d\n", dytc_version); /* Check DYTC is enabled and supports mode setting */ if (dytc_version < 5) return -ENODEV; @@ -10631,6 +10666,11 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) } } else if (dytc_capabilities & BIT(DYTC_FC_PSC)) { /* PSC MODE */ pr_debug("PSC is supported\n"); + if (dytc_version >= 9) { /* update profiles for DYTC 9 and up */ + platform_psc_profile_lowpower = DYTC_MODE_PSCV9_LOWPOWER; + platform_psc_profile_balanced = DYTC_MODE_PSCV9_BALANCE; + platform_psc_profile_performance = DYTC_MODE_PSCV9_PERFORM; + } } else { dbg_printk(TPACPI_DBG_INIT, "No DYTC support available\n"); return -ENODEV; @@ -10640,12 +10680,13 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) "DYTC version %d: thermal mode available\n", dytc_version); /* Create platform_profile structure and register */ - err = platform_profile_register(&dytc_profile); + tpacpi_pprof = platform_profile_register(&tpacpi_pdev->dev, "thinkpad-acpi-profile", + NULL, &dytc_profile_ops); /* * If for some reason platform_profiles aren't enabled * don't quit terminally. */ - if (err) + if (IS_ERR(tpacpi_pprof)) return -ENODEV; /* Ensure initial values are correct */ @@ -10660,7 +10701,8 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) static void dytc_profile_exit(void) { - platform_profile_remove(); + if (!IS_ERR_OR_NULL(tpacpi_pprof)) + platform_profile_remove(tpacpi_pprof); } static struct ibm_struct dytc_profile_driver_data = { @@ -11682,7 +11724,7 @@ static int __init set_ibm_param(const char *val, const struct kernel_param *kp) if (strcmp(ibm->name, kp->name) == 0 && ibm->write) { if (strlen(val) > sizeof(ibms_init[i].param) - 1) return -ENOSPC; - strcpy(ibms_init[i].param, val); + strscpy(ibms_init[i].param, val); return 0; } } diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 78a5aac2dcfd..5ad3a7183d33 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -2755,7 +2755,7 @@ static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev) } static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, - struct serio *port) + struct serio *port, void *context) { if (str & I8042_STR_AUXDATA) return false; @@ -2915,7 +2915,7 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) if (ec_handle && acpi_has_method(ec_handle, "NTFY")) { INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work); - error = i8042_install_filter(toshiba_acpi_i8042_filter); + error = i8042_install_filter(toshiba_acpi_i8042_filter, NULL); if (error) { pr_err("Error installing key filter\n"); goto err_free_dev; diff --git a/drivers/platform/x86/wmi-bmof.c b/drivers/platform/x86/wmi-bmof.c index df6f0ae6e6c7..3e33da36da8a 100644 --- a/drivers/platform/x86/wmi-bmof.c +++ b/drivers/platform/x86/wmi-bmof.c @@ -20,66 +20,66 @@ #define WMI_BMOF_GUID "05901221-D566-11D1-B2F0-00A0C9062910" -struct bmof_priv { - union acpi_object *bmofdata; - struct bin_attribute bmof_bin_attr; -}; - -static ssize_t read_bmof(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, +static ssize_t bmof_read(struct file *filp, struct kobject *kobj, const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { - struct bmof_priv *priv = container_of(attr, struct bmof_priv, bmof_bin_attr); + struct device *dev = kobj_to_dev(kobj); + union acpi_object *obj = dev_get_drvdata(dev); - return memory_read_from_buffer(buf, count, &off, priv->bmofdata->buffer.pointer, - priv->bmofdata->buffer.length); + return memory_read_from_buffer(buf, count, &off, obj->buffer.pointer, obj->buffer.length); } -static int wmi_bmof_probe(struct wmi_device *wdev, const void *context) +static const BIN_ATTR_ADMIN_RO(bmof, 0); + +static const struct bin_attribute * const bmof_attrs[] = { + &bin_attr_bmof, + NULL +}; + +static size_t bmof_bin_size(struct kobject *kobj, const struct bin_attribute *attr, int n) { - struct bmof_priv *priv; - int ret; + struct device *dev = kobj_to_dev(kobj); + union acpi_object *obj = dev_get_drvdata(dev); + + return obj->buffer.length; +} - priv = devm_kzalloc(&wdev->dev, sizeof(struct bmof_priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; +static const struct attribute_group bmof_group = { + .bin_size = bmof_bin_size, + .bin_attrs_new = bmof_attrs, +}; + +static const struct attribute_group *bmof_groups[] = { + &bmof_group, + NULL +}; - dev_set_drvdata(&wdev->dev, priv); +static int wmi_bmof_probe(struct wmi_device *wdev, const void *context) +{ + union acpi_object *obj; - priv->bmofdata = wmidev_block_query(wdev, 0); - if (!priv->bmofdata) { + obj = wmidev_block_query(wdev, 0); + if (!obj) { dev_err(&wdev->dev, "failed to read Binary MOF\n"); return -EIO; } - if (priv->bmofdata->type != ACPI_TYPE_BUFFER) { + if (obj->type != ACPI_TYPE_BUFFER) { dev_err(&wdev->dev, "Binary MOF is not a buffer\n"); - ret = -EIO; - goto err_free; + kfree(obj); + return -EIO; } - sysfs_bin_attr_init(&priv->bmof_bin_attr); - priv->bmof_bin_attr.attr.name = "bmof"; - priv->bmof_bin_attr.attr.mode = 0400; - priv->bmof_bin_attr.read = read_bmof; - priv->bmof_bin_attr.size = priv->bmofdata->buffer.length; - - ret = device_create_bin_file(&wdev->dev, &priv->bmof_bin_attr); - if (ret) - goto err_free; + dev_set_drvdata(&wdev->dev, obj); return 0; - - err_free: - kfree(priv->bmofdata); - return ret; } static void wmi_bmof_remove(struct wmi_device *wdev) { - struct bmof_priv *priv = dev_get_drvdata(&wdev->dev); + union acpi_object *obj = dev_get_drvdata(&wdev->dev); - device_remove_bin_file(&wdev->dev, &priv->bmof_bin_attr); - kfree(priv->bmofdata); + kfree(obj); } static const struct wmi_device_id wmi_bmof_id_table[] = { @@ -90,6 +90,7 @@ static const struct wmi_device_id wmi_bmof_id_table[] = { static struct wmi_driver wmi_bmof_driver = { .driver = { .name = "wmi-bmof", + .dev_groups = bmof_groups, }, .probe = wmi_bmof_probe, .remove = wmi_bmof_remove, diff --git a/drivers/platform/x86/x86-android-tablets/Kconfig b/drivers/platform/x86/x86-android-tablets/Kconfig index a67bddc43007..193da15ee01c 100644 --- a/drivers/platform/x86/x86-android-tablets/Kconfig +++ b/drivers/platform/x86/x86-android-tablets/Kconfig @@ -10,6 +10,7 @@ config X86_ANDROID_TABLETS depends on ACPI && EFI && PCI select NEW_LEDS select LEDS_CLASS + select POWER_SUPPLY help X86 tablets which ship with Android as (part of) the factory image typically have various problems with their DSDTs. The factory kernels diff --git a/drivers/platform/x86/x86-android-tablets/Makefile b/drivers/platform/x86/x86-android-tablets/Makefile index 41ece5a37137..313be30548bc 100644 --- a/drivers/platform/x86/x86-android-tablets/Makefile +++ b/drivers/platform/x86/x86-android-tablets/Makefile @@ -3,7 +3,7 @@ # X86 Android tablet support Makefile # +obj-$(CONFIG_X86_ANDROID_TABLETS) += vexia_atla10_ec.o obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o - x86-android-tablets-y := core.o dmi.o shared-psy-info.o \ asus.o lenovo.o other.o diff --git a/drivers/platform/x86/x86-android-tablets/asus.c b/drivers/platform/x86/x86-android-tablets/asus.c index 07fbeab2319a..7dde63b9943f 100644 --- a/drivers/platform/x86/x86-android-tablets/asus.c +++ b/drivers/platform/x86/x86-android-tablets/asus.c @@ -145,8 +145,8 @@ static const struct x86_i2c_client_info asus_me176c_i2c_clients[] __initconst = static const struct x86_serdev_info asus_me176c_serdevs[] __initconst = { { - .ctrl_hid = "80860F0A", - .ctrl_uid = "2", + .ctrl.acpi.hid = "80860F0A", + .ctrl.acpi.uid = "2", .ctrl_devname = "serial0", .serdev_hid = "BCM2E3A", }, diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c index 40b5cd9b172f..2a9c47178505 100644 --- a/drivers/platform/x86/x86-android-tablets/core.c +++ b/drivers/platform/x86/x86-android-tablets/core.c @@ -212,7 +212,7 @@ static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info if (board_info.irq < 0) return board_info.irq; - if (dev_info->use_pci_devname) + if (dev_info->use_pci) adap = get_i2c_adap_by_pci_parent(client_info); else adap = get_i2c_adap_by_handle(client_info); @@ -271,15 +271,32 @@ static __init int x86_instantiate_spi_dev(const struct x86_dev_info *dev_info, i return 0; } -static __init int x86_instantiate_serdev(const struct x86_serdev_info *info, int idx) +static __init struct device * +get_serdev_controller_by_pci_parent(const struct x86_serdev_info *info) { + struct pci_dev *pdev; + + pdev = pci_get_domain_bus_and_slot(0, 0, info->ctrl.pci.devfn); + if (!pdev) + return ERR_PTR(-EPROBE_DEFER); + + /* This puts our reference on pdev and returns a ref on the ctrl */ + return get_serdev_controller_from_parent(&pdev->dev, 0, info->ctrl_devname); +} + +static __init int x86_instantiate_serdev(const struct x86_dev_info *dev_info, int idx) +{ + const struct x86_serdev_info *info = &dev_info->serdev_info[idx]; struct acpi_device *serdev_adev; struct serdev_device *serdev; struct device *ctrl_dev; int ret = -ENODEV; - ctrl_dev = get_serdev_controller(info->ctrl_hid, info->ctrl_uid, 0, - info->ctrl_devname); + if (dev_info->use_pci) + ctrl_dev = get_serdev_controller_by_pci_parent(info); + else + ctrl_dev = get_serdev_controller(info->ctrl.acpi.hid, info->ctrl.acpi.uid, + 0, info->ctrl_devname); if (IS_ERR(ctrl_dev)) return PTR_ERR(ctrl_dev); @@ -446,7 +463,7 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev) serdev_count = dev_info->serdev_count; for (i = 0; i < serdev_count; i++) { - ret = x86_instantiate_serdev(&dev_info->serdev_info[i], i); + ret = x86_instantiate_serdev(dev_info, i); if (ret < 0) { x86_android_tablet_remove(pdev); return ret; diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index a60efbaf4817..1241a97cda39 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -178,8 +178,8 @@ static const struct platform_device_info lenovo_yb1_x90_pdevs[] __initconst = { */ static const struct x86_serdev_info lenovo_yb1_x90_serdevs[] __initconst = { { - .ctrl_hid = "8086228A", - .ctrl_uid = "1", + .ctrl.acpi.hid = "8086228A", + .ctrl.acpi.uid = "1", .ctrl_devname = "serial0", .serdev_hid = "BCM2E1A", }, diff --git a/drivers/platform/x86/x86-android-tablets/other.c b/drivers/platform/x86/x86-android-tablets/other.c index 3cd0db74c6c9..1d93d9edb23f 100644 --- a/drivers/platform/x86/x86-android-tablets/other.c +++ b/drivers/platform/x86/x86-android-tablets/other.c @@ -715,6 +715,14 @@ static const struct x86_i2c_client_info vexia_edu_atla10_i2c_clients[] __initcon } }; +static const struct x86_serdev_info vexia_edu_atla10_serdevs[] __initconst = { + { + .ctrl.pci.devfn = PCI_DEVFN(0x1e, 3), + .ctrl_devname = "serial0", + .serdev_hid = "OBDA8723", + }, +}; + static struct gpiod_lookup_table vexia_edu_atla10_ft5416_gpios = { .dev_id = "i2c-FTSC1000", .table = { @@ -755,9 +763,11 @@ static int __init vexia_edu_atla10_init(struct device *dev) const struct x86_dev_info vexia_edu_atla10_info __initconst = { .i2c_client_info = vexia_edu_atla10_i2c_clients, .i2c_client_count = ARRAY_SIZE(vexia_edu_atla10_i2c_clients), + .serdev_info = vexia_edu_atla10_serdevs, + .serdev_count = ARRAY_SIZE(vexia_edu_atla10_serdevs), .gpiod_lookup_tables = vexia_edu_atla10_gpios, .init = vexia_edu_atla10_init, - .use_pci_devname = true, + .use_pci = true, }; /* diff --git a/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c b/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c new file mode 100644 index 000000000000..5d02af1c5aaa --- /dev/null +++ b/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * power_supply class (battery) driver for the I2C attached embedded controller + * found on Vexia EDU ATLA 10 (9V version) tablets. + * + * This is based on the ACPI Battery device in the DSDT which should work + * expect that it expects the I2C controller to be enumerated as an ACPI + * device and the tablet's BIOS enumerates all LPSS devices as PCI devices + * (and changing the LPSS BIOS settings from PCI -> ACPI does not work). + * + * Copyright (c) 2024 Hans de Goede <hansg@kernel.org> + */ + +#include <linux/bits.h> +#include <linux/devm-helpers.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/power_supply.h> +#include <linux/types.h> +#include <linux/workqueue.h> + +#include <asm/byteorder.h> + +/* State field uses ACPI Battery spec status bits */ +#define ACPI_BATTERY_STATE_DISCHARGING BIT(0) +#define ACPI_BATTERY_STATE_CHARGING BIT(1) + +#define ATLA10_EC_BATTERY_STATE_COMMAND 0x87 +#define ATLA10_EC_BATTERY_INFO_COMMAND 0x88 + +/* From broken ACPI battery device in DSDT */ +#define ATLA10_EC_VOLTAGE_MIN_DESIGN_uV 3750000 + +/* Update data every 5 seconds */ +#define UPDATE_INTERVAL_JIFFIES (5 * HZ) + +struct atla10_ec_battery_state { + u8 status; /* Using ACPI Battery spec status bits */ + u8 capacity; /* Percent */ + __le16 charge_now_mAh; + __le16 voltage_now_mV; + __le16 current_now_mA; + __le16 charge_full_mAh; + __le16 temp; /* centi degrees Celsius */ +} __packed; + +struct atla10_ec_battery_info { + __le16 charge_full_design_mAh; + __le16 voltage_now_mV; /* Should be design voltage, but is not ? */ + __le16 charge_full_design2_mAh; +} __packed; + +struct atla10_ec_data { + struct i2c_client *client; + struct power_supply *psy; + struct delayed_work work; + struct mutex update_lock; + struct atla10_ec_battery_info info; + struct atla10_ec_battery_state state; + bool valid; /* true if state is valid */ + unsigned long last_update; /* In jiffies */ +}; + +static int atla10_ec_cmd(struct atla10_ec_data *data, u8 cmd, u8 len, u8 *values) +{ + struct device *dev = &data->client->dev; + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + ret = i2c_smbus_read_block_data(data->client, cmd, buf); + if (ret != len) { + dev_err(dev, "I2C command 0x%02x error: %d\n", cmd, ret); + return -EIO; + } + + memcpy(values, buf, len); + return 0; +} + +static int atla10_ec_update(struct atla10_ec_data *data) +{ + int ret; + + if (data->valid && time_before(jiffies, data->last_update + UPDATE_INTERVAL_JIFFIES)) + return 0; + + ret = atla10_ec_cmd(data, ATLA10_EC_BATTERY_STATE_COMMAND, + sizeof(data->state), (u8 *)&data->state); + if (ret) + return ret; + + data->last_update = jiffies; + data->valid = true; + return 0; +} + +static int atla10_ec_psy_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct atla10_ec_data *data = power_supply_get_drvdata(psy); + int charge_now_mAh, charge_full_mAh, ret; + + guard(mutex)(&data->update_lock); + + ret = atla10_ec_update(data); + if (ret) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (data->state.status & ACPI_BATTERY_STATE_DISCHARGING) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (data->state.status & ACPI_BATTERY_STATE_CHARGING) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (data->state.capacity == 100) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = data->state.capacity; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + /* + * The EC has a bug where it reports charge-full-design as + * charge-now when the battery is full. Clamp charge-now to + * charge-full to workaround this. + */ + charge_now_mAh = le16_to_cpu(data->state.charge_now_mAh); + charge_full_mAh = le16_to_cpu(data->state.charge_full_mAh); + val->intval = min(charge_now_mAh, charge_full_mAh) * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = le16_to_cpu(data->state.voltage_now_mV) * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = le16_to_cpu(data->state.current_now_mA) * 1000; + /* + * Documentation/ABI/testing/sysfs-class-power specifies + * negative current for discharging. + */ + if (data->state.status & ACPI_BATTERY_STATE_DISCHARGING) + val->intval = -val->intval; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = le16_to_cpu(data->state.charge_full_mAh) * 1000; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = le16_to_cpu(data->state.temp) / 10; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = le16_to_cpu(data->info.charge_full_design_mAh) * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = ATLA10_EC_VOLTAGE_MIN_DESIGN_uV; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void atla10_ec_external_power_changed_work(struct work_struct *work) +{ + struct atla10_ec_data *data = container_of(work, struct atla10_ec_data, work.work); + + dev_dbg(&data->client->dev, "External power changed\n"); + data->valid = false; + power_supply_changed(data->psy); +} + +static void atla10_ec_external_power_changed(struct power_supply *psy) +{ + struct atla10_ec_data *data = power_supply_get_drvdata(psy); + + /* After charger plug in/out wait 0.5s for things to stabilize */ + mod_delayed_work(system_wq, &data->work, HZ / 2); +} + +static const enum power_supply_property atla10_ec_psy_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, +}; + +static const struct power_supply_desc atla10_ec_psy_desc = { + .name = "atla10_ec_battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = atla10_ec_psy_props, + .num_properties = ARRAY_SIZE(atla10_ec_psy_props), + .get_property = atla10_ec_psy_get_property, + .external_power_changed = atla10_ec_external_power_changed, +}; + +static int atla10_ec_probe(struct i2c_client *client) +{ + struct power_supply_config psy_cfg = { }; + struct device *dev = &client->dev; + struct atla10_ec_data *data; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + psy_cfg.drv_data = data; + data->client = client; + + ret = devm_mutex_init(dev, &data->update_lock); + if (ret) + return ret; + + ret = devm_delayed_work_autocancel(dev, &data->work, + atla10_ec_external_power_changed_work); + if (ret) + return ret; + + ret = atla10_ec_cmd(data, ATLA10_EC_BATTERY_INFO_COMMAND, + sizeof(data->info), (u8 *)&data->info); + if (ret) + return ret; + + data->psy = devm_power_supply_register(dev, &atla10_ec_psy_desc, &psy_cfg); + return PTR_ERR_OR_ZERO(data->psy); +} + +static const struct i2c_device_id atla10_ec_id_table[] = { + { "vexia_atla10_ec" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, atla10_ec_id_table); + +static struct i2c_driver atla10_ec_driver = { + .driver = { + .name = "vexia_atla10_ec", + }, + .probe = atla10_ec_probe, + .id_table = atla10_ec_id_table, +}; +module_i2c_driver(atla10_ec_driver); + +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_DESCRIPTION("Battery driver for Vexia EDU ATLA 10 tablet EC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h index 0fc7e8cff672..63a38a0069ba 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h @@ -57,8 +57,15 @@ struct x86_spi_dev_info { }; struct x86_serdev_info { - const char *ctrl_hid; - const char *ctrl_uid; + union { + struct { + const char *hid; + const char *uid; + } acpi; + struct { + unsigned int devfn; + } pci; + } ctrl; const char *ctrl_devname; /* * ATM the serdev core only supports of or ACPI matching; and so far all @@ -91,7 +98,7 @@ struct x86_dev_info { int gpio_button_count; int (*init)(struct device *dev); void (*exit)(void); - bool use_pci_devname; + bool use_pci; }; int x86_android_tablet_get_gpiod(const char *chip, int pin, const char *con_id, |