From 31e56f2305bbc213e9e9f0e21826cceab70d5336 Mon Sep 17 00:00:00 2001 From: Zhang Xianwei Date: Tue, 4 Sep 2018 15:08:19 +0800 Subject: platform/x86: ideapad-laptop: Use __func__ instead of read_ec_cmd in pr_err Refer to the commit f1395edbcec8 ("platform/x86: ideapad-laptop: Use __func__ instead of write_ec_cmd in pr_err"), prefer using '"%s...", __func__' to using 'read_ec_cmd' in read_ec_data. Signed-off-by: Zhang Xianwei Acked-by: Ike Panhc Signed-off-by: Andy Shevchenko --- drivers/platform/x86/ideapad-laptop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index d4f1259ff5a2..f856d59361f2 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -212,7 +212,7 @@ static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data) return 0; } } - pr_err("timeout in read_ec_cmd\n"); + pr_err("timeout in %s\n", __func__); return -1; } -- cgit From 96402de65afcd9d7f12e04bf8a14d5a321aca4af Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 19 Sep 2018 20:14:51 +0200 Subject: platform/x86: intel_int0002_vgpio: Enable the driver on Bay Trail platforms The intel_int0002_vgpio driver was added to avoid an IRQ 9 storm on Cherry Trail platforms. When originally merged the CPU ID for Bay Trail SoCs was commented out of the list of valid CPU IDs because we did not have any reports of the IRQ storm on Bay Trail platforms. We now have a report of the IRQ 9 storm on the Bay Trail based Thinkpad Tablet 10 which is fixed by enabling this driver, so lets enable it on Bay Trail too. I've tested various other Bay Trail device with this driver enabled without adverse side-effects. BugLink: https://www.dpin.de/nf/finally-s0i3-is-there-thinkpad-tablet-10-sleeps-deeply-with-linux-kernel-4-15rc/#comments Reported-and-tested-by: Nicole Faerber Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_int0002_vgpio.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c index a473dc51b18d..987a3b03f225 100644 --- a/drivers/platform/x86/intel_int0002_vgpio.c +++ b/drivers/platform/x86/intel_int0002_vgpio.c @@ -57,11 +57,7 @@ #define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, } static const struct x86_cpu_id int0002_cpu_ids[] = { -/* - * Limit ourselves to Cherry Trail for now, until testing shows we - * need to handle the INT0002 device on Baytrail too. - * ICPU(INTEL_FAM6_ATOM_SILVERMONT1), * Valleyview, Bay Trail * - */ + ICPU(INTEL_FAM6_ATOM_SILVERMONT1), /* Valleyview, Bay Trail */ ICPU(INTEL_FAM6_ATOM_AIRMONT), /* Braswell, Cherry Trail */ {} }; -- cgit From c3b8e884defae3ad50956ee77bdba3638fe4852f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 24 Sep 2018 16:37:19 +0200 Subject: platform/x86: intel_int0002_vgpio: Implement irq_set_wake We were relying on the interrupt being shared with the ACPI SCI and the ACPI core calling irq_set_wake. But that does not always happen on Bay Trail devices, so we should do it ourselves. This fixes wake from USB not working on various Bay Trail devices. Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_int0002_vgpio.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c index 987a3b03f225..33c3489f5bc1 100644 --- a/drivers/platform/x86/intel_int0002_vgpio.c +++ b/drivers/platform/x86/intel_int0002_vgpio.c @@ -106,6 +106,21 @@ static void int0002_irq_mask(struct irq_data *data) outl(gpe_en_reg, GPE0A_EN_PORT); } +static int int0002_irq_set_wake(struct irq_data *data, unsigned int on) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct platform_device *pdev = to_platform_device(chip->parent); + int irq = platform_get_irq(pdev, 0); + + /* Propagate to parent irq */ + if (on) + enable_irq_wake(irq); + else + disable_irq_wake(irq); + + return 0; +} + static irqreturn_t int0002_irq(int irq, void *data) { struct gpio_chip *chip = data; @@ -128,6 +143,7 @@ static struct irq_chip int0002_irqchip = { .irq_ack = int0002_irq_ack, .irq_mask = int0002_irq_mask, .irq_unmask = int0002_irq_unmask, + .irq_set_wake = int0002_irq_set_wake, }; static int int0002_probe(struct platform_device *pdev) -- cgit From a8b60e484f3d245bf90e934d608f75c814aaa70c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 31 Aug 2018 11:34:31 +0300 Subject: platform/x86: intel_mid_powerbtn: Get rid of custom ICPU() macro Replace custom grown macro with generic INTEL_CPU_FAM6() one. No functional change intended. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_mid_powerbtn.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index d79fbf924b13..c93299002169 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -121,12 +121,9 @@ static const struct mid_pb_ddata mrfld_ddata = { .setup = mrfld_setup, }; -#define ICPU(model, ddata) \ - { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (kernel_ulong_t)&ddata } - static const struct x86_cpu_id mid_pb_cpu_ids[] = { - ICPU(INTEL_FAM6_ATOM_PENWELL, mfld_ddata), - ICPU(INTEL_FAM6_ATOM_MERRIFIELD, mrfld_ddata), + INTEL_CPU_FAM6(ATOM_PENWELL, mfld_ddata), + INTEL_CPU_FAM6(ATOM_MERRIFIELD, mrfld_ddata), {} }; -- cgit From 3836b816ddac7fcf4b3c9c55c5b57f41cea2a236 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 20 Sep 2018 21:44:16 -0400 Subject: platform/x86: acerhdf: clarify modinfo messages for BIOS override Normally, a module parameter for a BIOS check override implies "pretend you support this version" (and the user will enter their local version). However, this driver uses the model/BIOS module parameters in a way that is "pretend my system is the supported model XYZ with BIOS version ABC." which is less common. Since the help strings don't make such a distinction, one gets this somewhat frustrating scenario, where the user sees the error, enters *their* BIOS version and then gets the same error: root@gw:~# modprobe acerhdf acerhdf: Acer Aspire One Fan driver, v.0.7.0 acerhdf: unknown (unsupported) BIOS version Gateway /LT31 /v1.3307 , please report, aborting! modprobe: ERROR: could not insert 'acerhdf': Invalid argument root@gw:~# modprobe acerhdf force_bios=v1.3307 acerhdf: Acer Aspire One Fan driver, v.0.7.0 acerhdf: forcing BIOS version: v1.3307 acerhdf: unknown (unsupported) BIOS version Gateway /LT31 /v1.3307 , please report, aborting! modprobe: ERROR: could not insert 'acerhdf': Invalid argument Clarify the module param help text to make it clear that the driver expects a choice from existing supported models/versions. Cc: Peter Feuerer Cc: Darren Hart Cc: Andy Shevchenko Signed-off-by: Paul Gortmaker Signed-off-by: Andy Shevchenko Reviewed-by: Peter Feuerer --- drivers/platform/x86/acerhdf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index ea22591ee66f..3d5c4e183111 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -105,9 +105,9 @@ MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature"); module_param(verbose, uint, 0600); MODULE_PARM_DESC(verbose, "Enable verbose dmesg output"); module_param_string(force_bios, force_bios, 16, 0); -MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check"); +MODULE_PARM_DESC(force_bios, "Pretend system has this known supported BIOS version"); module_param_string(force_product, force_product, 16, 0); -MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check"); +MODULE_PARM_DESC(force_product, "Pretend system is this known supported model"); /* * cmd_off: to switch the fan completely off and check if the fan is off -- cgit From 291bd0e4605ee5d3cc8adefb79f8ef2047219571 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 20 Sep 2018 21:44:17 -0400 Subject: platform/x86: acerhdf: Enable ability to list supported systems This driver has two module parameters that allow an override of the checks for matching model and BIOS version. However, both parameters expect you to choose an entry from the existing list of supported systems, encoded within the driver itself. Without the source, such as in a binary distribution, the end user does not have access to this information, thus rendering the two module parameters essentially useless. Add a module parameter that allows the end user to dump the list of make/model/versions so that they can then pick one that most closely matches their own system. Cc: Peter Feuerer Cc: Darren Hart Cc: Andy Shevchenko Signed-off-by: Paul Gortmaker Signed-off-by: Andy Shevchenko Reviewed-by: Peter Feuerer --- drivers/platform/x86/acerhdf.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 3d5c4e183111..2735815c73c5 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -86,6 +86,7 @@ static unsigned int interval = 10; static unsigned int fanon = 60000; static unsigned int fanoff = 53000; static unsigned int verbose; +static unsigned int list_supported; static unsigned int fanstate = ACERHDF_FAN_AUTO; static char force_bios[16]; static char force_product[16]; @@ -104,6 +105,8 @@ module_param(fanoff, uint, 0600); MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature"); module_param(verbose, uint, 0600); MODULE_PARM_DESC(verbose, "Enable verbose dmesg output"); +module_param(list_supported, uint, 0600); +MODULE_PARM_DESC(list_supported, "List supported models and BIOS versions"); module_param_string(force_bios, force_bios, 16, 0); MODULE_PARM_DESC(force_bios, "Pretend system has this known supported BIOS version"); module_param_string(force_product, force_product, 16, 0); @@ -632,6 +635,17 @@ static int acerhdf_check_hardware(void) pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER); + if (list_supported) { + pr_info("List of supported Manufacturer/Model/BIOS:\n"); + pr_info("---------------------------------------------------\n"); + for (bt = bios_tbl; bt->vendor[0]; bt++) { + pr_info("%-13s | %-17s | %-10s\n", bt->vendor, + bt->product, bt->version); + } + pr_info("---------------------------------------------------\n"); + return -ECANCELED; + } + if (force_bios[0]) { version = force_bios; pr_info("forcing BIOS version: %s\n", version); -- cgit From 14c20688292583b3f7a8a595b65f6b5665616752 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 20 Sep 2018 21:44:18 -0400 Subject: platform/x86: acerhdf: Remove cut-and-paste trap from instructions Just like we avoid specifying actual block devices like sda for fdisk and dd examples, we should not specify specific thermal zones here. On the platform I was testing on, zone0 was acpitz, and zone1 was for this acerhdf driver. Make the printk such that it won't work with a blind cut-and-paste, and force the user to determine which zone is correct for this driver. Cc: Peter Feuerer Cc: Darren Hart Cc: Andy Shevchenko Signed-off-by: Paul Gortmaker Signed-off-by: Andy Shevchenko Reviewed-by: Peter Feuerer --- drivers/platform/x86/Kconfig | 5 ++++- drivers/platform/x86/acerhdf.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 0c1aa6c314f5..1fca33c97e8a 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -60,7 +60,10 @@ config ACERHDF After loading this driver the BIOS is still in control of the fan. To let the kernel handle the fan, do: - echo -n enabled > /sys/class/thermal/thermal_zone0/mode + echo -n enabled > /sys/class/thermal/thermal_zoneN/mode + where N=0,1,2... depending on the number of thermal nodes and the + detection order of your particular system. The "type" parameter + in the same node directory will tell you if it is "acerhdf". For more information about this driver see diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 2735815c73c5..fef3b727bc24 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -688,7 +688,7 @@ static int acerhdf_check_hardware(void) */ if (!kernelmode) { pr_notice("Fan control off, to enable do:\n"); - pr_notice("echo -n \"enabled\" > /sys/class/thermal/thermal_zone0/mode\n"); + pr_notice("echo -n \"enabled\" > /sys/class/thermal/thermal_zoneN/mode # N=0,1,2...\n"); } return 0; -- cgit From 684238d79ad85c5e19a71bb5818e77e329912fbc Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 20 Sep 2018 21:44:19 -0400 Subject: platform/x86: acerhdf: Add BIOS entry for Gateway LT31 v1.3307 To fix: acerhdf: unknown (unsupported) BIOS version Gateway /LT31 /v1.3307 , please report, aborting! As can be seen in the context, the BIOS registers haven't changed in the previous versions, so the assumption is they won't have changed in this last update for this somewhat older platform either. Cc: Peter Feuerer Cc: Darren Hart Cc: Andy Shevchenko Signed-off-by: Paul Gortmaker Signed-off-by: Andy Shevchenko Reviewed-by: Peter Feuerer --- drivers/platform/x86/acerhdf.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index fef3b727bc24..eddcd8e94a88 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -236,6 +236,7 @@ static const struct bios_settings bios_tbl[] = { {"Gateway", "LT31", "v1.3201", 0x55, 0x58, {0x9e, 0x00}, 0}, {"Gateway", "LT31", "v1.3302", 0x55, 0x58, {0x9e, 0x00}, 0}, {"Gateway", "LT31", "v1.3303t", 0x55, 0x58, {0x9e, 0x00}, 0}, + {"Gateway", "LT31", "v1.3307", 0x55, 0x58, {0x9e, 0x00}, 0}, /* Packard Bell */ {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00}, 0}, {"Packard Bell", "DOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00}, 0}, -- cgit From 1d0c3fd01afbd52cc365460d46176b6b4131960d Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 20 Sep 2018 21:44:20 -0400 Subject: platform/x86: acerhdf: mark appropriate content with __init prefix These three functions are only called from the probe code which is already marked __init and hence these can be __init as well. Cc: Peter Feuerer Cc: Darren Hart Cc: Andy Shevchenko Signed-off-by: Paul Gortmaker Signed-off-by: Andy Shevchenko Reviewed-by: Peter Feuerer --- drivers/platform/x86/acerhdf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index eddcd8e94a88..5f579fcde315 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -619,7 +619,7 @@ static int str_starts_with(const char *str, const char *start) } /* check hardware */ -static int acerhdf_check_hardware(void) +static int __init acerhdf_check_hardware(void) { char const *vendor, *version, *product; const struct bios_settings *bt = NULL; @@ -695,7 +695,7 @@ static int acerhdf_check_hardware(void) return 0; } -static int acerhdf_register_platform(void) +static int __init acerhdf_register_platform(void) { int err = 0; @@ -727,7 +727,7 @@ static void acerhdf_unregister_platform(void) platform_driver_unregister(&acerhdf_driver); } -static int acerhdf_register_thermal(void) +static int __init acerhdf_register_thermal(void) { cl_dev = thermal_cooling_device_register("acerhdf-fan", NULL, &acerhdf_cooling_ops); -- cgit From f7eaf3fb9d10cc72669fe8c8bd204087a65e50f6 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 20 Sep 2018 21:44:21 -0400 Subject: platform/x86: acerhdf: restructure to allow large BIOS table be __initconst There is a table of Vendor/Model/Version {control data} in this driver, but outside of the initial probe, the V/M/V is never used again, and neither are any of the entries for platforms other than the one which matches the running target. By simply storing the {control data} for the matched platform, we can mark the large table __initconst, which reduces the loaded driver size by 20 percent. Before: root@gw:~/git/linux-head# lsmod Module Size Used by acerhdf 20480 0 root@gw:~/git/linux-head# After: root@gw:~/git/linux-head# lsmod Module Size Used by acerhdf 16384 0 root@gw:~/git/linux-head# Cc: Peter Feuerer Cc: Darren Hart Cc: Andy Shevchenko Signed-off-by: Paul Gortmaker Signed-off-by: Andy Shevchenko Reviewed-by: Peter Feuerer --- drivers/platform/x86/acerhdf.c | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 5f579fcde315..505224225378 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -133,7 +133,7 @@ static const struct manualcmd mcmd = { .moff = 0xff, }; -/* BIOS settings */ +/* BIOS settings - only used during probe */ struct bios_settings { const char *vendor; const char *product; @@ -144,8 +144,18 @@ struct bios_settings { int mcmd_enable; }; +/* This could be a daughter struct in the above, but not worth the redirect */ +struct ctrl_settings { + u8 fanreg; + u8 tempreg; + struct fancmd cmd; + int mcmd_enable; +}; + +static struct ctrl_settings ctrl_cfg __read_mostly; + /* Register addresses and values for different BIOS versions */ -static const struct bios_settings bios_tbl[] = { +static const struct bios_settings bios_tbl[] __initconst = { /* AOA110 */ {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00}, 0}, {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00}, 0}, @@ -260,8 +270,6 @@ static const struct bios_settings bios_tbl[] = { {"", "", "", 0, 0, {0, 0}, 0} }; -static const struct bios_settings *bios_cfg __read_mostly; - /* * this struct is used to instruct thermal layer to use bang_bang instead of * default governor for acerhdf @@ -274,7 +282,7 @@ static int acerhdf_get_temp(int *temp) { u8 read_temp; - if (ec_read(bios_cfg->tempreg, &read_temp)) + if (ec_read(ctrl_cfg.tempreg, &read_temp)) return -EINVAL; *temp = read_temp * 1000; @@ -286,10 +294,10 @@ static int acerhdf_get_fanstate(int *state) { u8 fan; - if (ec_read(bios_cfg->fanreg, &fan)) + if (ec_read(ctrl_cfg.fanreg, &fan)) return -EINVAL; - if (fan != bios_cfg->cmd.cmd_off) + if (fan != ctrl_cfg.cmd.cmd_off) *state = ACERHDF_FAN_AUTO; else *state = ACERHDF_FAN_OFF; @@ -310,13 +318,13 @@ static void acerhdf_change_fanstate(int state) state = ACERHDF_FAN_AUTO; } - cmd = (state == ACERHDF_FAN_OFF) ? bios_cfg->cmd.cmd_off - : bios_cfg->cmd.cmd_auto; + cmd = (state == ACERHDF_FAN_OFF) ? ctrl_cfg.cmd.cmd_off + : ctrl_cfg.cmd.cmd_auto; fanstate = state; - ec_write(bios_cfg->fanreg, cmd); + ec_write(ctrl_cfg.fanreg, cmd); - if (bios_cfg->mcmd_enable && state == ACERHDF_FAN_OFF) { + if (ctrl_cfg.mcmd_enable && state == ACERHDF_FAN_OFF) { if (verbose) pr_notice("turning off fan manually\n"); ec_write(mcmd.mreg, mcmd.moff); @@ -623,6 +631,7 @@ static int __init acerhdf_check_hardware(void) { char const *vendor, *version, *product; const struct bios_settings *bt = NULL; + int found = 0; /* get BIOS data */ vendor = dmi_get_system_info(DMI_SYS_VENDOR); @@ -672,17 +681,23 @@ static int __init acerhdf_check_hardware(void) if (str_starts_with(vendor, bt->vendor) && str_starts_with(product, bt->product) && str_starts_with(version, bt->version)) { - bios_cfg = bt; + found = 1; break; } } - if (!bios_cfg) { + if (!found) { pr_err("unknown (unsupported) BIOS version %s/%s/%s, please report, aborting!\n", vendor, product, version); return -EINVAL; } + /* Copy control settings from BIOS table before we free it. */ + ctrl_cfg.fanreg = bt->fanreg; + ctrl_cfg.tempreg = bt->tempreg; + memcpy(&ctrl_cfg.cmd, &bt->cmd, sizeof(struct fancmd)); + ctrl_cfg.mcmd_enable = bt->mcmd_enable; + /* * if started with kernel mode off, prevent the kernel from switching * off the fan -- cgit From 6aecee6ad41cf97c0270f72da032c10eef025bf0 Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Wed, 26 Sep 2018 16:50:17 -0500 Subject: firmware: dell_rbu: Make payload memory uncachable The dell_rbu driver takes firmware update payloads and puts them in memory so the system BIOS can find them after a reboot. This sometimes fails (though rarely), because the memory containing the payload is in the CPU cache but never gets written back to main memory before the system is rebooted (CPU cache contents are lost on reboot). With this patch, the payload memory will be changed to uncachable to ensure that the payload is actually in main memory before the system is rebooted. Signed-off-by: Stuart Hayes Signed-off-by: Andy Shevchenko --- drivers/firmware/dell_rbu.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/dell_rbu.c b/drivers/firmware/dell_rbu.c index fb8af5cb7c9b..ccefa84f7305 100644 --- a/drivers/firmware/dell_rbu.c +++ b/drivers/firmware/dell_rbu.c @@ -45,6 +45,7 @@ #include #include #include +#include MODULE_AUTHOR("Abhay Salunke "); MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems"); @@ -181,6 +182,11 @@ static int create_packet(void *data, size_t length) packet_data_temp_buf = NULL; } } + /* + * set to uncachable or it may never get written back before reboot + */ + set_memory_uc((unsigned long)packet_data_temp_buf, 1 << ordernum); + spin_lock(&rbu_data.lock); newpacket->data = packet_data_temp_buf; @@ -349,6 +355,8 @@ static void packet_empty_list(void) * to make sure there are no stale RBU packets left in memory */ memset(newpacket->data, 0, rbu_data.packetsize); + set_memory_wb((unsigned long)newpacket->data, + 1 << newpacket->ordernum); free_pages((unsigned long) newpacket->data, newpacket->ordernum); kfree(newpacket); -- cgit From 12c956c4f32e08799de452abe1e1ec6021b1e41f Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Wed, 26 Sep 2018 16:50:18 -0500 Subject: firmware: dcdbas: Add support for WSMT ACPI table If the WSMT ACPI table is present and indicates that a fixed communication buffer should be used, use the firmware-specified buffer instead of allocating a buffer in memory for communications between the dcdbas driver and firmare. Signed-off-by: Stuart Hayes Signed-off-by: Andy Shevchenko --- drivers/firmware/dcdbas.c | 123 +++++++++++++++++++++++++++++++++++++++++++--- drivers/firmware/dcdbas.h | 10 ++++ 2 files changed, 127 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index 0bdea60c65dd..ae28e48ff7dc 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -41,7 +42,7 @@ #include "dcdbas.h" #define DRIVER_NAME "dcdbas" -#define DRIVER_VERSION "5.6.0-3.2" +#define DRIVER_VERSION "5.6.0-3.3" #define DRIVER_DESCRIPTION "Dell Systems Management Base Driver" static struct platform_device *dcdbas_pdev; @@ -49,19 +50,23 @@ static struct platform_device *dcdbas_pdev; static u8 *smi_data_buf; static dma_addr_t smi_data_buf_handle; static unsigned long smi_data_buf_size; +static unsigned long max_smi_data_buf_size = MAX_SMI_DATA_BUF_SIZE; static u32 smi_data_buf_phys_addr; static DEFINE_MUTEX(smi_data_lock); +static u8 *eps_buffer; static unsigned int host_control_action; static unsigned int host_control_smi_type; static unsigned int host_control_on_shutdown; +static bool wsmt_enabled; + /** * smi_data_buf_free: free SMI data buffer */ static void smi_data_buf_free(void) { - if (!smi_data_buf) + if (!smi_data_buf || wsmt_enabled) return; dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", @@ -86,7 +91,7 @@ static int smi_data_buf_realloc(unsigned long size) if (smi_data_buf_size >= size) return 0; - if (size > MAX_SMI_DATA_BUF_SIZE) + if (size > max_smi_data_buf_size) return -EINVAL; /* new buffer is needed */ @@ -169,7 +174,7 @@ static ssize_t smi_data_write(struct file *filp, struct kobject *kobj, { ssize_t ret; - if ((pos + count) > MAX_SMI_DATA_BUF_SIZE) + if ((pos + count) > max_smi_data_buf_size) return -EINVAL; mutex_lock(&smi_data_lock); @@ -322,8 +327,20 @@ static ssize_t smi_request_store(struct device *dev, ret = count; break; case 1: - /* Calling Interface SMI */ - smi_cmd->ebx = (u32) virt_to_phys(smi_cmd->command_buffer); + /* + * Calling Interface SMI + * + * Provide physical address of command buffer field within + * the struct smi_cmd to BIOS. + * + * Because the address that smi_cmd (smi_data_buf) points to + * will be from memremap() of a non-memory address if WSMT + * is present, we can't use virt_to_phys() on smi_cmd, so + * we have to use the physical address that was saved when + * the virtual address for smi_cmd was received. + */ + smi_cmd->ebx = smi_data_buf_phys_addr + + offsetof(struct smi_cmd, command_buffer); ret = dcdbas_smi_request(smi_cmd); if (!ret) ret = count; @@ -482,6 +499,93 @@ static void dcdbas_host_control(void) } } +/* WSMT */ + +static u8 checksum(u8 *buffer, u8 length) +{ + u8 sum = 0; + u8 *end = buffer + length; + + while (buffer < end) + sum += *buffer++; + return sum; +} + +static inline struct smm_eps_table *check_eps_table(u8 *addr) +{ + struct smm_eps_table *eps = (struct smm_eps_table *)addr; + + if (strncmp(eps->smm_comm_buff_anchor, SMM_EPS_SIG, 4) != 0) + return NULL; + + if (checksum(addr, eps->length) != 0) + return NULL; + + return eps; +} + +static int dcdbas_check_wsmt(void) +{ + struct acpi_table_wsmt *wsmt = NULL; + struct smm_eps_table *eps = NULL; + u64 remap_size; + u8 *addr; + + acpi_get_table(ACPI_SIG_WSMT, 0, (struct acpi_table_header **)&wsmt); + if (!wsmt) + return 0; + + /* Check if WSMT ACPI table shows that protection is enabled */ + if (!(wsmt->protection_flags & ACPI_WSMT_FIXED_COMM_BUFFERS) || + !(wsmt->protection_flags & ACPI_WSMT_COMM_BUFFER_NESTED_PTR_PROTECTION)) + return 0; + + /* Scan for EPS (entry point structure) */ + for (addr = (u8 *)__va(0xf0000); + addr < (u8 *)__va(0x100000 - sizeof(struct smm_eps_table)); + addr += 16) { + eps = check_eps_table(addr); + if (eps) + break; + } + + if (!eps) { + dev_dbg(&dcdbas_pdev->dev, "found WSMT, but no EPS found\n"); + return -ENODEV; + } + + /* + * Get physical address of buffer and map to virtual address. + * Table gives size in 4K pages, regardless of actual system page size. + */ + if (upper_32_bits(eps->smm_comm_buff_addr + 8)) { + dev_warn(&dcdbas_pdev->dev, "found WSMT, but EPS buffer address is above 4GB\n"); + return -EINVAL; + } + /* + * Limit remap size to MAX_SMI_DATA_BUF_SIZE + 8 (since the first 8 + * bytes are used for a semaphore, not the data buffer itself). + */ + remap_size = eps->num_of_4k_pages * PAGE_SIZE; + if (remap_size > MAX_SMI_DATA_BUF_SIZE + 8) + remap_size = MAX_SMI_DATA_BUF_SIZE + 8; + eps_buffer = memremap(eps->smm_comm_buff_addr, remap_size, MEMREMAP_WB); + if (!eps_buffer) { + dev_warn(&dcdbas_pdev->dev, "found WSMT, but failed to map EPS buffer\n"); + return -ENOMEM; + } + + /* First 8 bytes is for a semaphore, not part of the smi_data_buf */ + smi_data_buf_phys_addr = eps->smm_comm_buff_addr + 8; + smi_data_buf = eps_buffer + 8; + smi_data_buf_size = remap_size - 8; + max_smi_data_buf_size = smi_data_buf_size; + wsmt_enabled = true; + dev_info(&dcdbas_pdev->dev, + "WSMT found, using firmware-provided SMI buffer.\n"); + return 1; +} + /** * dcdbas_reboot_notify: handle reboot notification for host control */ @@ -548,6 +652,11 @@ static int dcdbas_probe(struct platform_device *dev) dcdbas_pdev = dev; + /* Check if ACPI WSMT table specifies protected SMI buffer address */ + error = dcdbas_check_wsmt(); + if (error < 0) + return error; + /* * BIOS SMI calls require buffer addresses be in 32-bit address space. * This is done by setting the DMA mask below. @@ -635,6 +744,8 @@ static void __exit dcdbas_exit(void) */ if (dcdbas_pdev) smi_data_buf_free(); + if (eps_buffer) + memunmap(eps_buffer); platform_device_unregister(dcdbas_pdev_reg); platform_driver_unregister(&dcdbas_driver); } diff --git a/drivers/firmware/dcdbas.h b/drivers/firmware/dcdbas.h index ca3cb0a54ab6..52729a494b00 100644 --- a/drivers/firmware/dcdbas.h +++ b/drivers/firmware/dcdbas.h @@ -53,6 +53,7 @@ #define EXPIRED_TIMER (0) #define SMI_CMD_MAGIC (0x534D4931) +#define SMM_EPS_SIG "$SCB" #define DCDBAS_DEV_ATTR_RW(_name) \ DEVICE_ATTR(_name,0600,_name##_show,_name##_store); @@ -103,5 +104,14 @@ struct apm_cmd { int dcdbas_smi_request(struct smi_cmd *smi_cmd); +struct smm_eps_table { + char smm_comm_buff_anchor[4]; + u8 length; + u8 checksum; + u8 version; + u64 smm_comm_buff_addr; + u64 num_of_4k_pages; +} __packed; + #endif /* _DCDBAS_H_ */ -- cgit From c48e2ffd717ca4a67b4938bb60d110b7eeed39c4 Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Wed, 26 Sep 2018 16:50:19 -0500 Subject: firmware: dell_rbu: Move dell_rbu to drivers/platform/x86 Move dell_rbu to the more appropriate directory drivers/platform/x86. Signed-off-by: Stuart Hayes Signed-off-by: Andy Shevchenko --- drivers/firmware/Kconfig | 12 - drivers/firmware/Makefile | 1 - drivers/firmware/dell_rbu.c | 753 ---------------------------------------- drivers/platform/x86/Kconfig | 12 + drivers/platform/x86/Makefile | 1 + drivers/platform/x86/dell_rbu.c | 753 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 766 insertions(+), 766 deletions(-) delete mode 100644 drivers/firmware/dell_rbu.c create mode 100644 drivers/platform/x86/dell_rbu.c (limited to 'drivers') diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 6e83880046d7..02f39d20efce 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -145,18 +145,6 @@ config EFI_PCDP See DIG64_HCDPv20_042804.pdf available from -config DELL_RBU - tristate "BIOS update support for DELL systems via sysfs" - depends on X86 - select FW_LOADER - select FW_LOADER_USER_HELPER - help - Say m if you want to have the option of updating the BIOS for your - DELL system. Note you need a Dell OpenManage or Dell Update package (DUP) - supporting application to communicate with the BIOS regarding the new - image for the image update to take effect. - See for more details on the driver. - config DCDBAS tristate "Dell Systems Management Base Driver" depends on X86 diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index e18a041cfc53..61887ba9df1d 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_DMI) += dmi_scan.o obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o obj-$(CONFIG_EDD) += edd.o obj-$(CONFIG_EFI_PCDP) += pcdp.o -obj-$(CONFIG_DELL_RBU) += dell_rbu.o obj-$(CONFIG_DCDBAS) += dcdbas.o obj-$(CONFIG_DMIID) += dmi-id.o obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o diff --git a/drivers/firmware/dell_rbu.c b/drivers/firmware/dell_rbu.c deleted file mode 100644 index ccefa84f7305..000000000000 --- a/drivers/firmware/dell_rbu.c +++ /dev/null @@ -1,753 +0,0 @@ -/* - * dell_rbu.c - * Bios Update driver for Dell systems - * Author: Dell Inc - * Abhay Salunke - * - * Copyright (C) 2005 Dell Inc. - * - * Remote BIOS Update (rbu) driver is used for updating DELL BIOS by - * creating entries in the /sys file systems on Linux 2.6 and higher - * kernels. The driver supports two mechanism to update the BIOS namely - * contiguous and packetized. Both these methods still require having some - * application to set the CMOS bit indicating the BIOS to update itself - * after a reboot. - * - * Contiguous method: - * This driver writes the incoming data in a monolithic image by allocating - * contiguous physical pages large enough to accommodate the incoming BIOS - * image size. - * - * Packetized method: - * The driver writes the incoming packet image by allocating a new packet - * on every time the packet data is written. This driver requires an - * application to break the BIOS image in to fixed sized packet chunks. - * - * See Documentation/dell_rbu.txt for more info. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Abhay Salunke "); -MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems"); -MODULE_LICENSE("GPL"); -MODULE_VERSION("3.2"); - -#define BIOS_SCAN_LIMIT 0xffffffff -#define MAX_IMAGE_LENGTH 16 -static struct _rbu_data { - void *image_update_buffer; - unsigned long image_update_buffer_size; - unsigned long bios_image_size; - int image_update_ordernum; - int dma_alloc; - spinlock_t lock; - unsigned long packet_read_count; - unsigned long num_packets; - unsigned long packetsize; - unsigned long imagesize; - int entry_created; -} rbu_data; - -static char image_type[MAX_IMAGE_LENGTH + 1] = "mono"; -module_param_string(image_type, image_type, sizeof (image_type), 0); -MODULE_PARM_DESC(image_type, - "BIOS image type. choose- mono or packet or init"); - -static unsigned long allocation_floor = 0x100000; -module_param(allocation_floor, ulong, 0644); -MODULE_PARM_DESC(allocation_floor, - "Minimum address for allocations when using Packet mode"); - -struct packet_data { - struct list_head list; - size_t length; - void *data; - int ordernum; -}; - -static struct packet_data packet_data_head; - -static struct platform_device *rbu_device; -static int context; -static dma_addr_t dell_rbu_dmaaddr; - -static void init_packet_head(void) -{ - INIT_LIST_HEAD(&packet_data_head.list); - rbu_data.packet_read_count = 0; - rbu_data.num_packets = 0; - rbu_data.packetsize = 0; - rbu_data.imagesize = 0; -} - -static int create_packet(void *data, size_t length) -{ - struct packet_data *newpacket; - int ordernum = 0; - int retval = 0; - unsigned int packet_array_size = 0; - void **invalid_addr_packet_array = NULL; - void *packet_data_temp_buf = NULL; - unsigned int idx = 0; - - pr_debug("create_packet: entry \n"); - - if (!rbu_data.packetsize) { - pr_debug("create_packet: packetsize not specified\n"); - retval = -EINVAL; - goto out_noalloc; - } - - spin_unlock(&rbu_data.lock); - - newpacket = kzalloc(sizeof (struct packet_data), GFP_KERNEL); - - if (!newpacket) { - printk(KERN_WARNING - "dell_rbu:%s: failed to allocate new " - "packet\n", __func__); - retval = -ENOMEM; - spin_lock(&rbu_data.lock); - goto out_noalloc; - } - - ordernum = get_order(length); - - /* - * BIOS errata mean we cannot allocate packets below 1MB or they will - * be overwritten by BIOS. - * - * array to temporarily hold packets - * that are below the allocation floor - * - * NOTE: very simplistic because we only need the floor to be at 1MB - * due to BIOS errata. This shouldn't be used for higher floors - * or you will run out of mem trying to allocate the array. - */ - packet_array_size = max( - (unsigned int)(allocation_floor / rbu_data.packetsize), - (unsigned int)1); - invalid_addr_packet_array = kcalloc(packet_array_size, sizeof(void *), - GFP_KERNEL); - - if (!invalid_addr_packet_array) { - printk(KERN_WARNING - "dell_rbu:%s: failed to allocate " - "invalid_addr_packet_array \n", - __func__); - retval = -ENOMEM; - spin_lock(&rbu_data.lock); - goto out_alloc_packet; - } - - while (!packet_data_temp_buf) { - packet_data_temp_buf = (unsigned char *) - __get_free_pages(GFP_KERNEL, ordernum); - if (!packet_data_temp_buf) { - printk(KERN_WARNING - "dell_rbu:%s: failed to allocate new " - "packet\n", __func__); - retval = -ENOMEM; - spin_lock(&rbu_data.lock); - goto out_alloc_packet_array; - } - - if ((unsigned long)virt_to_phys(packet_data_temp_buf) - < allocation_floor) { - pr_debug("packet 0x%lx below floor at 0x%lx.\n", - (unsigned long)virt_to_phys( - packet_data_temp_buf), - allocation_floor); - invalid_addr_packet_array[idx++] = packet_data_temp_buf; - packet_data_temp_buf = NULL; - } - } - /* - * set to uncachable or it may never get written back before reboot - */ - set_memory_uc((unsigned long)packet_data_temp_buf, 1 << ordernum); - - spin_lock(&rbu_data.lock); - - newpacket->data = packet_data_temp_buf; - - pr_debug("create_packet: newpacket at physical addr %lx\n", - (unsigned long)virt_to_phys(newpacket->data)); - - /* packets may not have fixed size */ - newpacket->length = length; - newpacket->ordernum = ordernum; - ++rbu_data.num_packets; - - /* initialize the newly created packet headers */ - INIT_LIST_HEAD(&newpacket->list); - list_add_tail(&newpacket->list, &packet_data_head.list); - - memcpy(newpacket->data, data, length); - - pr_debug("create_packet: exit \n"); - -out_alloc_packet_array: - /* always free packet array */ - for (;idx>0;idx--) { - pr_debug("freeing unused packet below floor 0x%lx.\n", - (unsigned long)virt_to_phys( - invalid_addr_packet_array[idx-1])); - free_pages((unsigned long)invalid_addr_packet_array[idx-1], - ordernum); - } - kfree(invalid_addr_packet_array); - -out_alloc_packet: - /* if error, free data */ - if (retval) - kfree(newpacket); - -out_noalloc: - return retval; -} - -static int packetize_data(const u8 *data, size_t length) -{ - int rc = 0; - int done = 0; - int packet_length; - u8 *temp; - u8 *end = (u8 *) data + length; - pr_debug("packetize_data: data length %zd\n", length); - if (!rbu_data.packetsize) { - printk(KERN_WARNING - "dell_rbu: packetsize not specified\n"); - return -EIO; - } - - temp = (u8 *) data; - - /* packetize the hunk */ - while (!done) { - if ((temp + rbu_data.packetsize) < end) - packet_length = rbu_data.packetsize; - else { - /* this is the last packet */ - packet_length = end - temp; - done = 1; - } - - if ((rc = create_packet(temp, packet_length))) - return rc; - - pr_debug("%p:%td\n", temp, (end - temp)); - temp += packet_length; - } - - rbu_data.imagesize = length; - - return rc; -} - -static int do_packet_read(char *data, struct list_head *ptemp_list, - int length, int bytes_read, int *list_read_count) -{ - void *ptemp_buf; - struct packet_data *newpacket = NULL; - int bytes_copied = 0; - int j = 0; - - newpacket = list_entry(ptemp_list, struct packet_data, list); - *list_read_count += newpacket->length; - - if (*list_read_count > bytes_read) { - /* point to the start of unread data */ - j = newpacket->length - (*list_read_count - bytes_read); - /* point to the offset in the packet buffer */ - ptemp_buf = (u8 *) newpacket->data + j; - /* - * check if there is enough room in - * * the incoming buffer - */ - if (length > (*list_read_count - bytes_read)) - /* - * copy what ever is there in this - * packet and move on - */ - bytes_copied = (*list_read_count - bytes_read); - else - /* copy the remaining */ - bytes_copied = length; - memcpy(data, ptemp_buf, bytes_copied); - } - return bytes_copied; -} - -static int packet_read_list(char *data, size_t * pread_length) -{ - struct list_head *ptemp_list; - int temp_count = 0; - int bytes_copied = 0; - int bytes_read = 0; - int remaining_bytes = 0; - char *pdest = data; - - /* check if we have any packets */ - if (0 == rbu_data.num_packets) - return -ENOMEM; - - remaining_bytes = *pread_length; - bytes_read = rbu_data.packet_read_count; - - ptemp_list = (&packet_data_head.list)->next; - while (!list_empty(ptemp_list)) { - bytes_copied = do_packet_read(pdest, ptemp_list, - remaining_bytes, bytes_read, &temp_count); - remaining_bytes -= bytes_copied; - bytes_read += bytes_copied; - pdest += bytes_copied; - /* - * check if we reached end of buffer before reaching the - * last packet - */ - if (remaining_bytes == 0) - break; - - ptemp_list = ptemp_list->next; - } - /*finally set the bytes read */ - *pread_length = bytes_read - rbu_data.packet_read_count; - rbu_data.packet_read_count = bytes_read; - return 0; -} - -static void packet_empty_list(void) -{ - struct list_head *ptemp_list; - struct list_head *pnext_list; - struct packet_data *newpacket; - - ptemp_list = (&packet_data_head.list)->next; - while (!list_empty(ptemp_list)) { - newpacket = - list_entry(ptemp_list, struct packet_data, list); - pnext_list = ptemp_list->next; - list_del(ptemp_list); - ptemp_list = pnext_list; - /* - * zero out the RBU packet memory before freeing - * to make sure there are no stale RBU packets left in memory - */ - memset(newpacket->data, 0, rbu_data.packetsize); - set_memory_wb((unsigned long)newpacket->data, - 1 << newpacket->ordernum); - free_pages((unsigned long) newpacket->data, - newpacket->ordernum); - kfree(newpacket); - } - rbu_data.packet_read_count = 0; - rbu_data.num_packets = 0; - rbu_data.imagesize = 0; -} - -/* - * img_update_free: Frees the buffer allocated for storing BIOS image - * Always called with lock held and returned with lock held - */ -static void img_update_free(void) -{ - if (!rbu_data.image_update_buffer) - return; - /* - * zero out this buffer before freeing it to get rid of any stale - * BIOS image copied in memory. - */ - memset(rbu_data.image_update_buffer, 0, - rbu_data.image_update_buffer_size); - if (rbu_data.dma_alloc == 1) - dma_free_coherent(NULL, rbu_data.bios_image_size, - rbu_data.image_update_buffer, dell_rbu_dmaaddr); - else - free_pages((unsigned long) rbu_data.image_update_buffer, - rbu_data.image_update_ordernum); - - /* - * Re-initialize the rbu_data variables after a free - */ - rbu_data.image_update_ordernum = -1; - rbu_data.image_update_buffer = NULL; - rbu_data.image_update_buffer_size = 0; - rbu_data.bios_image_size = 0; - rbu_data.dma_alloc = 0; -} - -/* - * img_update_realloc: This function allocates the contiguous pages to - * accommodate the requested size of data. The memory address and size - * values are stored globally and on every call to this function the new - * size is checked to see if more data is required than the existing size. - * If true the previous memory is freed and new allocation is done to - * accommodate the new size. If the incoming size is less then than the - * already allocated size, then that memory is reused. This function is - * called with lock held and returns with lock held. - */ -static int img_update_realloc(unsigned long size) -{ - unsigned char *image_update_buffer = NULL; - unsigned long rc; - unsigned long img_buf_phys_addr; - int ordernum; - int dma_alloc = 0; - - /* - * check if the buffer of sufficient size has been - * already allocated - */ - if (rbu_data.image_update_buffer_size >= size) { - /* - * check for corruption - */ - if ((size != 0) && (rbu_data.image_update_buffer == NULL)) { - printk(KERN_ERR "dell_rbu:%s: corruption " - "check failed\n", __func__); - return -EINVAL; - } - /* - * we have a valid pre-allocated buffer with - * sufficient size - */ - return 0; - } - - /* - * free any previously allocated buffer - */ - img_update_free(); - - spin_unlock(&rbu_data.lock); - - ordernum = get_order(size); - image_update_buffer = - (unsigned char *) __get_free_pages(GFP_KERNEL, ordernum); - - img_buf_phys_addr = - (unsigned long) virt_to_phys(image_update_buffer); - - if (img_buf_phys_addr > BIOS_SCAN_LIMIT) { - free_pages((unsigned long) image_update_buffer, ordernum); - ordernum = -1; - image_update_buffer = dma_alloc_coherent(NULL, size, - &dell_rbu_dmaaddr, GFP_KERNEL); - dma_alloc = 1; - } - - spin_lock(&rbu_data.lock); - - if (image_update_buffer != NULL) { - rbu_data.image_update_buffer = image_update_buffer; - rbu_data.image_update_buffer_size = size; - rbu_data.bios_image_size = - rbu_data.image_update_buffer_size; - rbu_data.image_update_ordernum = ordernum; - rbu_data.dma_alloc = dma_alloc; - rc = 0; - } else { - pr_debug("Not enough memory for image update:" - "size = %ld\n", size); - rc = -ENOMEM; - } - - return rc; -} - -static ssize_t read_packet_data(char *buffer, loff_t pos, size_t count) -{ - int retval; - size_t bytes_left; - size_t data_length; - char *ptempBuf = buffer; - - /* check to see if we have something to return */ - if (rbu_data.num_packets == 0) { - pr_debug("read_packet_data: no packets written\n"); - retval = -ENOMEM; - goto read_rbu_data_exit; - } - - if (pos > rbu_data.imagesize) { - retval = 0; - printk(KERN_WARNING "dell_rbu:read_packet_data: " - "data underrun\n"); - goto read_rbu_data_exit; - } - - bytes_left = rbu_data.imagesize - pos; - data_length = min(bytes_left, count); - - if ((retval = packet_read_list(ptempBuf, &data_length)) < 0) - goto read_rbu_data_exit; - - if ((pos + count) > rbu_data.imagesize) { - rbu_data.packet_read_count = 0; - /* this was the last copy */ - retval = bytes_left; - } else - retval = count; - - read_rbu_data_exit: - return retval; -} - -static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count) -{ - /* check to see if we have something to return */ - if ((rbu_data.image_update_buffer == NULL) || - (rbu_data.bios_image_size == 0)) { - pr_debug("read_rbu_data_mono: image_update_buffer %p ," - "bios_image_size %lu\n", - rbu_data.image_update_buffer, - rbu_data.bios_image_size); - return -ENOMEM; - } - - return memory_read_from_buffer(buffer, count, &pos, - rbu_data.image_update_buffer, rbu_data.bios_image_size); -} - -static ssize_t read_rbu_data(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buffer, loff_t pos, size_t count) -{ - ssize_t ret_count = 0; - - spin_lock(&rbu_data.lock); - - if (!strcmp(image_type, "mono")) - ret_count = read_rbu_mono_data(buffer, pos, count); - else if (!strcmp(image_type, "packet")) - ret_count = read_packet_data(buffer, pos, count); - else - pr_debug("read_rbu_data: invalid image type specified\n"); - - spin_unlock(&rbu_data.lock); - return ret_count; -} - -static void callbackfn_rbu(const struct firmware *fw, void *context) -{ - rbu_data.entry_created = 0; - - if (!fw) - return; - - if (!fw->size) - goto out; - - spin_lock(&rbu_data.lock); - if (!strcmp(image_type, "mono")) { - if (!img_update_realloc(fw->size)) - memcpy(rbu_data.image_update_buffer, - fw->data, fw->size); - } else if (!strcmp(image_type, "packet")) { - /* - * we need to free previous packets if a - * new hunk of packets needs to be downloaded - */ - packet_empty_list(); - if (packetize_data(fw->data, fw->size)) - /* Incase something goes wrong when we are - * in middle of packetizing the data, we - * need to free up whatever packets might - * have been created before we quit. - */ - packet_empty_list(); - } else - pr_debug("invalid image type specified.\n"); - spin_unlock(&rbu_data.lock); - out: - release_firmware(fw); -} - -static ssize_t read_rbu_image_type(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buffer, loff_t pos, size_t count) -{ - int size = 0; - if (!pos) - size = scnprintf(buffer, count, "%s\n", image_type); - return size; -} - -static ssize_t write_rbu_image_type(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buffer, loff_t pos, size_t count) -{ - int rc = count; - int req_firm_rc = 0; - int i; - spin_lock(&rbu_data.lock); - /* - * Find the first newline or space - */ - for (i = 0; i < count; ++i) - if (buffer[i] == '\n' || buffer[i] == ' ') { - buffer[i] = '\0'; - break; - } - if (i == count) - buffer[count] = '\0'; - - if (strstr(buffer, "mono")) - strcpy(image_type, "mono"); - else if (strstr(buffer, "packet")) - strcpy(image_type, "packet"); - else if (strstr(buffer, "init")) { - /* - * If due to the user error the driver gets in a bad - * state where even though it is loaded , the - * /sys/class/firmware/dell_rbu entries are missing. - * to cover this situation the user can recreate entries - * by writing init to image_type. - */ - if (!rbu_data.entry_created) { - spin_unlock(&rbu_data.lock); - req_firm_rc = request_firmware_nowait(THIS_MODULE, - FW_ACTION_NOHOTPLUG, "dell_rbu", - &rbu_device->dev, GFP_KERNEL, &context, - callbackfn_rbu); - if (req_firm_rc) { - printk(KERN_ERR - "dell_rbu:%s request_firmware_nowait" - " failed %d\n", __func__, rc); - rc = -EIO; - } else - rbu_data.entry_created = 1; - - spin_lock(&rbu_data.lock); - } - } else { - printk(KERN_WARNING "dell_rbu: image_type is invalid\n"); - spin_unlock(&rbu_data.lock); - return -EINVAL; - } - - /* we must free all previous allocations */ - packet_empty_list(); - img_update_free(); - spin_unlock(&rbu_data.lock); - - return rc; -} - -static ssize_t read_rbu_packet_size(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buffer, loff_t pos, size_t count) -{ - int size = 0; - if (!pos) { - spin_lock(&rbu_data.lock); - size = scnprintf(buffer, count, "%lu\n", rbu_data.packetsize); - spin_unlock(&rbu_data.lock); - } - return size; -} - -static ssize_t write_rbu_packet_size(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buffer, loff_t pos, size_t count) -{ - unsigned long temp; - spin_lock(&rbu_data.lock); - packet_empty_list(); - sscanf(buffer, "%lu", &temp); - if (temp < 0xffffffff) - rbu_data.packetsize = temp; - - spin_unlock(&rbu_data.lock); - return count; -} - -static struct bin_attribute rbu_data_attr = { - .attr = {.name = "data", .mode = 0444}, - .read = read_rbu_data, -}; - -static struct bin_attribute rbu_image_type_attr = { - .attr = {.name = "image_type", .mode = 0644}, - .read = read_rbu_image_type, - .write = write_rbu_image_type, -}; - -static struct bin_attribute rbu_packet_size_attr = { - .attr = {.name = "packet_size", .mode = 0644}, - .read = read_rbu_packet_size, - .write = write_rbu_packet_size, -}; - -static int __init dcdrbu_init(void) -{ - int rc; - spin_lock_init(&rbu_data.lock); - - init_packet_head(); - rbu_device = platform_device_register_simple("dell_rbu", -1, NULL, 0); - if (IS_ERR(rbu_device)) { - printk(KERN_ERR - "dell_rbu:%s:platform_device_register_simple " - "failed\n", __func__); - return PTR_ERR(rbu_device); - } - - rc = sysfs_create_bin_file(&rbu_device->dev.kobj, &rbu_data_attr); - if (rc) - goto out_devreg; - rc = sysfs_create_bin_file(&rbu_device->dev.kobj, &rbu_image_type_attr); - if (rc) - goto out_data; - rc = sysfs_create_bin_file(&rbu_device->dev.kobj, - &rbu_packet_size_attr); - if (rc) - goto out_imtype; - - rbu_data.entry_created = 0; - return 0; - -out_imtype: - sysfs_remove_bin_file(&rbu_device->dev.kobj, &rbu_image_type_attr); -out_data: - sysfs_remove_bin_file(&rbu_device->dev.kobj, &rbu_data_attr); -out_devreg: - platform_device_unregister(rbu_device); - return rc; -} - -static __exit void dcdrbu_exit(void) -{ - spin_lock(&rbu_data.lock); - packet_empty_list(); - img_update_free(); - spin_unlock(&rbu_data.lock); - platform_device_unregister(rbu_device); -} - -module_exit(dcdrbu_exit); -module_init(dcdrbu_init); - -/* vim:noet:ts=8:sw=8 -*/ diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 1fca33c97e8a..097212c712f7 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -230,6 +230,18 @@ config DELL_RBTN To compile this driver as a module, choose M here: the module will be called dell-rbtn. +config DELL_RBU + tristate "BIOS update support for DELL systems via sysfs" + depends on X86 + select FW_LOADER + select FW_LOADER_USER_HELPER + help + Say m if you want to have the option of updating the BIOS for your + DELL system. Note you need a Dell OpenManage or Dell Update package (DUP) + supporting application to communicate with the BIOS regarding the new + image for the image update to take effect. + See for more details on the driver. + config FUJITSU_LAPTOP tristate "Fujitsu Laptop Extras" diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index e6d1becf81ce..8843f8e22127 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o obj-$(CONFIG_DELL_RBTN) += dell-rbtn.o +obj-$(CONFIG_DELL_RBU) += dell_rbu.o obj-$(CONFIG_ACER_WMI) += acer-wmi.o obj-$(CONFIG_ACER_WIRELESS) += acer-wireless.o obj-$(CONFIG_ACERHDF) += acerhdf.o diff --git a/drivers/platform/x86/dell_rbu.c b/drivers/platform/x86/dell_rbu.c new file mode 100644 index 000000000000..ccefa84f7305 --- /dev/null +++ b/drivers/platform/x86/dell_rbu.c @@ -0,0 +1,753 @@ +/* + * dell_rbu.c + * Bios Update driver for Dell systems + * Author: Dell Inc + * Abhay Salunke + * + * Copyright (C) 2005 Dell Inc. + * + * Remote BIOS Update (rbu) driver is used for updating DELL BIOS by + * creating entries in the /sys file systems on Linux 2.6 and higher + * kernels. The driver supports two mechanism to update the BIOS namely + * contiguous and packetized. Both these methods still require having some + * application to set the CMOS bit indicating the BIOS to update itself + * after a reboot. + * + * Contiguous method: + * This driver writes the incoming data in a monolithic image by allocating + * contiguous physical pages large enough to accommodate the incoming BIOS + * image size. + * + * Packetized method: + * The driver writes the incoming packet image by allocating a new packet + * on every time the packet data is written. This driver requires an + * application to break the BIOS image in to fixed sized packet chunks. + * + * See Documentation/dell_rbu.txt for more info. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 as published by + * the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Abhay Salunke "); +MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("3.2"); + +#define BIOS_SCAN_LIMIT 0xffffffff +#define MAX_IMAGE_LENGTH 16 +static struct _rbu_data { + void *image_update_buffer; + unsigned long image_update_buffer_size; + unsigned long bios_image_size; + int image_update_ordernum; + int dma_alloc; + spinlock_t lock; + unsigned long packet_read_count; + unsigned long num_packets; + unsigned long packetsize; + unsigned long imagesize; + int entry_created; +} rbu_data; + +static char image_type[MAX_IMAGE_LENGTH + 1] = "mono"; +module_param_string(image_type, image_type, sizeof (image_type), 0); +MODULE_PARM_DESC(image_type, + "BIOS image type. choose- mono or packet or init"); + +static unsigned long allocation_floor = 0x100000; +module_param(allocation_floor, ulong, 0644); +MODULE_PARM_DESC(allocation_floor, + "Minimum address for allocations when using Packet mode"); + +struct packet_data { + struct list_head list; + size_t length; + void *data; + int ordernum; +}; + +static struct packet_data packet_data_head; + +static struct platform_device *rbu_device; +static int context; +static dma_addr_t dell_rbu_dmaaddr; + +static void init_packet_head(void) +{ + INIT_LIST_HEAD(&packet_data_head.list); + rbu_data.packet_read_count = 0; + rbu_data.num_packets = 0; + rbu_data.packetsize = 0; + rbu_data.imagesize = 0; +} + +static int create_packet(void *data, size_t length) +{ + struct packet_data *newpacket; + int ordernum = 0; + int retval = 0; + unsigned int packet_array_size = 0; + void **invalid_addr_packet_array = NULL; + void *packet_data_temp_buf = NULL; + unsigned int idx = 0; + + pr_debug("create_packet: entry \n"); + + if (!rbu_data.packetsize) { + pr_debug("create_packet: packetsize not specified\n"); + retval = -EINVAL; + goto out_noalloc; + } + + spin_unlock(&rbu_data.lock); + + newpacket = kzalloc(sizeof (struct packet_data), GFP_KERNEL); + + if (!newpacket) { + printk(KERN_WARNING + "dell_rbu:%s: failed to allocate new " + "packet\n", __func__); + retval = -ENOMEM; + spin_lock(&rbu_data.lock); + goto out_noalloc; + } + + ordernum = get_order(length); + + /* + * BIOS errata mean we cannot allocate packets below 1MB or they will + * be overwritten by BIOS. + * + * array to temporarily hold packets + * that are below the allocation floor + * + * NOTE: very simplistic because we only need the floor to be at 1MB + * due to BIOS errata. This shouldn't be used for higher floors + * or you will run out of mem trying to allocate the array. + */ + packet_array_size = max( + (unsigned int)(allocation_floor / rbu_data.packetsize), + (unsigned int)1); + invalid_addr_packet_array = kcalloc(packet_array_size, sizeof(void *), + GFP_KERNEL); + + if (!invalid_addr_packet_array) { + printk(KERN_WARNING + "dell_rbu:%s: failed to allocate " + "invalid_addr_packet_array \n", + __func__); + retval = -ENOMEM; + spin_lock(&rbu_data.lock); + goto out_alloc_packet; + } + + while (!packet_data_temp_buf) { + packet_data_temp_buf = (unsigned char *) + __get_free_pages(GFP_KERNEL, ordernum); + if (!packet_data_temp_buf) { + printk(KERN_WARNING + "dell_rbu:%s: failed to allocate new " + "packet\n", __func__); + retval = -ENOMEM; + spin_lock(&rbu_data.lock); + goto out_alloc_packet_array; + } + + if ((unsigned long)virt_to_phys(packet_data_temp_buf) + < allocation_floor) { + pr_debug("packet 0x%lx below floor at 0x%lx.\n", + (unsigned long)virt_to_phys( + packet_data_temp_buf), + allocation_floor); + invalid_addr_packet_array[idx++] = packet_data_temp_buf; + packet_data_temp_buf = NULL; + } + } + /* + * set to uncachable or it may never get written back before reboot + */ + set_memory_uc((unsigned long)packet_data_temp_buf, 1 << ordernum); + + spin_lock(&rbu_data.lock); + + newpacket->data = packet_data_temp_buf; + + pr_debug("create_packet: newpacket at physical addr %lx\n", + (unsigned long)virt_to_phys(newpacket->data)); + + /* packets may not have fixed size */ + newpacket->length = length; + newpacket->ordernum = ordernum; + ++rbu_data.num_packets; + + /* initialize the newly created packet headers */ + INIT_LIST_HEAD(&newpacket->list); + list_add_tail(&newpacket->list, &packet_data_head.list); + + memcpy(newpacket->data, data, length); + + pr_debug("create_packet: exit \n"); + +out_alloc_packet_array: + /* always free packet array */ + for (;idx>0;idx--) { + pr_debug("freeing unused packet below floor 0x%lx.\n", + (unsigned long)virt_to_phys( + invalid_addr_packet_array[idx-1])); + free_pages((unsigned long)invalid_addr_packet_array[idx-1], + ordernum); + } + kfree(invalid_addr_packet_array); + +out_alloc_packet: + /* if error, free data */ + if (retval) + kfree(newpacket); + +out_noalloc: + return retval; +} + +static int packetize_data(const u8 *data, size_t length) +{ + int rc = 0; + int done = 0; + int packet_length; + u8 *temp; + u8 *end = (u8 *) data + length; + pr_debug("packetize_data: data length %zd\n", length); + if (!rbu_data.packetsize) { + printk(KERN_WARNING + "dell_rbu: packetsize not specified\n"); + return -EIO; + } + + temp = (u8 *) data; + + /* packetize the hunk */ + while (!done) { + if ((temp + rbu_data.packetsize) < end) + packet_length = rbu_data.packetsize; + else { + /* this is the last packet */ + packet_length = end - temp; + done = 1; + } + + if ((rc = create_packet(temp, packet_length))) + return rc; + + pr_debug("%p:%td\n", temp, (end - temp)); + temp += packet_length; + } + + rbu_data.imagesize = length; + + return rc; +} + +static int do_packet_read(char *data, struct list_head *ptemp_list, + int length, int bytes_read, int *list_read_count) +{ + void *ptemp_buf; + struct packet_data *newpacket = NULL; + int bytes_copied = 0; + int j = 0; + + newpacket = list_entry(ptemp_list, struct packet_data, list); + *list_read_count += newpacket->length; + + if (*list_read_count > bytes_read) { + /* point to the start of unread data */ + j = newpacket->length - (*list_read_count - bytes_read); + /* point to the offset in the packet buffer */ + ptemp_buf = (u8 *) newpacket->data + j; + /* + * check if there is enough room in + * * the incoming buffer + */ + if (length > (*list_read_count - bytes_read)) + /* + * copy what ever is there in this + * packet and move on + */ + bytes_copied = (*list_read_count - bytes_read); + else + /* copy the remaining */ + bytes_copied = length; + memcpy(data, ptemp_buf, bytes_copied); + } + return bytes_copied; +} + +static int packet_read_list(char *data, size_t * pread_length) +{ + struct list_head *ptemp_list; + int temp_count = 0; + int bytes_copied = 0; + int bytes_read = 0; + int remaining_bytes = 0; + char *pdest = data; + + /* check if we have any packets */ + if (0 == rbu_data.num_packets) + return -ENOMEM; + + remaining_bytes = *pread_length; + bytes_read = rbu_data.packet_read_count; + + ptemp_list = (&packet_data_head.list)->next; + while (!list_empty(ptemp_list)) { + bytes_copied = do_packet_read(pdest, ptemp_list, + remaining_bytes, bytes_read, &temp_count); + remaining_bytes -= bytes_copied; + bytes_read += bytes_copied; + pdest += bytes_copied; + /* + * check if we reached end of buffer before reaching the + * last packet + */ + if (remaining_bytes == 0) + break; + + ptemp_list = ptemp_list->next; + } + /*finally set the bytes read */ + *pread_length = bytes_read - rbu_data.packet_read_count; + rbu_data.packet_read_count = bytes_read; + return 0; +} + +static void packet_empty_list(void) +{ + struct list_head *ptemp_list; + struct list_head *pnext_list; + struct packet_data *newpacket; + + ptemp_list = (&packet_data_head.list)->next; + while (!list_empty(ptemp_list)) { + newpacket = + list_entry(ptemp_list, struct packet_data, list); + pnext_list = ptemp_list->next; + list_del(ptemp_list); + ptemp_list = pnext_list; + /* + * zero out the RBU packet memory before freeing + * to make sure there are no stale RBU packets left in memory + */ + memset(newpacket->data, 0, rbu_data.packetsize); + set_memory_wb((unsigned long)newpacket->data, + 1 << newpacket->ordernum); + free_pages((unsigned long) newpacket->data, + newpacket->ordernum); + kfree(newpacket); + } + rbu_data.packet_read_count = 0; + rbu_data.num_packets = 0; + rbu_data.imagesize = 0; +} + +/* + * img_update_free: Frees the buffer allocated for storing BIOS image + * Always called with lock held and returned with lock held + */ +static void img_update_free(void) +{ + if (!rbu_data.image_update_buffer) + return; + /* + * zero out this buffer before freeing it to get rid of any stale + * BIOS image copied in memory. + */ + memset(rbu_data.image_update_buffer, 0, + rbu_data.image_update_buffer_size); + if (rbu_data.dma_alloc == 1) + dma_free_coherent(NULL, rbu_data.bios_image_size, + rbu_data.image_update_buffer, dell_rbu_dmaaddr); + else + free_pages((unsigned long) rbu_data.image_update_buffer, + rbu_data.image_update_ordernum); + + /* + * Re-initialize the rbu_data variables after a free + */ + rbu_data.image_update_ordernum = -1; + rbu_data.image_update_buffer = NULL; + rbu_data.image_update_buffer_size = 0; + rbu_data.bios_image_size = 0; + rbu_data.dma_alloc = 0; +} + +/* + * img_update_realloc: This function allocates the contiguous pages to + * accommodate the requested size of data. The memory address and size + * values are stored globally and on every call to this function the new + * size is checked to see if more data is required than the existing size. + * If true the previous memory is freed and new allocation is done to + * accommodate the new size. If the incoming size is less then than the + * already allocated size, then that memory is reused. This function is + * called with lock held and returns with lock held. + */ +static int img_update_realloc(unsigned long size) +{ + unsigned char *image_update_buffer = NULL; + unsigned long rc; + unsigned long img_buf_phys_addr; + int ordernum; + int dma_alloc = 0; + + /* + * check if the buffer of sufficient size has been + * already allocated + */ + if (rbu_data.image_update_buffer_size >= size) { + /* + * check for corruption + */ + if ((size != 0) && (rbu_data.image_update_buffer == NULL)) { + printk(KERN_ERR "dell_rbu:%s: corruption " + "check failed\n", __func__); + return -EINVAL; + } + /* + * we have a valid pre-allocated buffer with + * sufficient size + */ + return 0; + } + + /* + * free any previously allocated buffer + */ + img_update_free(); + + spin_unlock(&rbu_data.lock); + + ordernum = get_order(size); + image_update_buffer = + (unsigned char *) __get_free_pages(GFP_KERNEL, ordernum); + + img_buf_phys_addr = + (unsigned long) virt_to_phys(image_update_buffer); + + if (img_buf_phys_addr > BIOS_SCAN_LIMIT) { + free_pages((unsigned long) image_update_buffer, ordernum); + ordernum = -1; + image_update_buffer = dma_alloc_coherent(NULL, size, + &dell_rbu_dmaaddr, GFP_KERNEL); + dma_alloc = 1; + } + + spin_lock(&rbu_data.lock); + + if (image_update_buffer != NULL) { + rbu_data.image_update_buffer = image_update_buffer; + rbu_data.image_update_buffer_size = size; + rbu_data.bios_image_size = + rbu_data.image_update_buffer_size; + rbu_data.image_update_ordernum = ordernum; + rbu_data.dma_alloc = dma_alloc; + rc = 0; + } else { + pr_debug("Not enough memory for image update:" + "size = %ld\n", size); + rc = -ENOMEM; + } + + return rc; +} + +static ssize_t read_packet_data(char *buffer, loff_t pos, size_t count) +{ + int retval; + size_t bytes_left; + size_t data_length; + char *ptempBuf = buffer; + + /* check to see if we have something to return */ + if (rbu_data.num_packets == 0) { + pr_debug("read_packet_data: no packets written\n"); + retval = -ENOMEM; + goto read_rbu_data_exit; + } + + if (pos > rbu_data.imagesize) { + retval = 0; + printk(KERN_WARNING "dell_rbu:read_packet_data: " + "data underrun\n"); + goto read_rbu_data_exit; + } + + bytes_left = rbu_data.imagesize - pos; + data_length = min(bytes_left, count); + + if ((retval = packet_read_list(ptempBuf, &data_length)) < 0) + goto read_rbu_data_exit; + + if ((pos + count) > rbu_data.imagesize) { + rbu_data.packet_read_count = 0; + /* this was the last copy */ + retval = bytes_left; + } else + retval = count; + + read_rbu_data_exit: + return retval; +} + +static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count) +{ + /* check to see if we have something to return */ + if ((rbu_data.image_update_buffer == NULL) || + (rbu_data.bios_image_size == 0)) { + pr_debug("read_rbu_data_mono: image_update_buffer %p ," + "bios_image_size %lu\n", + rbu_data.image_update_buffer, + rbu_data.bios_image_size); + return -ENOMEM; + } + + return memory_read_from_buffer(buffer, count, &pos, + rbu_data.image_update_buffer, rbu_data.bios_image_size); +} + +static ssize_t read_rbu_data(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) +{ + ssize_t ret_count = 0; + + spin_lock(&rbu_data.lock); + + if (!strcmp(image_type, "mono")) + ret_count = read_rbu_mono_data(buffer, pos, count); + else if (!strcmp(image_type, "packet")) + ret_count = read_packet_data(buffer, pos, count); + else + pr_debug("read_rbu_data: invalid image type specified\n"); + + spin_unlock(&rbu_data.lock); + return ret_count; +} + +static void callbackfn_rbu(const struct firmware *fw, void *context) +{ + rbu_data.entry_created = 0; + + if (!fw) + return; + + if (!fw->size) + goto out; + + spin_lock(&rbu_data.lock); + if (!strcmp(image_type, "mono")) { + if (!img_update_realloc(fw->size)) + memcpy(rbu_data.image_update_buffer, + fw->data, fw->size); + } else if (!strcmp(image_type, "packet")) { + /* + * we need to free previous packets if a + * new hunk of packets needs to be downloaded + */ + packet_empty_list(); + if (packetize_data(fw->data, fw->size)) + /* Incase something goes wrong when we are + * in middle of packetizing the data, we + * need to free up whatever packets might + * have been created before we quit. + */ + packet_empty_list(); + } else + pr_debug("invalid image type specified.\n"); + spin_unlock(&rbu_data.lock); + out: + release_firmware(fw); +} + +static ssize_t read_rbu_image_type(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) +{ + int size = 0; + if (!pos) + size = scnprintf(buffer, count, "%s\n", image_type); + return size; +} + +static ssize_t write_rbu_image_type(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) +{ + int rc = count; + int req_firm_rc = 0; + int i; + spin_lock(&rbu_data.lock); + /* + * Find the first newline or space + */ + for (i = 0; i < count; ++i) + if (buffer[i] == '\n' || buffer[i] == ' ') { + buffer[i] = '\0'; + break; + } + if (i == count) + buffer[count] = '\0'; + + if (strstr(buffer, "mono")) + strcpy(image_type, "mono"); + else if (strstr(buffer, "packet")) + strcpy(image_type, "packet"); + else if (strstr(buffer, "init")) { + /* + * If due to the user error the driver gets in a bad + * state where even though it is loaded , the + * /sys/class/firmware/dell_rbu entries are missing. + * to cover this situation the user can recreate entries + * by writing init to image_type. + */ + if (!rbu_data.entry_created) { + spin_unlock(&rbu_data.lock); + req_firm_rc = request_firmware_nowait(THIS_MODULE, + FW_ACTION_NOHOTPLUG, "dell_rbu", + &rbu_device->dev, GFP_KERNEL, &context, + callbackfn_rbu); + if (req_firm_rc) { + printk(KERN_ERR + "dell_rbu:%s request_firmware_nowait" + " failed %d\n", __func__, rc); + rc = -EIO; + } else + rbu_data.entry_created = 1; + + spin_lock(&rbu_data.lock); + } + } else { + printk(KERN_WARNING "dell_rbu: image_type is invalid\n"); + spin_unlock(&rbu_data.lock); + return -EINVAL; + } + + /* we must free all previous allocations */ + packet_empty_list(); + img_update_free(); + spin_unlock(&rbu_data.lock); + + return rc; +} + +static ssize_t read_rbu_packet_size(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) +{ + int size = 0; + if (!pos) { + spin_lock(&rbu_data.lock); + size = scnprintf(buffer, count, "%lu\n", rbu_data.packetsize); + spin_unlock(&rbu_data.lock); + } + return size; +} + +static ssize_t write_rbu_packet_size(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) +{ + unsigned long temp; + spin_lock(&rbu_data.lock); + packet_empty_list(); + sscanf(buffer, "%lu", &temp); + if (temp < 0xffffffff) + rbu_data.packetsize = temp; + + spin_unlock(&rbu_data.lock); + return count; +} + +static struct bin_attribute rbu_data_attr = { + .attr = {.name = "data", .mode = 0444}, + .read = read_rbu_data, +}; + +static struct bin_attribute rbu_image_type_attr = { + .attr = {.name = "image_type", .mode = 0644}, + .read = read_rbu_image_type, + .write = write_rbu_image_type, +}; + +static struct bin_attribute rbu_packet_size_attr = { + .attr = {.name = "packet_size", .mode = 0644}, + .read = read_rbu_packet_size, + .write = write_rbu_packet_size, +}; + +static int __init dcdrbu_init(void) +{ + int rc; + spin_lock_init(&rbu_data.lock); + + init_packet_head(); + rbu_device = platform_device_register_simple("dell_rbu", -1, NULL, 0); + if (IS_ERR(rbu_device)) { + printk(KERN_ERR + "dell_rbu:%s:platform_device_register_simple " + "failed\n", __func__); + return PTR_ERR(rbu_device); + } + + rc = sysfs_create_bin_file(&rbu_device->dev.kobj, &rbu_data_attr); + if (rc) + goto out_devreg; + rc = sysfs_create_bin_file(&rbu_device->dev.kobj, &rbu_image_type_attr); + if (rc) + goto out_data; + rc = sysfs_create_bin_file(&rbu_device->dev.kobj, + &rbu_packet_size_attr); + if (rc) + goto out_imtype; + + rbu_data.entry_created = 0; + return 0; + +out_imtype: + sysfs_remove_bin_file(&rbu_device->dev.kobj, &rbu_image_type_attr); +out_data: + sysfs_remove_bin_file(&rbu_device->dev.kobj, &rbu_data_attr); +out_devreg: + platform_device_unregister(rbu_device); + return rc; +} + +static __exit void dcdrbu_exit(void) +{ + spin_lock(&rbu_data.lock); + packet_empty_list(); + img_update_free(); + spin_unlock(&rbu_data.lock); + platform_device_unregister(rbu_device); +} + +module_exit(dcdrbu_exit); +module_init(dcdrbu_init); + +/* vim:noet:ts=8:sw=8 +*/ -- cgit From 8e5cddd1262cdee59257d554440cc9a80e5fcb7b Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Wed, 26 Sep 2018 16:50:20 -0500 Subject: firmware: dcdbas: Move dcdbas to drivers/platform/x86 Move dcdbas to the more appropriate directory drivers/platform/x86. Signed-off-by: Stuart Hayes Signed-off-by: Andy Shevchenko --- drivers/firmware/Kconfig | 16 - drivers/firmware/Makefile | 1 - drivers/firmware/dcdbas.c | 761 --------------------------------- drivers/firmware/dcdbas.h | 117 ----- drivers/platform/x86/Kconfig | 16 + drivers/platform/x86/Makefile | 1 + drivers/platform/x86/dcdbas.c | 761 +++++++++++++++++++++++++++++++++ drivers/platform/x86/dcdbas.h | 117 +++++ drivers/platform/x86/dell-smbios-smm.c | 2 +- 9 files changed, 896 insertions(+), 896 deletions(-) delete mode 100644 drivers/firmware/dcdbas.c delete mode 100644 drivers/firmware/dcdbas.h create mode 100644 drivers/platform/x86/dcdbas.c create mode 100644 drivers/platform/x86/dcdbas.h (limited to 'drivers') diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 02f39d20efce..6d0c28fd3bad 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -145,22 +145,6 @@ config EFI_PCDP See DIG64_HCDPv20_042804.pdf available from -config DCDBAS - tristate "Dell Systems Management Base Driver" - depends on X86 - help - The Dell Systems Management Base Driver provides a sysfs interface - for systems management software to perform System Management - Interrupts (SMIs) and Host Control Actions (system power cycle or - power off after OS shutdown) on certain Dell systems. - - See for more details on the driver - and the Dell systems on which Dell systems management software makes - use of this driver. - - Say Y or M here to enable the driver for use by Dell systems - management software such as Dell OpenManage. - config DMIID bool "Export DMI identification via sysfs to userspace" depends on DMI diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 61887ba9df1d..edda4206d8fc 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_DMI) += dmi_scan.o obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o obj-$(CONFIG_EDD) += edd.o obj-$(CONFIG_EFI_PCDP) += pcdp.o -obj-$(CONFIG_DCDBAS) += dcdbas.o obj-$(CONFIG_DMIID) += dmi-id.o obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c deleted file mode 100644 index ae28e48ff7dc..000000000000 --- a/drivers/firmware/dcdbas.c +++ /dev/null @@ -1,761 +0,0 @@ -/* - * dcdbas.c: Dell Systems Management Base Driver - * - * The Dell Systems Management Base Driver provides a sysfs interface for - * systems management software to perform System Management Interrupts (SMIs) - * and Host Control Actions (power cycle or power off after OS shutdown) on - * Dell systems. - * - * See Documentation/dcdbas.txt for more information. - * - * Copyright (C) 1995-2006 Dell Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dcdbas.h" - -#define DRIVER_NAME "dcdbas" -#define DRIVER_VERSION "5.6.0-3.3" -#define DRIVER_DESCRIPTION "Dell Systems Management Base Driver" - -static struct platform_device *dcdbas_pdev; - -static u8 *smi_data_buf; -static dma_addr_t smi_data_buf_handle; -static unsigned long smi_data_buf_size; -static unsigned long max_smi_data_buf_size = MAX_SMI_DATA_BUF_SIZE; -static u32 smi_data_buf_phys_addr; -static DEFINE_MUTEX(smi_data_lock); -static u8 *eps_buffer; - -static unsigned int host_control_action; -static unsigned int host_control_smi_type; -static unsigned int host_control_on_shutdown; - -static bool wsmt_enabled; - -/** - * smi_data_buf_free: free SMI data buffer - */ -static void smi_data_buf_free(void) -{ - if (!smi_data_buf || wsmt_enabled) - return; - - dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", - __func__, smi_data_buf_phys_addr, smi_data_buf_size); - - dma_free_coherent(&dcdbas_pdev->dev, smi_data_buf_size, smi_data_buf, - smi_data_buf_handle); - smi_data_buf = NULL; - smi_data_buf_handle = 0; - smi_data_buf_phys_addr = 0; - smi_data_buf_size = 0; -} - -/** - * smi_data_buf_realloc: grow SMI data buffer if needed - */ -static int smi_data_buf_realloc(unsigned long size) -{ - void *buf; - dma_addr_t handle; - - if (smi_data_buf_size >= size) - return 0; - - if (size > max_smi_data_buf_size) - return -EINVAL; - - /* new buffer is needed */ - buf = dma_alloc_coherent(&dcdbas_pdev->dev, size, &handle, GFP_KERNEL); - if (!buf) { - dev_dbg(&dcdbas_pdev->dev, - "%s: failed to allocate memory size %lu\n", - __func__, size); - return -ENOMEM; - } - /* memory zeroed by dma_alloc_coherent */ - - if (smi_data_buf) - memcpy(buf, smi_data_buf, smi_data_buf_size); - - /* free any existing buffer */ - smi_data_buf_free(); - - /* set up new buffer for use */ - smi_data_buf = buf; - smi_data_buf_handle = handle; - smi_data_buf_phys_addr = (u32) virt_to_phys(buf); - smi_data_buf_size = size; - - dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", - __func__, smi_data_buf_phys_addr, smi_data_buf_size); - - return 0; -} - -static ssize_t smi_data_buf_phys_addr_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%x\n", smi_data_buf_phys_addr); -} - -static ssize_t smi_data_buf_size_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%lu\n", smi_data_buf_size); -} - -static ssize_t smi_data_buf_size_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - unsigned long buf_size; - ssize_t ret; - - buf_size = simple_strtoul(buf, NULL, 10); - - /* make sure SMI data buffer is at least buf_size */ - mutex_lock(&smi_data_lock); - ret = smi_data_buf_realloc(buf_size); - mutex_unlock(&smi_data_lock); - if (ret) - return ret; - - return count; -} - -static ssize_t smi_data_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t pos, size_t count) -{ - ssize_t ret; - - mutex_lock(&smi_data_lock); - ret = memory_read_from_buffer(buf, count, &pos, smi_data_buf, - smi_data_buf_size); - mutex_unlock(&smi_data_lock); - return ret; -} - -static ssize_t smi_data_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t pos, size_t count) -{ - ssize_t ret; - - if ((pos + count) > max_smi_data_buf_size) - return -EINVAL; - - mutex_lock(&smi_data_lock); - - ret = smi_data_buf_realloc(pos + count); - if (ret) - goto out; - - memcpy(smi_data_buf + pos, buf, count); - ret = count; -out: - mutex_unlock(&smi_data_lock); - return ret; -} - -static ssize_t host_control_action_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%u\n", host_control_action); -} - -static ssize_t host_control_action_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - ssize_t ret; - - /* make sure buffer is available for host control command */ - mutex_lock(&smi_data_lock); - ret = smi_data_buf_realloc(sizeof(struct apm_cmd)); - mutex_unlock(&smi_data_lock); - if (ret) - return ret; - - host_control_action = simple_strtoul(buf, NULL, 10); - return count; -} - -static ssize_t host_control_smi_type_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%u\n", host_control_smi_type); -} - -static ssize_t host_control_smi_type_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - host_control_smi_type = simple_strtoul(buf, NULL, 10); - return count; -} - -static ssize_t host_control_on_shutdown_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%u\n", host_control_on_shutdown); -} - -static ssize_t host_control_on_shutdown_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - host_control_on_shutdown = simple_strtoul(buf, NULL, 10); - return count; -} - -static int raise_smi(void *par) -{ - struct smi_cmd *smi_cmd = par; - - if (smp_processor_id() != 0) { - dev_dbg(&dcdbas_pdev->dev, "%s: failed to get CPU 0\n", - __func__); - return -EBUSY; - } - - /* generate SMI */ - /* inb to force posted write through and make SMI happen now */ - asm volatile ( - "outb %b0,%w1\n" - "inb %w1" - : /* no output args */ - : "a" (smi_cmd->command_code), - "d" (smi_cmd->command_address), - "b" (smi_cmd->ebx), - "c" (smi_cmd->ecx) - : "memory" - ); - - return 0; -} -/** - * dcdbas_smi_request: generate SMI request - * - * Called with smi_data_lock. - */ -int dcdbas_smi_request(struct smi_cmd *smi_cmd) -{ - int ret; - - if (smi_cmd->magic != SMI_CMD_MAGIC) { - dev_info(&dcdbas_pdev->dev, "%s: invalid magic value\n", - __func__); - return -EBADR; - } - - /* SMI requires CPU 0 */ - get_online_cpus(); - ret = smp_call_on_cpu(0, raise_smi, smi_cmd, true); - put_online_cpus(); - - return ret; -} - -/** - * smi_request_store: - * - * The valid values are: - * 0: zero SMI data buffer - * 1: generate calling interface SMI - * 2: generate raw SMI - * - * User application writes smi_cmd to smi_data before telling driver - * to generate SMI. - */ -static ssize_t smi_request_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct smi_cmd *smi_cmd; - unsigned long val = simple_strtoul(buf, NULL, 10); - ssize_t ret; - - mutex_lock(&smi_data_lock); - - if (smi_data_buf_size < sizeof(struct smi_cmd)) { - ret = -ENODEV; - goto out; - } - smi_cmd = (struct smi_cmd *)smi_data_buf; - - switch (val) { - case 2: - /* Raw SMI */ - ret = dcdbas_smi_request(smi_cmd); - if (!ret) - ret = count; - break; - case 1: - /* - * Calling Interface SMI - * - * Provide physical address of command buffer field within - * the struct smi_cmd to BIOS. - * - * Because the address that smi_cmd (smi_data_buf) points to - * will be from memremap() of a non-memory address if WSMT - * is present, we can't use virt_to_phys() on smi_cmd, so - * we have to use the physical address that was saved when - * the virtual address for smi_cmd was received. - */ - smi_cmd->ebx = smi_data_buf_phys_addr + - offsetof(struct smi_cmd, command_buffer); - ret = dcdbas_smi_request(smi_cmd); - if (!ret) - ret = count; - break; - case 0: - memset(smi_data_buf, 0, smi_data_buf_size); - ret = count; - break; - default: - ret = -EINVAL; - break; - } - -out: - mutex_unlock(&smi_data_lock); - return ret; -} -EXPORT_SYMBOL(dcdbas_smi_request); - -/** - * host_control_smi: generate host control SMI - * - * Caller must set up the host control command in smi_data_buf. - */ -static int host_control_smi(void) -{ - struct apm_cmd *apm_cmd; - u8 *data; - unsigned long flags; - u32 num_ticks; - s8 cmd_status; - u8 index; - - apm_cmd = (struct apm_cmd *)smi_data_buf; - apm_cmd->status = ESM_STATUS_CMD_UNSUCCESSFUL; - - switch (host_control_smi_type) { - case HC_SMITYPE_TYPE1: - spin_lock_irqsave(&rtc_lock, flags); - /* write SMI data buffer physical address */ - data = (u8 *)&smi_data_buf_phys_addr; - for (index = PE1300_CMOS_CMD_STRUCT_PTR; - index < (PE1300_CMOS_CMD_STRUCT_PTR + 4); - index++, data++) { - outb(index, - (CMOS_BASE_PORT + CMOS_PAGE2_INDEX_PORT_PIIX4)); - outb(*data, - (CMOS_BASE_PORT + CMOS_PAGE2_DATA_PORT_PIIX4)); - } - - /* first set status to -1 as called by spec */ - cmd_status = ESM_STATUS_CMD_UNSUCCESSFUL; - outb((u8) cmd_status, PCAT_APM_STATUS_PORT); - - /* generate SMM call */ - outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT); - spin_unlock_irqrestore(&rtc_lock, flags); - - /* wait a few to see if it executed */ - num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING; - while ((cmd_status = inb(PCAT_APM_STATUS_PORT)) - == ESM_STATUS_CMD_UNSUCCESSFUL) { - num_ticks--; - if (num_ticks == EXPIRED_TIMER) - return -ETIME; - } - break; - - case HC_SMITYPE_TYPE2: - case HC_SMITYPE_TYPE3: - spin_lock_irqsave(&rtc_lock, flags); - /* write SMI data buffer physical address */ - data = (u8 *)&smi_data_buf_phys_addr; - for (index = PE1400_CMOS_CMD_STRUCT_PTR; - index < (PE1400_CMOS_CMD_STRUCT_PTR + 4); - index++, data++) { - outb(index, (CMOS_BASE_PORT + CMOS_PAGE1_INDEX_PORT)); - outb(*data, (CMOS_BASE_PORT + CMOS_PAGE1_DATA_PORT)); - } - - /* generate SMM call */ - if (host_control_smi_type == HC_SMITYPE_TYPE3) - outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT); - else - outb(ESM_APM_CMD, PE1400_APM_CONTROL_PORT); - - /* restore RTC index pointer since it was written to above */ - CMOS_READ(RTC_REG_C); - spin_unlock_irqrestore(&rtc_lock, flags); - - /* read control port back to serialize write */ - cmd_status = inb(PE1400_APM_CONTROL_PORT); - - /* wait a few to see if it executed */ - num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING; - while (apm_cmd->status == ESM_STATUS_CMD_UNSUCCESSFUL) { - num_ticks--; - if (num_ticks == EXPIRED_TIMER) - return -ETIME; - } - break; - - default: - dev_dbg(&dcdbas_pdev->dev, "%s: invalid SMI type %u\n", - __func__, host_control_smi_type); - return -ENOSYS; - } - - return 0; -} - -/** - * dcdbas_host_control: initiate host control - * - * This function is called by the driver after the system has - * finished shutting down if the user application specified a - * host control action to perform on shutdown. It is safe to - * use smi_data_buf at this point because the system has finished - * shutting down and no userspace apps are running. - */ -static void dcdbas_host_control(void) -{ - struct apm_cmd *apm_cmd; - u8 action; - - if (host_control_action == HC_ACTION_NONE) - return; - - action = host_control_action; - host_control_action = HC_ACTION_NONE; - - if (!smi_data_buf) { - dev_dbg(&dcdbas_pdev->dev, "%s: no SMI buffer\n", __func__); - return; - } - - if (smi_data_buf_size < sizeof(struct apm_cmd)) { - dev_dbg(&dcdbas_pdev->dev, "%s: SMI buffer too small\n", - __func__); - return; - } - - apm_cmd = (struct apm_cmd *)smi_data_buf; - - /* power off takes precedence */ - if (action & HC_ACTION_HOST_CONTROL_POWEROFF) { - apm_cmd->command = ESM_APM_POWER_CYCLE; - apm_cmd->reserved = 0; - *((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16) 0; - host_control_smi(); - } else if (action & HC_ACTION_HOST_CONTROL_POWERCYCLE) { - apm_cmd->command = ESM_APM_POWER_CYCLE; - apm_cmd->reserved = 0; - *((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16) 20; - host_control_smi(); - } -} - -/* WSMT */ - -static u8 checksum(u8 *buffer, u8 length) -{ - u8 sum = 0; - u8 *end = buffer + length; - - while (buffer < end) - sum += *buffer++; - return sum; -} - -static inline struct smm_eps_table *check_eps_table(u8 *addr) -{ - struct smm_eps_table *eps = (struct smm_eps_table *)addr; - - if (strncmp(eps->smm_comm_buff_anchor, SMM_EPS_SIG, 4) != 0) - return NULL; - - if (checksum(addr, eps->length) != 0) - return NULL; - - return eps; -} - -static int dcdbas_check_wsmt(void) -{ - struct acpi_table_wsmt *wsmt = NULL; - struct smm_eps_table *eps = NULL; - u64 remap_size; - u8 *addr; - - acpi_get_table(ACPI_SIG_WSMT, 0, (struct acpi_table_header **)&wsmt); - if (!wsmt) - return 0; - - /* Check if WSMT ACPI table shows that protection is enabled */ - if (!(wsmt->protection_flags & ACPI_WSMT_FIXED_COMM_BUFFERS) || - !(wsmt->protection_flags & ACPI_WSMT_COMM_BUFFER_NESTED_PTR_PROTECTION)) - return 0; - - /* Scan for EPS (entry point structure) */ - for (addr = (u8 *)__va(0xf0000); - addr < (u8 *)__va(0x100000 - sizeof(struct smm_eps_table)); - addr += 16) { - eps = check_eps_table(addr); - if (eps) - break; - } - - if (!eps) { - dev_dbg(&dcdbas_pdev->dev, "found WSMT, but no EPS found\n"); - return -ENODEV; - } - - /* - * Get physical address of buffer and map to virtual address. - * Table gives size in 4K pages, regardless of actual system page size. - */ - if (upper_32_bits(eps->smm_comm_buff_addr + 8)) { - dev_warn(&dcdbas_pdev->dev, "found WSMT, but EPS buffer address is above 4GB\n"); - return -EINVAL; - } - /* - * Limit remap size to MAX_SMI_DATA_BUF_SIZE + 8 (since the first 8 - * bytes are used for a semaphore, not the data buffer itself). - */ - remap_size = eps->num_of_4k_pages * PAGE_SIZE; - if (remap_size > MAX_SMI_DATA_BUF_SIZE + 8) - remap_size = MAX_SMI_DATA_BUF_SIZE + 8; - eps_buffer = memremap(eps->smm_comm_buff_addr, remap_size, MEMREMAP_WB); - if (!eps_buffer) { - dev_warn(&dcdbas_pdev->dev, "found WSMT, but failed to map EPS buffer\n"); - return -ENOMEM; - } - - /* First 8 bytes is for a semaphore, not part of the smi_data_buf */ - smi_data_buf_phys_addr = eps->smm_comm_buff_addr + 8; - smi_data_buf = eps_buffer + 8; - smi_data_buf_size = remap_size - 8; - max_smi_data_buf_size = smi_data_buf_size; - wsmt_enabled = true; - dev_info(&dcdbas_pdev->dev, - "WSMT found, using firmware-provided SMI buffer.\n"); - return 1; -} - -/** - * dcdbas_reboot_notify: handle reboot notification for host control - */ -static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code, - void *unused) -{ - switch (code) { - case SYS_DOWN: - case SYS_HALT: - case SYS_POWER_OFF: - if (host_control_on_shutdown) { - /* firmware is going to perform host control action */ - printk(KERN_WARNING "Please wait for shutdown " - "action to complete...\n"); - dcdbas_host_control(); - } - break; - } - - return NOTIFY_DONE; -} - -static struct notifier_block dcdbas_reboot_nb = { - .notifier_call = dcdbas_reboot_notify, - .next = NULL, - .priority = INT_MIN -}; - -static DCDBAS_BIN_ATTR_RW(smi_data); - -static struct bin_attribute *dcdbas_bin_attrs[] = { - &bin_attr_smi_data, - NULL -}; - -static DCDBAS_DEV_ATTR_RW(smi_data_buf_size); -static DCDBAS_DEV_ATTR_RO(smi_data_buf_phys_addr); -static DCDBAS_DEV_ATTR_WO(smi_request); -static DCDBAS_DEV_ATTR_RW(host_control_action); -static DCDBAS_DEV_ATTR_RW(host_control_smi_type); -static DCDBAS_DEV_ATTR_RW(host_control_on_shutdown); - -static struct attribute *dcdbas_dev_attrs[] = { - &dev_attr_smi_data_buf_size.attr, - &dev_attr_smi_data_buf_phys_addr.attr, - &dev_attr_smi_request.attr, - &dev_attr_host_control_action.attr, - &dev_attr_host_control_smi_type.attr, - &dev_attr_host_control_on_shutdown.attr, - NULL -}; - -static const struct attribute_group dcdbas_attr_group = { - .attrs = dcdbas_dev_attrs, - .bin_attrs = dcdbas_bin_attrs, -}; - -static int dcdbas_probe(struct platform_device *dev) -{ - int error; - - host_control_action = HC_ACTION_NONE; - host_control_smi_type = HC_SMITYPE_NONE; - - dcdbas_pdev = dev; - - /* Check if ACPI WSMT table specifies protected SMI buffer address */ - error = dcdbas_check_wsmt(); - if (error < 0) - return error; - - /* - * BIOS SMI calls require buffer addresses be in 32-bit address space. - * This is done by setting the DMA mask below. - */ - error = dma_set_coherent_mask(&dcdbas_pdev->dev, DMA_BIT_MASK(32)); - if (error) - return error; - - error = sysfs_create_group(&dev->dev.kobj, &dcdbas_attr_group); - if (error) - return error; - - register_reboot_notifier(&dcdbas_reboot_nb); - - dev_info(&dev->dev, "%s (version %s)\n", - DRIVER_DESCRIPTION, DRIVER_VERSION); - - return 0; -} - -static int dcdbas_remove(struct platform_device *dev) -{ - unregister_reboot_notifier(&dcdbas_reboot_nb); - sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group); - - return 0; -} - -static struct platform_driver dcdbas_driver = { - .driver = { - .name = DRIVER_NAME, - }, - .probe = dcdbas_probe, - .remove = dcdbas_remove, -}; - -static const struct platform_device_info dcdbas_dev_info __initconst = { - .name = DRIVER_NAME, - .id = -1, - .dma_mask = DMA_BIT_MASK(32), -}; - -static struct platform_device *dcdbas_pdev_reg; - -/** - * dcdbas_init: initialize driver - */ -static int __init dcdbas_init(void) -{ - int error; - - error = platform_driver_register(&dcdbas_driver); - if (error) - return error; - - dcdbas_pdev_reg = platform_device_register_full(&dcdbas_dev_info); - if (IS_ERR(dcdbas_pdev_reg)) { - error = PTR_ERR(dcdbas_pdev_reg); - goto err_unregister_driver; - } - - return 0; - - err_unregister_driver: - platform_driver_unregister(&dcdbas_driver); - return error; -} - -/** - * dcdbas_exit: perform driver cleanup - */ -static void __exit dcdbas_exit(void) -{ - /* - * make sure functions that use dcdbas_pdev are called - * before platform_device_unregister - */ - unregister_reboot_notifier(&dcdbas_reboot_nb); - - /* - * We have to free the buffer here instead of dcdbas_remove - * because only in module exit function we can be sure that - * all sysfs attributes belonging to this module have been - * released. - */ - if (dcdbas_pdev) - smi_data_buf_free(); - if (eps_buffer) - memunmap(eps_buffer); - platform_device_unregister(dcdbas_pdev_reg); - platform_driver_unregister(&dcdbas_driver); -} - -subsys_initcall_sync(dcdbas_init); -module_exit(dcdbas_exit); - -MODULE_DESCRIPTION(DRIVER_DESCRIPTION " (version " DRIVER_VERSION ")"); -MODULE_VERSION(DRIVER_VERSION); -MODULE_AUTHOR("Dell Inc."); -MODULE_LICENSE("GPL"); -/* Any System or BIOS claiming to be by Dell */ -MODULE_ALIAS("dmi:*:[bs]vnD[Ee][Ll][Ll]*:*"); diff --git a/drivers/firmware/dcdbas.h b/drivers/firmware/dcdbas.h deleted file mode 100644 index 52729a494b00..000000000000 --- a/drivers/firmware/dcdbas.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * dcdbas.h: Definitions for Dell Systems Management Base driver - * - * Copyright (C) 1995-2005 Dell Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _DCDBAS_H_ -#define _DCDBAS_H_ - -#include -#include -#include - -#define MAX_SMI_DATA_BUF_SIZE (256 * 1024) - -#define HC_ACTION_NONE (0) -#define HC_ACTION_HOST_CONTROL_POWEROFF BIT(1) -#define HC_ACTION_HOST_CONTROL_POWERCYCLE BIT(2) - -#define HC_SMITYPE_NONE (0) -#define HC_SMITYPE_TYPE1 (1) -#define HC_SMITYPE_TYPE2 (2) -#define HC_SMITYPE_TYPE3 (3) - -#define ESM_APM_CMD (0x0A0) -#define ESM_APM_POWER_CYCLE (0x10) -#define ESM_STATUS_CMD_UNSUCCESSFUL (-1) - -#define CMOS_BASE_PORT (0x070) -#define CMOS_PAGE1_INDEX_PORT (0) -#define CMOS_PAGE1_DATA_PORT (1) -#define CMOS_PAGE2_INDEX_PORT_PIIX4 (2) -#define CMOS_PAGE2_DATA_PORT_PIIX4 (3) -#define PE1400_APM_CONTROL_PORT (0x0B0) -#define PCAT_APM_CONTROL_PORT (0x0B2) -#define PCAT_APM_STATUS_PORT (0x0B3) -#define PE1300_CMOS_CMD_STRUCT_PTR (0x38) -#define PE1400_CMOS_CMD_STRUCT_PTR (0x70) - -#define MAX_SYSMGMT_SHORTCMD_PARMBUF_LEN (14) -#define MAX_SYSMGMT_LONGCMD_SGENTRY_NUM (16) - -#define TIMEOUT_USEC_SHORT_SEMA_BLOCKING (10000) -#define EXPIRED_TIMER (0) - -#define SMI_CMD_MAGIC (0x534D4931) -#define SMM_EPS_SIG "$SCB" - -#define DCDBAS_DEV_ATTR_RW(_name) \ - DEVICE_ATTR(_name,0600,_name##_show,_name##_store); - -#define DCDBAS_DEV_ATTR_RO(_name) \ - DEVICE_ATTR(_name,0400,_name##_show,NULL); - -#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; - __u32 ecx; - __u16 command_address; - __u8 command_code; - __u8 reserved; - __u8 command_buffer[1]; -} __attribute__ ((packed)); - -struct apm_cmd { - __u8 command; - __s8 status; - __u16 reserved; - union { - struct { - __u8 parm[MAX_SYSMGMT_SHORTCMD_PARMBUF_LEN]; - } __attribute__ ((packed)) shortreq; - - struct { - __u16 num_sg_entries; - struct { - __u32 size; - __u64 addr; - } __attribute__ ((packed)) - sglist[MAX_SYSMGMT_LONGCMD_SGENTRY_NUM]; - } __attribute__ ((packed)) longreq; - } __attribute__ ((packed)) parameters; -} __attribute__ ((packed)); - -int dcdbas_smi_request(struct smi_cmd *smi_cmd); - -struct smm_eps_table { - char smm_comm_buff_anchor[4]; - u8 length; - u8 checksum; - u8 version; - u64 smm_comm_buff_addr; - u64 num_of_4k_pages; -} __packed; - -#endif /* _DCDBAS_H_ */ - diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 097212c712f7..4283239115ca 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -108,6 +108,22 @@ config ASUS_LAPTOP If you have an ACPI-compatible ASUS laptop, say Y or M here. +config DCDBAS + tristate "Dell Systems Management Base Driver" + depends on X86 + help + The Dell Systems Management Base Driver provides a sysfs interface + for systems management software to perform System Management + Interrupts (SMIs) and Host Control Actions (system power cycle or + power off after OS shutdown) on certain Dell systems. + + See for more details on the driver + and the Dell systems on which Dell systems management software makes + use of this driver. + + Say Y or M here to enable the driver for use by Dell systems + management software such as Dell OpenManage. + # # The DELL_SMBIOS driver depends on ACPI_WMI and/or DCDBAS if those # backends are selected. The "depends" line prevents a configuration diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 8843f8e22127..4e2712c9c0b0 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o +obj-$(CONFIG_DCDBAS) += dcdbas.o obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o dell-smbios-objs := dell-smbios-base.o dell-smbios-$(CONFIG_DELL_SMBIOS_WMI) += dell-smbios-wmi.o diff --git a/drivers/platform/x86/dcdbas.c b/drivers/platform/x86/dcdbas.c new file mode 100644 index 000000000000..ae28e48ff7dc --- /dev/null +++ b/drivers/platform/x86/dcdbas.c @@ -0,0 +1,761 @@ +/* + * dcdbas.c: Dell Systems Management Base Driver + * + * The Dell Systems Management Base Driver provides a sysfs interface for + * systems management software to perform System Management Interrupts (SMIs) + * and Host Control Actions (power cycle or power off after OS shutdown) on + * Dell systems. + * + * See Documentation/dcdbas.txt for more information. + * + * Copyright (C) 1995-2006 Dell Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dcdbas.h" + +#define DRIVER_NAME "dcdbas" +#define DRIVER_VERSION "5.6.0-3.3" +#define DRIVER_DESCRIPTION "Dell Systems Management Base Driver" + +static struct platform_device *dcdbas_pdev; + +static u8 *smi_data_buf; +static dma_addr_t smi_data_buf_handle; +static unsigned long smi_data_buf_size; +static unsigned long max_smi_data_buf_size = MAX_SMI_DATA_BUF_SIZE; +static u32 smi_data_buf_phys_addr; +static DEFINE_MUTEX(smi_data_lock); +static u8 *eps_buffer; + +static unsigned int host_control_action; +static unsigned int host_control_smi_type; +static unsigned int host_control_on_shutdown; + +static bool wsmt_enabled; + +/** + * smi_data_buf_free: free SMI data buffer + */ +static void smi_data_buf_free(void) +{ + if (!smi_data_buf || wsmt_enabled) + return; + + dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", + __func__, smi_data_buf_phys_addr, smi_data_buf_size); + + dma_free_coherent(&dcdbas_pdev->dev, smi_data_buf_size, smi_data_buf, + smi_data_buf_handle); + smi_data_buf = NULL; + smi_data_buf_handle = 0; + smi_data_buf_phys_addr = 0; + smi_data_buf_size = 0; +} + +/** + * smi_data_buf_realloc: grow SMI data buffer if needed + */ +static int smi_data_buf_realloc(unsigned long size) +{ + void *buf; + dma_addr_t handle; + + if (smi_data_buf_size >= size) + return 0; + + if (size > max_smi_data_buf_size) + return -EINVAL; + + /* new buffer is needed */ + buf = dma_alloc_coherent(&dcdbas_pdev->dev, size, &handle, GFP_KERNEL); + if (!buf) { + dev_dbg(&dcdbas_pdev->dev, + "%s: failed to allocate memory size %lu\n", + __func__, size); + return -ENOMEM; + } + /* memory zeroed by dma_alloc_coherent */ + + if (smi_data_buf) + memcpy(buf, smi_data_buf, smi_data_buf_size); + + /* free any existing buffer */ + smi_data_buf_free(); + + /* set up new buffer for use */ + smi_data_buf = buf; + smi_data_buf_handle = handle; + smi_data_buf_phys_addr = (u32) virt_to_phys(buf); + smi_data_buf_size = size; + + dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", + __func__, smi_data_buf_phys_addr, smi_data_buf_size); + + return 0; +} + +static ssize_t smi_data_buf_phys_addr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%x\n", smi_data_buf_phys_addr); +} + +static ssize_t smi_data_buf_size_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%lu\n", smi_data_buf_size); +} + +static ssize_t smi_data_buf_size_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long buf_size; + ssize_t ret; + + buf_size = simple_strtoul(buf, NULL, 10); + + /* make sure SMI data buffer is at least buf_size */ + mutex_lock(&smi_data_lock); + ret = smi_data_buf_realloc(buf_size); + mutex_unlock(&smi_data_lock); + if (ret) + return ret; + + return count; +} + +static ssize_t smi_data_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t count) +{ + ssize_t ret; + + mutex_lock(&smi_data_lock); + ret = memory_read_from_buffer(buf, count, &pos, smi_data_buf, + smi_data_buf_size); + mutex_unlock(&smi_data_lock); + return ret; +} + +static ssize_t smi_data_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t count) +{ + ssize_t ret; + + if ((pos + count) > max_smi_data_buf_size) + return -EINVAL; + + mutex_lock(&smi_data_lock); + + ret = smi_data_buf_realloc(pos + count); + if (ret) + goto out; + + memcpy(smi_data_buf + pos, buf, count); + ret = count; +out: + mutex_unlock(&smi_data_lock); + return ret; +} + +static ssize_t host_control_action_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", host_control_action); +} + +static ssize_t host_control_action_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t ret; + + /* make sure buffer is available for host control command */ + mutex_lock(&smi_data_lock); + ret = smi_data_buf_realloc(sizeof(struct apm_cmd)); + mutex_unlock(&smi_data_lock); + if (ret) + return ret; + + host_control_action = simple_strtoul(buf, NULL, 10); + return count; +} + +static ssize_t host_control_smi_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", host_control_smi_type); +} + +static ssize_t host_control_smi_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + host_control_smi_type = simple_strtoul(buf, NULL, 10); + return count; +} + +static ssize_t host_control_on_shutdown_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", host_control_on_shutdown); +} + +static ssize_t host_control_on_shutdown_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + host_control_on_shutdown = simple_strtoul(buf, NULL, 10); + return count; +} + +static int raise_smi(void *par) +{ + struct smi_cmd *smi_cmd = par; + + if (smp_processor_id() != 0) { + dev_dbg(&dcdbas_pdev->dev, "%s: failed to get CPU 0\n", + __func__); + return -EBUSY; + } + + /* generate SMI */ + /* inb to force posted write through and make SMI happen now */ + asm volatile ( + "outb %b0,%w1\n" + "inb %w1" + : /* no output args */ + : "a" (smi_cmd->command_code), + "d" (smi_cmd->command_address), + "b" (smi_cmd->ebx), + "c" (smi_cmd->ecx) + : "memory" + ); + + return 0; +} +/** + * dcdbas_smi_request: generate SMI request + * + * Called with smi_data_lock. + */ +int dcdbas_smi_request(struct smi_cmd *smi_cmd) +{ + int ret; + + if (smi_cmd->magic != SMI_CMD_MAGIC) { + dev_info(&dcdbas_pdev->dev, "%s: invalid magic value\n", + __func__); + return -EBADR; + } + + /* SMI requires CPU 0 */ + get_online_cpus(); + ret = smp_call_on_cpu(0, raise_smi, smi_cmd, true); + put_online_cpus(); + + return ret; +} + +/** + * smi_request_store: + * + * The valid values are: + * 0: zero SMI data buffer + * 1: generate calling interface SMI + * 2: generate raw SMI + * + * User application writes smi_cmd to smi_data before telling driver + * to generate SMI. + */ +static ssize_t smi_request_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct smi_cmd *smi_cmd; + unsigned long val = simple_strtoul(buf, NULL, 10); + ssize_t ret; + + mutex_lock(&smi_data_lock); + + if (smi_data_buf_size < sizeof(struct smi_cmd)) { + ret = -ENODEV; + goto out; + } + smi_cmd = (struct smi_cmd *)smi_data_buf; + + switch (val) { + case 2: + /* Raw SMI */ + ret = dcdbas_smi_request(smi_cmd); + if (!ret) + ret = count; + break; + case 1: + /* + * Calling Interface SMI + * + * Provide physical address of command buffer field within + * the struct smi_cmd to BIOS. + * + * Because the address that smi_cmd (smi_data_buf) points to + * will be from memremap() of a non-memory address if WSMT + * is present, we can't use virt_to_phys() on smi_cmd, so + * we have to use the physical address that was saved when + * the virtual address for smi_cmd was received. + */ + smi_cmd->ebx = smi_data_buf_phys_addr + + offsetof(struct smi_cmd, command_buffer); + ret = dcdbas_smi_request(smi_cmd); + if (!ret) + ret = count; + break; + case 0: + memset(smi_data_buf, 0, smi_data_buf_size); + ret = count; + break; + default: + ret = -EINVAL; + break; + } + +out: + mutex_unlock(&smi_data_lock); + return ret; +} +EXPORT_SYMBOL(dcdbas_smi_request); + +/** + * host_control_smi: generate host control SMI + * + * Caller must set up the host control command in smi_data_buf. + */ +static int host_control_smi(void) +{ + struct apm_cmd *apm_cmd; + u8 *data; + unsigned long flags; + u32 num_ticks; + s8 cmd_status; + u8 index; + + apm_cmd = (struct apm_cmd *)smi_data_buf; + apm_cmd->status = ESM_STATUS_CMD_UNSUCCESSFUL; + + switch (host_control_smi_type) { + case HC_SMITYPE_TYPE1: + spin_lock_irqsave(&rtc_lock, flags); + /* write SMI data buffer physical address */ + data = (u8 *)&smi_data_buf_phys_addr; + for (index = PE1300_CMOS_CMD_STRUCT_PTR; + index < (PE1300_CMOS_CMD_STRUCT_PTR + 4); + index++, data++) { + outb(index, + (CMOS_BASE_PORT + CMOS_PAGE2_INDEX_PORT_PIIX4)); + outb(*data, + (CMOS_BASE_PORT + CMOS_PAGE2_DATA_PORT_PIIX4)); + } + + /* first set status to -1 as called by spec */ + cmd_status = ESM_STATUS_CMD_UNSUCCESSFUL; + outb((u8) cmd_status, PCAT_APM_STATUS_PORT); + + /* generate SMM call */ + outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT); + spin_unlock_irqrestore(&rtc_lock, flags); + + /* wait a few to see if it executed */ + num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING; + while ((cmd_status = inb(PCAT_APM_STATUS_PORT)) + == ESM_STATUS_CMD_UNSUCCESSFUL) { + num_ticks--; + if (num_ticks == EXPIRED_TIMER) + return -ETIME; + } + break; + + case HC_SMITYPE_TYPE2: + case HC_SMITYPE_TYPE3: + spin_lock_irqsave(&rtc_lock, flags); + /* write SMI data buffer physical address */ + data = (u8 *)&smi_data_buf_phys_addr; + for (index = PE1400_CMOS_CMD_STRUCT_PTR; + index < (PE1400_CMOS_CMD_STRUCT_PTR + 4); + index++, data++) { + outb(index, (CMOS_BASE_PORT + CMOS_PAGE1_INDEX_PORT)); + outb(*data, (CMOS_BASE_PORT + CMOS_PAGE1_DATA_PORT)); + } + + /* generate SMM call */ + if (host_control_smi_type == HC_SMITYPE_TYPE3) + outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT); + else + outb(ESM_APM_CMD, PE1400_APM_CONTROL_PORT); + + /* restore RTC index pointer since it was written to above */ + CMOS_READ(RTC_REG_C); + spin_unlock_irqrestore(&rtc_lock, flags); + + /* read control port back to serialize write */ + cmd_status = inb(PE1400_APM_CONTROL_PORT); + + /* wait a few to see if it executed */ + num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING; + while (apm_cmd->status == ESM_STATUS_CMD_UNSUCCESSFUL) { + num_ticks--; + if (num_ticks == EXPIRED_TIMER) + return -ETIME; + } + break; + + default: + dev_dbg(&dcdbas_pdev->dev, "%s: invalid SMI type %u\n", + __func__, host_control_smi_type); + return -ENOSYS; + } + + return 0; +} + +/** + * dcdbas_host_control: initiate host control + * + * This function is called by the driver after the system has + * finished shutting down if the user application specified a + * host control action to perform on shutdown. It is safe to + * use smi_data_buf at this point because the system has finished + * shutting down and no userspace apps are running. + */ +static void dcdbas_host_control(void) +{ + struct apm_cmd *apm_cmd; + u8 action; + + if (host_control_action == HC_ACTION_NONE) + return; + + action = host_control_action; + host_control_action = HC_ACTION_NONE; + + if (!smi_data_buf) { + dev_dbg(&dcdbas_pdev->dev, "%s: no SMI buffer\n", __func__); + return; + } + + if (smi_data_buf_size < sizeof(struct apm_cmd)) { + dev_dbg(&dcdbas_pdev->dev, "%s: SMI buffer too small\n", + __func__); + return; + } + + apm_cmd = (struct apm_cmd *)smi_data_buf; + + /* power off takes precedence */ + if (action & HC_ACTION_HOST_CONTROL_POWEROFF) { + apm_cmd->command = ESM_APM_POWER_CYCLE; + apm_cmd->reserved = 0; + *((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16) 0; + host_control_smi(); + } else if (action & HC_ACTION_HOST_CONTROL_POWERCYCLE) { + apm_cmd->command = ESM_APM_POWER_CYCLE; + apm_cmd->reserved = 0; + *((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16) 20; + host_control_smi(); + } +} + +/* WSMT */ + +static u8 checksum(u8 *buffer, u8 length) +{ + u8 sum = 0; + u8 *end = buffer + length; + + while (buffer < end) + sum += *buffer++; + return sum; +} + +static inline struct smm_eps_table *check_eps_table(u8 *addr) +{ + struct smm_eps_table *eps = (struct smm_eps_table *)addr; + + if (strncmp(eps->smm_comm_buff_anchor, SMM_EPS_SIG, 4) != 0) + return NULL; + + if (checksum(addr, eps->length) != 0) + return NULL; + + return eps; +} + +static int dcdbas_check_wsmt(void) +{ + struct acpi_table_wsmt *wsmt = NULL; + struct smm_eps_table *eps = NULL; + u64 remap_size; + u8 *addr; + + acpi_get_table(ACPI_SIG_WSMT, 0, (struct acpi_table_header **)&wsmt); + if (!wsmt) + return 0; + + /* Check if WSMT ACPI table shows that protection is enabled */ + if (!(wsmt->protection_flags & ACPI_WSMT_FIXED_COMM_BUFFERS) || + !(wsmt->protection_flags & ACPI_WSMT_COMM_BUFFER_NESTED_PTR_PROTECTION)) + return 0; + + /* Scan for EPS (entry point structure) */ + for (addr = (u8 *)__va(0xf0000); + addr < (u8 *)__va(0x100000 - sizeof(struct smm_eps_table)); + addr += 16) { + eps = check_eps_table(addr); + if (eps) + break; + } + + if (!eps) { + dev_dbg(&dcdbas_pdev->dev, "found WSMT, but no EPS found\n"); + return -ENODEV; + } + + /* + * Get physical address of buffer and map to virtual address. + * Table gives size in 4K pages, regardless of actual system page size. + */ + if (upper_32_bits(eps->smm_comm_buff_addr + 8)) { + dev_warn(&dcdbas_pdev->dev, "found WSMT, but EPS buffer address is above 4GB\n"); + return -EINVAL; + } + /* + * Limit remap size to MAX_SMI_DATA_BUF_SIZE + 8 (since the first 8 + * bytes are used for a semaphore, not the data buffer itself). + */ + remap_size = eps->num_of_4k_pages * PAGE_SIZE; + if (remap_size > MAX_SMI_DATA_BUF_SIZE + 8) + remap_size = MAX_SMI_DATA_BUF_SIZE + 8; + eps_buffer = memremap(eps->smm_comm_buff_addr, remap_size, MEMREMAP_WB); + if (!eps_buffer) { + dev_warn(&dcdbas_pdev->dev, "found WSMT, but failed to map EPS buffer\n"); + return -ENOMEM; + } + + /* First 8 bytes is for a semaphore, not part of the smi_data_buf */ + smi_data_buf_phys_addr = eps->smm_comm_buff_addr + 8; + smi_data_buf = eps_buffer + 8; + smi_data_buf_size = remap_size - 8; + max_smi_data_buf_size = smi_data_buf_size; + wsmt_enabled = true; + dev_info(&dcdbas_pdev->dev, + "WSMT found, using firmware-provided SMI buffer.\n"); + return 1; +} + +/** + * dcdbas_reboot_notify: handle reboot notification for host control + */ +static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code, + void *unused) +{ + switch (code) { + case SYS_DOWN: + case SYS_HALT: + case SYS_POWER_OFF: + if (host_control_on_shutdown) { + /* firmware is going to perform host control action */ + printk(KERN_WARNING "Please wait for shutdown " + "action to complete...\n"); + dcdbas_host_control(); + } + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block dcdbas_reboot_nb = { + .notifier_call = dcdbas_reboot_notify, + .next = NULL, + .priority = INT_MIN +}; + +static DCDBAS_BIN_ATTR_RW(smi_data); + +static struct bin_attribute *dcdbas_bin_attrs[] = { + &bin_attr_smi_data, + NULL +}; + +static DCDBAS_DEV_ATTR_RW(smi_data_buf_size); +static DCDBAS_DEV_ATTR_RO(smi_data_buf_phys_addr); +static DCDBAS_DEV_ATTR_WO(smi_request); +static DCDBAS_DEV_ATTR_RW(host_control_action); +static DCDBAS_DEV_ATTR_RW(host_control_smi_type); +static DCDBAS_DEV_ATTR_RW(host_control_on_shutdown); + +static struct attribute *dcdbas_dev_attrs[] = { + &dev_attr_smi_data_buf_size.attr, + &dev_attr_smi_data_buf_phys_addr.attr, + &dev_attr_smi_request.attr, + &dev_attr_host_control_action.attr, + &dev_attr_host_control_smi_type.attr, + &dev_attr_host_control_on_shutdown.attr, + NULL +}; + +static const struct attribute_group dcdbas_attr_group = { + .attrs = dcdbas_dev_attrs, + .bin_attrs = dcdbas_bin_attrs, +}; + +static int dcdbas_probe(struct platform_device *dev) +{ + int error; + + host_control_action = HC_ACTION_NONE; + host_control_smi_type = HC_SMITYPE_NONE; + + dcdbas_pdev = dev; + + /* Check if ACPI WSMT table specifies protected SMI buffer address */ + error = dcdbas_check_wsmt(); + if (error < 0) + return error; + + /* + * BIOS SMI calls require buffer addresses be in 32-bit address space. + * This is done by setting the DMA mask below. + */ + error = dma_set_coherent_mask(&dcdbas_pdev->dev, DMA_BIT_MASK(32)); + if (error) + return error; + + error = sysfs_create_group(&dev->dev.kobj, &dcdbas_attr_group); + if (error) + return error; + + register_reboot_notifier(&dcdbas_reboot_nb); + + dev_info(&dev->dev, "%s (version %s)\n", + DRIVER_DESCRIPTION, DRIVER_VERSION); + + return 0; +} + +static int dcdbas_remove(struct platform_device *dev) +{ + unregister_reboot_notifier(&dcdbas_reboot_nb); + sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group); + + return 0; +} + +static struct platform_driver dcdbas_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = dcdbas_probe, + .remove = dcdbas_remove, +}; + +static const struct platform_device_info dcdbas_dev_info __initconst = { + .name = DRIVER_NAME, + .id = -1, + .dma_mask = DMA_BIT_MASK(32), +}; + +static struct platform_device *dcdbas_pdev_reg; + +/** + * dcdbas_init: initialize driver + */ +static int __init dcdbas_init(void) +{ + int error; + + error = platform_driver_register(&dcdbas_driver); + if (error) + return error; + + dcdbas_pdev_reg = platform_device_register_full(&dcdbas_dev_info); + if (IS_ERR(dcdbas_pdev_reg)) { + error = PTR_ERR(dcdbas_pdev_reg); + goto err_unregister_driver; + } + + return 0; + + err_unregister_driver: + platform_driver_unregister(&dcdbas_driver); + return error; +} + +/** + * dcdbas_exit: perform driver cleanup + */ +static void __exit dcdbas_exit(void) +{ + /* + * make sure functions that use dcdbas_pdev are called + * before platform_device_unregister + */ + unregister_reboot_notifier(&dcdbas_reboot_nb); + + /* + * We have to free the buffer here instead of dcdbas_remove + * because only in module exit function we can be sure that + * all sysfs attributes belonging to this module have been + * released. + */ + if (dcdbas_pdev) + smi_data_buf_free(); + if (eps_buffer) + memunmap(eps_buffer); + platform_device_unregister(dcdbas_pdev_reg); + platform_driver_unregister(&dcdbas_driver); +} + +subsys_initcall_sync(dcdbas_init); +module_exit(dcdbas_exit); + +MODULE_DESCRIPTION(DRIVER_DESCRIPTION " (version " DRIVER_VERSION ")"); +MODULE_VERSION(DRIVER_VERSION); +MODULE_AUTHOR("Dell Inc."); +MODULE_LICENSE("GPL"); +/* Any System or BIOS claiming to be by Dell */ +MODULE_ALIAS("dmi:*:[bs]vnD[Ee][Ll][Ll]*:*"); diff --git a/drivers/platform/x86/dcdbas.h b/drivers/platform/x86/dcdbas.h new file mode 100644 index 000000000000..52729a494b00 --- /dev/null +++ b/drivers/platform/x86/dcdbas.h @@ -0,0 +1,117 @@ +/* + * dcdbas.h: Definitions for Dell Systems Management Base driver + * + * Copyright (C) 1995-2005 Dell Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DCDBAS_H_ +#define _DCDBAS_H_ + +#include +#include +#include + +#define MAX_SMI_DATA_BUF_SIZE (256 * 1024) + +#define HC_ACTION_NONE (0) +#define HC_ACTION_HOST_CONTROL_POWEROFF BIT(1) +#define HC_ACTION_HOST_CONTROL_POWERCYCLE BIT(2) + +#define HC_SMITYPE_NONE (0) +#define HC_SMITYPE_TYPE1 (1) +#define HC_SMITYPE_TYPE2 (2) +#define HC_SMITYPE_TYPE3 (3) + +#define ESM_APM_CMD (0x0A0) +#define ESM_APM_POWER_CYCLE (0x10) +#define ESM_STATUS_CMD_UNSUCCESSFUL (-1) + +#define CMOS_BASE_PORT (0x070) +#define CMOS_PAGE1_INDEX_PORT (0) +#define CMOS_PAGE1_DATA_PORT (1) +#define CMOS_PAGE2_INDEX_PORT_PIIX4 (2) +#define CMOS_PAGE2_DATA_PORT_PIIX4 (3) +#define PE1400_APM_CONTROL_PORT (0x0B0) +#define PCAT_APM_CONTROL_PORT (0x0B2) +#define PCAT_APM_STATUS_PORT (0x0B3) +#define PE1300_CMOS_CMD_STRUCT_PTR (0x38) +#define PE1400_CMOS_CMD_STRUCT_PTR (0x70) + +#define MAX_SYSMGMT_SHORTCMD_PARMBUF_LEN (14) +#define MAX_SYSMGMT_LONGCMD_SGENTRY_NUM (16) + +#define TIMEOUT_USEC_SHORT_SEMA_BLOCKING (10000) +#define EXPIRED_TIMER (0) + +#define SMI_CMD_MAGIC (0x534D4931) +#define SMM_EPS_SIG "$SCB" + +#define DCDBAS_DEV_ATTR_RW(_name) \ + DEVICE_ATTR(_name,0600,_name##_show,_name##_store); + +#define DCDBAS_DEV_ATTR_RO(_name) \ + DEVICE_ATTR(_name,0400,_name##_show,NULL); + +#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; + __u32 ecx; + __u16 command_address; + __u8 command_code; + __u8 reserved; + __u8 command_buffer[1]; +} __attribute__ ((packed)); + +struct apm_cmd { + __u8 command; + __s8 status; + __u16 reserved; + union { + struct { + __u8 parm[MAX_SYSMGMT_SHORTCMD_PARMBUF_LEN]; + } __attribute__ ((packed)) shortreq; + + struct { + __u16 num_sg_entries; + struct { + __u32 size; + __u64 addr; + } __attribute__ ((packed)) + sglist[MAX_SYSMGMT_LONGCMD_SGENTRY_NUM]; + } __attribute__ ((packed)) longreq; + } __attribute__ ((packed)) parameters; +} __attribute__ ((packed)); + +int dcdbas_smi_request(struct smi_cmd *smi_cmd); + +struct smm_eps_table { + char smm_comm_buff_anchor[4]; + u8 length; + u8 checksum; + u8 version; + u64 smm_comm_buff_addr; + u64 num_of_4k_pages; +} __packed; + +#endif /* _DCDBAS_H_ */ + diff --git a/drivers/platform/x86/dell-smbios-smm.c b/drivers/platform/x86/dell-smbios-smm.c index 97a90bebc360..ab9b822a6dfe 100644 --- a/drivers/platform/x86/dell-smbios-smm.c +++ b/drivers/platform/x86/dell-smbios-smm.c @@ -18,7 +18,7 @@ #include #include #include -#include "../../firmware/dcdbas.h" +#include "dcdbas.h" #include "dell-smbios.h" static int da_command_address; -- cgit From 24c6f7200bb326122e65528e44a12962403185fe Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel_bxtwc_tmu: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_bxtwc_tmu.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_bxtwc_tmu.c b/drivers/platform/x86/intel_bxtwc_tmu.c index 227943a20212..951c105bafc1 100644 --- a/drivers/platform/x86/intel_bxtwc_tmu.c +++ b/drivers/platform/x86/intel_bxtwc_tmu.c @@ -1,21 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * intel_bxtwc_tmu.c - Intel BXT Whiskey Cove PMIC TMU driver + * Intel BXT Whiskey Cove PMIC TMU driver * * Copyright (C) 2016 Intel Corporation. All rights reserved. * * This driver adds TMU (Time Management Unit) support for Intel BXT platform. * It enables the alarm wake-up functionality in the TMU unit of Whiskey Cove * PMIC. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include -- cgit From 83e8ee26cb5ad54d37d6f6359979e004ce7cb9e9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel_cht_int33fe: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. While here, correct MODULE_LICENSE() string to be aligned with license text. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_cht_int33fe.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_cht_int33fe.c b/drivers/platform/x86/intel_cht_int33fe.c index 39d4100c60a2..6531a0c3cc0d 100644 --- a/drivers/platform/x86/intel_cht_int33fe.c +++ b/drivers/platform/x86/intel_cht_int33fe.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Cherry Trail ACPI INT33FE pseudo device driver * * Copyright (C) 2017 Hans de Goede * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * Some Intel Cherry Trail based device which ship with Windows 10, have * this weird INT33FE ACPI device with a CRS table with 4 I2cSerialBusV2 * resources, for 4 different chips attached to various i2c busses: @@ -266,4 +263,4 @@ module_i2c_driver(cht_int33fe_driver); MODULE_DESCRIPTION("Intel Cherry Trail ACPI INT33FE pseudo device driver"); MODULE_AUTHOR("Hans de Goede "); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); -- cgit From 924931745bf392a5aa25ad88c09edbb414556334 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:58:52 +0300 Subject: platform/x86: intel_chtdc_ti_pwrbtn: Add SPDX identifier Driver misses the licence text or identifier, thus, append it here. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_chtdc_ti_pwrbtn.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_chtdc_ti_pwrbtn.c b/drivers/platform/x86/intel_chtdc_ti_pwrbtn.c index 38b8e7cfe88c..0df2e82dd249 100644 --- a/drivers/platform/x86/intel_chtdc_ti_pwrbtn.c +++ b/drivers/platform/x86/intel_chtdc_ti_pwrbtn.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Power-button driver for Dollar Cove TI PMIC * Copyright (C) 2014 Intel Corp -- cgit From 31daa5dd32fc77d3114ad0f6e1a2f1aedfe43616 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel-hid: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel-hid.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index 6cf9b7fa5bf0..e28bcf61b126 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -1,19 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Intel HID event & 5 button array driver * * Copyright (C) 2015 Alex Hung * Copyright (C) 2015 Andrew Lutomirski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include -- cgit From 79c24dbdcbcb23e722e5c2680dba642fcc74a357 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel_int0002_vgpio: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. While here, correct MODULE_LICENSE() string to be aligned with license text. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_int0002_vgpio.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c index 33c3489f5bc1..c958a628c375 100644 --- a/drivers/platform/x86/intel_int0002_vgpio.c +++ b/drivers/platform/x86/intel_int0002_vgpio.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel INT0002 "Virtual GPIO" driver * @@ -9,10 +10,6 @@ * * Author: Dyut Kumar Sil * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * Some peripherals on Bay Trail and Cherry Trail platforms signal a Power * Management Event (PME) to the Power Management Controller (PMC) to wakeup * the system. When this happens software needs to clear the PME bus 0 status @@ -228,4 +225,4 @@ module_platform_driver(int0002_driver); MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("Intel INT0002 Virtual GPIO driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); -- cgit From c4e4c94641109095b04024ba15b899f621c52ca4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel-ips: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. While here, correct MODULE_LICENSE() string to be aligned with license text. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_ips.c | 15 ++------------- drivers/platform/x86/intel_ips.h | 13 +------------ 2 files changed, 3 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index c5ece7ef08c6..225638a1b09e 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -1,18 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2009-2010 Intel Corporation * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * * Authors: * Jesse Barnes */ @@ -1697,6 +1686,6 @@ static struct pci_driver ips_pci_driver = { module_pci_driver(ips_pci_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jesse Barnes "); MODULE_DESCRIPTION("Intelligent Power Sharing Driver"); diff --git a/drivers/platform/x86/intel_ips.h b/drivers/platform/x86/intel_ips.h index 60f4e3ddbe9f..512ad234ad0d 100644 --- a/drivers/platform/x86/intel_ips.h +++ b/drivers/platform/x86/intel_ips.h @@ -1,17 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2010 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". */ void ips_link_to_i915_driver(void); -- cgit From 8855ab3b2ef65de985484c063301cdc1b2aab187 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:14 +0300 Subject: platform/x86: intel_menlow: Sort headers alphabetically Sort headers alphabetically for better maintenance. No functional change. While here, remove unneeded linux/init.h inclusion. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_menlow.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index ef9b0af8cdd3..76854f0bf70b 100644 --- a/drivers/platform/x86/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c @@ -29,15 +29,14 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include -#include -#include -#include #include #include +#include #include -#include +#include MODULE_AUTHOR("Thomas Sujith"); MODULE_AUTHOR("Zhang Rui"); -- cgit From 0b5d9856b6070fe2ffc3906e0190d48f465ae771 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel_menlow: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. While here, correct MODULE_LICENSE() string to be aligned with license text. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_menlow.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index 76854f0bf70b..77eb8709c931 100644 --- a/drivers/platform/x86/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c @@ -1,25 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * intel_menlow.c - Intel menlow Driver for thermal management extension + * Intel menlow Driver for thermal management extension * * Copyright (C) 2008 Intel Corp * Copyright (C) 2008 Sujith Thomas * Copyright (C) 2008 Zhang Rui - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This driver creates the sys I/F for programming the sensors. * It also implements the driver for intel menlow memory controller (hardware @@ -41,7 +26,7 @@ MODULE_AUTHOR("Thomas Sujith"); MODULE_AUTHOR("Zhang Rui"); MODULE_DESCRIPTION("Intel Menlow platform specific driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); /* * Memory controller device control -- cgit From f7949b185b8eb15bb61610f627c7014c7f042feb Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel_mid_powerbtn: Remove unnecessary init.h inclusion No need to include linux/init.h when linux/module.h is. No functional change intended. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_mid_powerbtn.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index c93299002169..b8d659233c6a 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -16,7 +16,6 @@ * General Public License for more details. */ -#include #include #include #include -- cgit From f6b27d0907d0381c330c09ae6f7149c8121c3f37 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel_mid_powerbtn: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_mid_powerbtn.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index b8d659233c6a..a8c0fbb7f799 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Power button driver for Intel MID platforms. * @@ -5,15 +6,6 @@ * * Author: Hong Liu * Author: Andy Shevchenko - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. */ #include -- cgit From e6e69a31dc7412ac9fb40881ae9788ad82ac6c98 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:14 +0300 Subject: platform/x86: intel_mid_thermal: Sort headers alphabetically Sort headers alphabetically for better maintenance. No functional change. While here, remove unneeded linux/init.h inclusion. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_mid_thermal.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index 008a76903cbf..3fa8207dd34e 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -24,16 +24,15 @@ #define pr_fmt(fmt) "intel_mid_thermal: " fmt -#include -#include +#include #include +#include +#include #include -#include #include -#include #include +#include #include -#include /* Number of thermal sensors */ #define MSIC_THERMAL_SENSORS 4 -- cgit From 56df47de25402ba44382b89cab4665faed783711 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel_mid_thermal: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. While here, correct MODULE_LICENSE() string to be aligned with license text. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_mid_thermal.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index 3fa8207dd34e..f402e2e74a38 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -1,24 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * intel_mid_thermal.c - Intel MID platform thermal driver + * Intel MID platform thermal driver * * Copyright (C) 2011 Intel Corporation * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Author: Durgadoss R */ @@ -566,4 +551,4 @@ module_platform_driver(mid_thermal_driver); MODULE_AUTHOR("Durgadoss R "); MODULE_DESCRIPTION("Intel Medfield Platform Thermal Driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); -- cgit From bce99455f56ee45f128ffedd5c87201003850188 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:14 +0300 Subject: platform/x86: intel_oaktrail: Sort headers alphabetically Sort headers alphabetically for better maintenance. No functional change. While here, remove unneeded linux/init.h inclusion. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_oaktrail.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c index 5747f63c8d9f..1d98fd9ca1ea 100644 --- a/drivers/platform/x86/intel_oaktrail.c +++ b/drivers/platform/x86/intel_oaktrail.c @@ -38,18 +38,18 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include #include -#include -#include +#include +#include #include +#include #include -#include +#include +#include +#include #include -#include #include + #include #define DRIVER_NAME "intel_oaktrail" -- cgit From 56ca5ec102b4a4e8f6b945a3ed132d8677be528b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel_oaktrail: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_oaktrail.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c index 1d98fd9ca1ea..3c0438ba385e 100644 --- a/drivers/platform/x86/intel_oaktrail.c +++ b/drivers/platform/x86/intel_oaktrail.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0+ /* - * intel_oaktrail.c - Intel OakTrail Platform support. + * Intel OakTrail Platform support * * Copyright (C) 2010-2011 Intel Corporation * Author: Yin Kangkai (kangkai.yin@intel.com) @@ -8,21 +9,6 @@ * , based on MSI driver * Copyright (C) 2006 Lennart Poettering * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * * This driver does below things: * 1. registers itself in the Linux backlight control in * /sys/class/backlight/intel_oaktrail/ -- cgit From 908817720056ce19bfabd9c02e9bd09ff696d898 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:14 +0300 Subject: platform/x86: intel_pmc: Sort headers alphabetically Sort headers alphabetically for better maintenance. No functional change. While here, remove unneeded linux/init.h inclusion. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_pmc_ipc.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index e7edc8c63936..8b88e1743e41 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -15,25 +15,24 @@ * core through IPC mechanism which in turn messaging between IA core ad PMC. */ -#include +#include +#include +#include #include -#include -#include #include -#include +#include +#include +#include +#include +#include +#include #include #include -#include +#include #include -#include -#include #include -#include -#include -#include -#include -#include #include +#include #include -- cgit From ad51f287ff593efb559f23e655312f3b6694fd90 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel_pmc: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. While here, correct MODULE_LICENSE() string to be aligned with license text. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_pmc_core.c | 11 +---------- drivers/platform/x86/intel_pmc_core.h | 11 +---------- drivers/platform/x86/intel_pmc_ipc.c | 12 ++++-------- 3 files changed, 6 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c index 2d272a3e0176..6b31d410cb09 100644 --- a/drivers/platform/x86/intel_pmc_core.c +++ b/drivers/platform/x86/intel_pmc_core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Core SoC Power Management Controller Driver * @@ -6,16 +7,6 @@ * * Authors: Rajneesh Bhardwaj * Vishwanath Somayaji - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/platform/x86/intel_pmc_core.h b/drivers/platform/x86/intel_pmc_core.h index 93a7e99e1f8b..be045348ad86 100644 --- a/drivers/platform/x86/intel_pmc_core.h +++ b/drivers/platform/x86/intel_pmc_core.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Core SoC Power Management Controller Header File * @@ -6,16 +7,6 @@ * * Authors: Rajneesh Bhardwaj * Vishwanath Somayaji - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * */ #ifndef PMC_CORE_H diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index 8b88e1743e41..7964ba22ef8d 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -1,16 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * intel_pmc_ipc.c: Driver for the Intel PMC IPC mechanism + * Driver for the Intel PMC IPC mechanism * * (C) Copyright 2014-2015 Intel Corporation * - * This driver is based on Intel SCU IPC driver(intel_scu_opc.c) by + * This driver is based on Intel SCU IPC driver(intel_scu_ipc.c) by * Sreedhara DS * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - * * PMC running in ARC processor communicates with other entity running in IA * core through IPC mechanism which in turn messaging between IA core ad PMC. */ @@ -1028,7 +1024,7 @@ static void __exit intel_pmc_ipc_exit(void) MODULE_AUTHOR("Zha Qipeng "); MODULE_DESCRIPTION("Intel PMC IPC driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); /* Some modules are dependent on this, so init earlier */ fs_initcall(intel_pmc_ipc_init); -- cgit From 5e66d08e6b485bcb691c2c15806cfaaac8740da3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:14 +0300 Subject: platform/x86: intel_punit_ipc: Sort headers alphabetically Sort headers alphabetically for better maintenance. No functional change. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_punit_ipc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c index 2efeab650345..8eaa60c9ad69 100644 --- a/drivers/platform/x86/intel_punit_ipc.c +++ b/drivers/platform/x86/intel_punit_ipc.c @@ -11,15 +11,16 @@ * which provide mailbox interface for power management usage. */ -#include -#include #include -#include #include +#include #include #include #include +#include +#include #include + #include /* IPC Mailbox registers */ -- cgit From bc15757e0cfd1264bb4fae8bbd65ea736e7617af Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel_punit_ipc: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_punit_ipc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c index 8eaa60c9ad69..79671927f4ef 100644 --- a/drivers/platform/x86/intel_punit_ipc.c +++ b/drivers/platform/x86/intel_punit_ipc.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for the Intel P-Unit Mailbox IPC mechanism * * (C) Copyright 2015 Intel Corporation * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * The heart of the P-Unit is the Foxton microcontroller and its firmware, * which provide mailbox interface for power management usage. */ -- cgit From 5f4ad6afe96b69886b57ca3cbc6b5e88b7911367 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:14 +0300 Subject: platform/x86: intel-rst: Sort headers alphabetically Sort headers alphabetically for better maintenance. No functional change. While here, remove unneeded linux/init.h inclusion. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel-rst.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel-rst.c b/drivers/platform/x86/intel-rst.c index 7344d841f4d9..56abf5cbb824 100644 --- a/drivers/platform/x86/intel-rst.c +++ b/drivers/platform/x86/intel-rst.c @@ -17,10 +17,9 @@ */ -#include +#include #include #include -#include MODULE_LICENSE("GPL"); -- cgit From bd7c5866bbe09f085c51e87578c1fa94c22728e0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel-rst: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. While here, throw away some extra blank lines. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel-rst.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel-rst.c b/drivers/platform/x86/intel-rst.c index 56abf5cbb824..3b81cb896fed 100644 --- a/drivers/platform/x86/intel-rst.c +++ b/drivers/platform/x86/intel-rst.c @@ -1,22 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2013 Matthew Garrett - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - #include #include #include @@ -52,12 +38,10 @@ static ssize_t irst_store_wakeup_events(struct device *dev, acpi = to_acpi_device(dev); error = kstrtoul(buf, 0, &value); - if (error) return error; status = acpi_execute_simple_method(acpi->handle, "SFFS", value); - if (ACPI_FAILURE(status)) return -EINVAL; @@ -98,12 +82,10 @@ static ssize_t irst_store_wakeup_time(struct device *dev, acpi = to_acpi_device(dev); error = kstrtoul(buf, 0, &value); - if (error) return error; status = acpi_execute_simple_method(acpi->handle, "SFTV", value); - if (ACPI_FAILURE(status)) return -EINVAL; -- cgit From 2d0554e8ba555eeed84741099f1565437ae4b574 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:14 +0300 Subject: platform/x86: intel_scu_ipc: Sort headers alphabetically Sort headers alphabetically for better maintenance. No functional change. While here, remove unneeded linux/init.h inclusion. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_scu_ipc.c | 8 +++++--- drivers/platform/x86/intel_scu_ipcutil.c | 12 ++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 75c8fef7a482..3f4b24a33430 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -16,14 +16,16 @@ * IPC-1 Driver provides an API for power control unit registers (e.g. MSIC) * along with other APIs. */ + #include +#include #include #include -#include -#include -#include #include +#include +#include #include + #include #include diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c index aa454241489c..2a65cabe87f4 100644 --- a/drivers/platform/x86/intel_scu_ipcutil.c +++ b/drivers/platform/x86/intel_scu_ipcutil.c @@ -12,16 +12,16 @@ * This driver provides ioctl interfaces to call intel scu ipc driver api */ -#include -#include #include -#include -#include #include +#include +#include +#include #include -#include #include -#include +#include +#include + #include static int major; -- cgit From 9b748e0efee547f5a17c07c674cebe3ca148233f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel_scu_ipc: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_scu_ipc.c | 8 ++------ drivers/platform/x86/intel_scu_ipcutil.c | 12 ++++-------- 2 files changed, 6 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 3f4b24a33430..cdab916fbf92 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * intel_scu_ipc.c: Driver for the Intel SCU IPC mechanism + * Driver for the Intel SCU IPC mechanism * * (C) Copyright 2008-2010,2015 Intel Corporation * Author: Sreedhara DS (sreedhara.ds@intel.com) * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - * * SCU running in ARC processor communicates with other entity running in IA * core through IPC mechanism which in turn messaging between IA core ad SCU. * SCU has two IPC mechanism IPC-1 and IPC-2. IPC-1 is used between IA32 and diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c index 2a65cabe87f4..8afe6fa06d7b 100644 --- a/drivers/platform/x86/intel_scu_ipcutil.c +++ b/drivers/platform/x86/intel_scu_ipcutil.c @@ -1,15 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * intel_scu_ipc.c: Driver for the Intel SCU IPC mechanism + * Driver for the Intel SCU IPC mechanism * * (C) Copyright 2008-2010 Intel Corporation * Author: Sreedhara DS (sreedhara.ds@intel.com) * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - * - * This driver provides ioctl interfaces to call intel scu ipc driver api + * This driver provides IOCTL interfaces to call Intel SCU IPC driver API. */ #include @@ -26,7 +22,7 @@ static int major; -/* ioctl commnds */ +/* IOCTL commands */ #define INTE_SCU_IPC_REGISTER_READ 0 #define INTE_SCU_IPC_REGISTER_WRITE 1 #define INTE_SCU_IPC_REGISTER_UPDATE 2 -- cgit From 8046f0499c14819fb36176a589d21d225e69ae01 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:14 +0300 Subject: platform/x86: intel-smartconnect: Sort headers alphabetically Sort headers alphabetically for better maintenance. No functional change. While here, remove unneeded linux/init.h inclusion and move MODULE_DEVICE_TABLE() close to the table. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel-smartconnect.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel-smartconnect.c b/drivers/platform/x86/intel-smartconnect.c index bbe4c06c769f..274ac488afcb 100644 --- a/drivers/platform/x86/intel-smartconnect.c +++ b/drivers/platform/x86/intel-smartconnect.c @@ -17,9 +17,8 @@ */ -#include -#include #include +#include MODULE_LICENSE("GPL"); @@ -44,6 +43,7 @@ static const struct acpi_device_id smartconnect_ids[] = { {"INT33A0", 0}, {"", 0} }; +MODULE_DEVICE_TABLE(acpi, smartconnect_ids); static struct acpi_driver smartconnect_driver = { .owner = THIS_MODULE, @@ -56,5 +56,3 @@ static struct acpi_driver smartconnect_driver = { }; module_acpi_driver(smartconnect_driver); - -MODULE_DEVICE_TABLE(acpi, smartconnect_ids); -- cgit From 3e57f2648a09622b0ef3738aefb8d27b83dec60f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel-smartconnect: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. While here, throw away some extra blank lines. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel-smartconnect.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel-smartconnect.c b/drivers/platform/x86/intel-smartconnect.c index 274ac488afcb..64c2dc93472f 100644 --- a/drivers/platform/x86/intel-smartconnect.c +++ b/drivers/platform/x86/intel-smartconnect.c @@ -1,22 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2013 Matthew Garrett - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - #include #include -- cgit From 917f450aa3fa84c7682537b008b837806a77d99a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel_telemetry: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. While here, correct MODULE_LICENSE() string to be aligned with license text. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_telemetry_core.c | 12 ++---------- drivers/platform/x86/intel_telemetry_debugfs.c | 12 ++---------- drivers/platform/x86/intel_telemetry_pltdrv.c | 12 ++---------- 3 files changed, 6 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_telemetry_core.c b/drivers/platform/x86/intel_telemetry_core.c index f378621b5fe9..d4040bb222b4 100644 --- a/drivers/platform/x86/intel_telemetry_core.c +++ b/drivers/platform/x86/intel_telemetry_core.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel SoC Core Telemetry Driver * Copyright (C) 2015, Intel Corporation. * All Rights Reserved. * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * * Telemetry Framework provides platform related PM and performance statistics. * This file provides the core telemetry API implementation. */ @@ -460,4 +452,4 @@ module_exit(telemetry_module_exit); MODULE_AUTHOR("Souvik Kumar Chakravarty "); MODULE_DESCRIPTION("Intel SoC Telemetry Interface"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_telemetry_debugfs.c b/drivers/platform/x86/intel_telemetry_debugfs.c index ffd0474b0531..badd1682f9c1 100644 --- a/drivers/platform/x86/intel_telemetry_debugfs.c +++ b/drivers/platform/x86/intel_telemetry_debugfs.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel SOC Telemetry debugfs Driver: Currently supports APL * Copyright (c) 2015, Intel Corporation. * All Rights Reserved. * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * * This file provides the debugfs interfaces for telemetry. * /sys/kernel/debug/telemetry/pss_info: Shows Primary Control Sub-Sys Counters * /sys/kernel/debug/telemetry/ioss_info: Shows IO Sub-System Counters @@ -1037,4 +1029,4 @@ module_exit(telemetry_debugfs_exit); MODULE_AUTHOR("Souvik Kumar Chakravarty "); MODULE_DESCRIPTION("Intel SoC Telemetry debugfs Interface"); MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c index 2f889d6c270e..62531d2524d8 100644 --- a/drivers/platform/x86/intel_telemetry_pltdrv.c +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel SOC Telemetry Platform Driver: Currently supports APL * Copyright (c) 2015, Intel Corporation. * All Rights Reserved. * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * * This file provides the platform specific telemetry implementation for APL. * It used the PUNIT and PMC IPC interfaces for configuring the counters. * The accumulated results are fetched from SRAM. @@ -1242,4 +1234,4 @@ module_exit(telemetry_module_exit); MODULE_AUTHOR("Souvik Kumar Chakravarty "); MODULE_DESCRIPTION("Intel SoC Telemetry Platform Driver"); MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); -- cgit From 303211089c966db3933d4efc91bcaf10e235ad34 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:14 +0300 Subject: platform/x86: intel_turbo_max_3: Sort headers alphabetically Sort headers alphabetically for better maintenance. No functional change. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_turbo_max_3.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_turbo_max_3.c b/drivers/platform/x86/intel_turbo_max_3.c index a6d5aa0c3c47..0e1bdb883ceb 100644 --- a/drivers/platform/x86/intel_turbo_max_3.c +++ b/drivers/platform/x86/intel_turbo_max_3.c @@ -17,12 +17,13 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include +#include +#include #include +#include #include #include -#include -#include + #include #include -- cgit From de415deeb68b1a5c0a036fbbcb10389b83d47e02 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel_turbo_max_3: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_turbo_max_3.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_turbo_max_3.c b/drivers/platform/x86/intel_turbo_max_3.c index 0e1bdb883ceb..7b9cc841ab65 100644 --- a/drivers/platform/x86/intel_turbo_max_3.c +++ b/drivers/platform/x86/intel_turbo_max_3.c @@ -1,19 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Turbo Boost Max Technology 3.0 legacy (non HWP) enumeration driver * Copyright (c) 2017, Intel Corporation. * All rights reserved. * * Author: Srinivas Pandruvada - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -- cgit From a8694eebae878aadcee8d3306379c3d74924d89b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Sep 2018 18:27:40 +0300 Subject: platform/x86: intel-wmi-thunderbolt: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. While here, correct MODULE_LICENSE() string to be aligned with license text. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel-wmi-thunderbolt.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel-wmi-thunderbolt.c b/drivers/platform/x86/intel-wmi-thunderbolt.c index c2257bd06f18..b029a70b084a 100644 --- a/drivers/platform/x86/intel-wmi-thunderbolt.c +++ b/drivers/platform/x86/intel-wmi-thunderbolt.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * WMI Thunderbolt driver * * Copyright (C) 2017 Dell Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -95,4 +87,4 @@ module_wmi_driver(intel_wmi_thunderbolt_driver); MODULE_ALIAS("wmi:" INTEL_WMI_THUNDERBOLT_GUID); MODULE_AUTHOR("Mario Limonciello "); MODULE_DESCRIPTION("Intel WMI Thunderbolt force power driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); -- cgit From 7eccb5edba24cec9bb0a2b990ac66e755456303a Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 26 Sep 2018 11:10:58 -0500 Subject: platform/x86: intel-wmi-thunderbolt: Add dynamic debugging Some users have been reporting issues with thunderbolt being turned off before fully initialized. This is suspected to be caused by userspace turning off the Thunderbolt controller using intel-wmi-thunderbolt prematurely. Userspace has already made some mitigations for this situation: https://github.com/hughsie/fwupd/commit/ef6f1d76983c9b66 https://github.com/hughsie/fwupd/commit/c07ce5b4889a5384 To allow easier debugging of this situation add output that can be turned on with dynamic debugging to better root cause this problem. BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=199631 BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=201227 Suggested-by: Mika Westerberg Signed-off-by: Mario Limonciello Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg --- drivers/platform/x86/intel-wmi-thunderbolt.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel-wmi-thunderbolt.c b/drivers/platform/x86/intel-wmi-thunderbolt.c index b029a70b084a..9ded8e2af312 100644 --- a/drivers/platform/x86/intel-wmi-thunderbolt.c +++ b/drivers/platform/x86/intel-wmi-thunderbolt.c @@ -30,12 +30,16 @@ static ssize_t force_power_store(struct device *dev, input.length = sizeof(u8); input.pointer = &mode; mode = hex_to_bin(buf[0]); + dev_dbg(dev, "force_power: storing %#x\n", mode); if (mode == 0 || mode == 1) { status = wmi_evaluate_method(INTEL_WMI_THUNDERBOLT_GUID, 0, 1, &input, NULL); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { + dev_dbg(dev, "force_power: failed to evaluate ACPI method\n"); return -ENODEV; + } } else { + dev_dbg(dev, "force_power: unsupported mode\n"); return -EINVAL; } return count; -- cgit From 2991cc22966a712706c053ca3d3b03bd1ae384ae Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 2 Oct 2018 23:16:42 +0200 Subject: firmware: dcdbas: include linux/io.h memremap() is declared in linux/io.h, not in asm/io.h, so we should include that header to avoid build errors: drivers/platform/x86/dcdbas.c: In function 'dcdbas_check_wsmt': drivers/platform/x86/dcdbas.c:572:15: error: implicit declaration of function 'memremap'; did you mean 'ioremap'? [-Werror=implicit-function-declaration] eps_buffer = memremap(eps->smm_comm_buff_addr, remap_size, MEMREMAP_WB); ^~~~~~~~ ioremap drivers/platform/x86/dcdbas.c:572:61: error: 'MEMREMAP_WB' undeclared (first use in this function) eps_buffer = memremap(eps->smm_comm_buff_addr, remap_size, MEMREMAP_WB); ^~~~~~~~~~~ drivers/platform/x86/dcdbas.c:572:61: note: each undeclared identifier is reported only once for each function it appears in drivers/platform/x86/dcdbas.c: In function 'dcdbas_exit': drivers/platform/x86/dcdbas.c:748:3: error: implicit declaration of function 'memunmap'; did you mean 'vm_munmap'? [-Werror=implicit-function-declaration] Fixes: 12c956c4f32e ("firmware: dcdbas: Add support for WSMT ACPI table") Signed-off-by: Arnd Bergmann Reported-by: Randy Dunlap Signed-off-by: Andy Shevchenko --- drivers/platform/x86/dcdbas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/dcdbas.c b/drivers/platform/x86/dcdbas.c index ae28e48ff7dc..88bd7efafe14 100644 --- a/drivers/platform/x86/dcdbas.c +++ b/drivers/platform/x86/dcdbas.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,6 @@ #include #include #include -#include #include "dcdbas.h" -- cgit From 8289c4b6f2e53750de78bd38cecb6bce4d7a988c Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 25 Sep 2018 22:21:15 -0700 Subject: platform/x86: mlx-platform: Properly use mlxplat_mlxcpld_msn201x_items Clang warns that mlxplat_mlxcpld_msn201x_items is not going to be emitted in the final assembly because it's only used in ARRAY_SIZE right now, which is a compile time evaluation since the array's size is known. drivers/platform/x86/mlx-platform.c:555:32: warning: variable 'mlxplat_mlxcpld_msn201x_items' is not needed and will not be emitted [-Wunneeded-internal-declaration] static struct mlxreg_core_item mlxplat_mlxcpld_msn201x_items[] = { ^ 1 warning generated. It appears this was a copy and paste mistake from when this item was first added. Use the definition in mlxplat_mlxcpld_msn201x_data so that Clang no longer warns. Link: https://github.com/ClangBuiltLinux/linux/issues/141 Fixes: a49a41482f61 ("platform/x86: mlx-platform: Add support for new msn201x system type") Signed-off-by: Nathan Chancellor Acked-by: Vadim Pasternak Signed-off-by: Andy Shevchenko --- drivers/platform/x86/mlx-platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index d89936c93ba0..c2c3a1a19879 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -575,7 +575,7 @@ static struct mlxreg_core_item mlxplat_mlxcpld_msn201x_items[] = { static struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn201x_data = { - .items = mlxplat_mlxcpld_msn21xx_items, + .items = mlxplat_mlxcpld_msn201x_items, .counter = ARRAY_SIZE(mlxplat_mlxcpld_msn201x_items), .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET, .mask = MLXPLAT_CPLD_AGGR_MASK_DEF, -- cgit From cacad0b2e395b12a05487f57b2e716698443e6ae Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 4 Oct 2018 14:34:45 +0200 Subject: platform/x86: touchscreen_dmi: Add info for the Trekstor Primebook C11 convertible Add touchscreen info for the Trekstor Primebook C11 convertible. Cc: Alicia Hormann Suggested-by: Alicia Hormann Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/platform/x86/touchscreen_dmi.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index cb204f973491..c4f04a753120 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -366,6 +366,22 @@ static const struct ts_dmi_data teclast_x98plus2_data = { .properties = teclast_x98plus2_props, }; +static const struct property_entry trekstor_primebook_c11_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1970), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1530), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), + PROPERTY_ENTRY_STRING("firmware-name", + "gsl1680-trekstor-primebook-c11.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct ts_dmi_data trekstor_primebook_c11_data = { + .acpi_name = "MSSL1680:00", + .properties = trekstor_primebook_c11_props, +}; + static const struct property_entry trekstor_primebook_c13_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 2624), PROPERTY_ENTRY_U32("touchscreen-size-y", 1920), @@ -640,6 +656,14 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "X98 Plus II"), }, }, + { + /* Trekstor Primebook C11 */ + .driver_data = (void *)&trekstor_primebook_c11_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TREKSTOR"), + DMI_MATCH(DMI_PRODUCT_NAME, "Primebook C11"), + }, + }, { /* Trekstor Primebook C13 */ .driver_data = (void *)&trekstor_primebook_c13_data, -- cgit From 9fe44fc98ce45fe42004be9fd282775030c6b147 Mon Sep 17 00:00:00 2001 From: Jian-Hong Pan Date: Thu, 27 Sep 2018 16:50:09 +0800 Subject: platform/x86: asus-wmi: Simplify the keyboard brightness updating process The original asus-wmi queues a work which calls the ACPI/WMI methods to update the keyboard LED brightness. Similar drivers - acer-wmi, dell-wmi-led just call the ACPI/WMI methods directly without workqueues. This patch simplifies the keyboard brightness updating process which calls the kbd_led_update function directly without workqueue in asus-wmi. Signed-off-by: Jian-Hong Pan Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 2d6e272315a8..9441cce636e6 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -239,7 +239,6 @@ struct asus_wmi { int lightbar_led_wk; struct workqueue_struct *led_workqueue; struct work_struct tpd_led_work; - struct work_struct kbd_led_work; struct work_struct wlan_led_work; struct work_struct lightbar_led_work; @@ -456,12 +455,9 @@ static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) return read_tpd_led_state(asus); } -static void kbd_led_update(struct work_struct *work) +static void kbd_led_update(struct asus_wmi *asus) { int ctrl_param = 0; - struct asus_wmi *asus; - - asus = container_of(work, struct asus_wmi, kbd_led_work); /* * bits 0-2: level @@ -516,7 +512,7 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) value = 0; asus->kbd_led_wk = value; - queue_work(asus->led_workqueue, &asus->kbd_led_work); + kbd_led_update(asus); } static void kbd_led_set(struct led_classdev *led_cdev, @@ -671,8 +667,6 @@ static int asus_wmi_led_init(struct asus_wmi *asus) led_val = kbd_led_read(asus, NULL, NULL); if (led_val >= 0) { - INIT_WORK(&asus->kbd_led_work, kbd_led_update); - asus->kbd_led_wk = led_val; asus->kbd_led.name = "asus::kbd_backlight"; asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; @@ -2310,7 +2304,7 @@ static int asus_hotk_resume(struct device *device) struct asus_wmi *asus = dev_get_drvdata(device); if (!IS_ERR_OR_NULL(asus->kbd_led.dev)) - queue_work(asus->led_workqueue, &asus->kbd_led_work); + kbd_led_update(asus); return 0; } @@ -2346,7 +2340,7 @@ static int asus_hotk_restore(struct device *device) rfkill_set_sw_state(asus->uwb.rfkill, bl); } if (!IS_ERR_OR_NULL(asus->kbd_led.dev)) - queue_work(asus->led_workqueue, &asus->kbd_led_work); + kbd_led_update(asus); return 0; } -- cgit From dbf0c5a6b1f8e7bec5e17baa60a1e04c28d90f9b Mon Sep 17 00:00:00 2001 From: Matan Ziv-Av Date: Fri, 28 Sep 2018 17:34:06 +0300 Subject: platform/x86: Add LG Gram laptop special features driver A driver for LG Gram laptop supporting features not available through the standard interfaces: - Support for the 5 Fn keys that generate ACPI or WMI events. - Two software controlled LEDs: keyboard backlight (also controlled by hardware) and touchpad LED. - Extra features: reader mode, Fn lock, cooling mode, USB charge mode, and maximal battery charging level. Signed-off-by: Matan Ziv-Av Signed-off-by: Andy Shevchenko --- drivers/platform/x86/Kconfig | 14 + drivers/platform/x86/Makefile | 1 + drivers/platform/x86/lg-laptop.c | 700 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 715 insertions(+) create mode 100644 drivers/platform/x86/lg-laptop.c (limited to 'drivers') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 4283239115ca..1991608549d5 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -367,6 +367,20 @@ config HP_WMI To compile this driver as a module, choose M here: the module will be called hp-wmi. +config LG_LAPTOP + tristate "LG Laptop Extras" + depends on ACPI + depends on ACPI_WMI + depends on INPUT + select INPUT_SPARSEKMAP + select LEDS_CLASS + help + This driver adds support for hotkeys as well as control of keyboard + backlight, battery maximum charge level and various other ACPI + features. + + If you have an LG Gram laptop, say Y or M here. + config MSI_LAPTOP tristate "MSI Laptop Extras" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 4e2712c9c0b0..395c8ff12bd1 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o obj-$(CONFIG_ASUS_WIRELESS) += asus-wireless.o obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o +obj-$(CONFIG_LG_LAPTOP) += lg-laptop.o obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c new file mode 100644 index 000000000000..c0bb1f864dfe --- /dev/null +++ b/drivers/platform/x86/lg-laptop.c @@ -0,0 +1,700 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * lg-laptop.c - LG Gram ACPI features and hotkeys Driver + * + * Copyright (C) 2018 Matan Ziv-Av + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#define LED_DEVICE(_name, max) struct led_classdev _name = { \ + .name = __stringify(_name), \ + .max_brightness = max, \ + .brightness_set = _name##_set, \ + .brightness_get = _name##_get, \ +} + +MODULE_AUTHOR("Matan Ziv-Av"); +MODULE_DESCRIPTION("LG WMI Hotkey Driver"); +MODULE_LICENSE("GPL"); + +#define WMI_EVENT_GUID0 "E4FB94F9-7F2B-4173-AD1A-CD1D95086248" +#define WMI_EVENT_GUID1 "023B133E-49D1-4E10-B313-698220140DC2" +#define WMI_EVENT_GUID2 "37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6" +#define WMI_EVENT_GUID3 "911BAD44-7DF8-4FBB-9319-BABA1C4B293B" +#define WMI_METHOD_WMAB "C3A72B38-D3EF-42D3-8CBB-D5A57049F66D" +#define WMI_METHOD_WMBB "2B4F501A-BD3C-4394-8DCF-00A7D2BC8210" +#define WMI_EVENT_GUID WMI_EVENT_GUID0 + +#define WMAB_METHOD "\\XINI.WMAB" +#define WMBB_METHOD "\\XINI.WMBB" +#define SB_GGOV_METHOD "\\_SB.GGOV" +#define GOV_TLED 0x2020008 +#define WM_GET 1 +#define WM_SET 2 +#define WM_KEY_LIGHT 0x400 +#define WM_TLED 0x404 +#define WM_FN_LOCK 0x407 +#define WM_BATT_LIMIT 0x61 +#define WM_READER_MODE 0xBF +#define WM_FAN_MODE 0x33 +#define WMBB_USB_CHARGE 0x10B +#define WMBB_BATT_LIMIT 0x10C + +#define PLATFORM_NAME "lg-laptop" + +MODULE_ALIAS("wmi:" WMI_EVENT_GUID0); +MODULE_ALIAS("wmi:" WMI_EVENT_GUID1); +MODULE_ALIAS("wmi:" WMI_EVENT_GUID2); +MODULE_ALIAS("wmi:" WMI_EVENT_GUID3); +MODULE_ALIAS("wmi:" WMI_METHOD_WMAB); +MODULE_ALIAS("wmi:" WMI_METHOD_WMBB); +MODULE_ALIAS("acpi*:LGEX0815:*"); + +static struct platform_device *pf_device; +static struct input_dev *wmi_input_dev; + +static u32 inited; +#define INIT_INPUT_WMI_0 0x01 +#define INIT_INPUT_WMI_2 0x02 +#define INIT_INPUT_ACPI 0x04 +#define INIT_TPAD_LED 0x08 +#define INIT_KBD_LED 0x10 +#define INIT_SPARSE_KEYMAP 0x80 + +static const struct key_entry wmi_keymap[] = { + {KE_KEY, 0x70, {KEY_F15} }, /* LG control panel (F1) */ + {KE_KEY, 0x74, {KEY_F13} }, /* Touchpad toggle (F5) */ + {KE_KEY, 0xf020000, {KEY_F14} }, /* Read mode (F9) */ + {KE_KEY, 0x10000000, {KEY_F16} },/* Keyboard backlight (F8) - pressing + * this key both sends an event and + * changes backlight level. + */ + {KE_KEY, 0x80, {KEY_RFKILL} }, + {KE_END, 0} +}; + +static int ggov(u32 arg0) +{ + union acpi_object args[1]; + union acpi_object *r; + acpi_status status; + acpi_handle handle; + struct acpi_object_list arg; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + int res; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = arg0; + + status = acpi_get_handle(NULL, (acpi_string) SB_GGOV_METHOD, &handle); + if (ACPI_FAILURE(status)) { + pr_err("Cannot get handle"); + return -ENODEV; + } + + arg.count = 1; + arg.pointer = args; + + status = acpi_evaluate_object(handle, NULL, &arg, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_err(handle, "GGOV: call failed.\n"); + return -EINVAL; + } + + r = buffer.pointer; + if (r->type != ACPI_TYPE_INTEGER) { + kfree(r); + return -EINVAL; + } + + res = r->integer.value; + kfree(r); + + return res; +} + +static union acpi_object *lg_wmab(u32 method, u32 arg1, u32 arg2) +{ + union acpi_object args[3]; + acpi_status status; + acpi_handle handle; + struct acpi_object_list arg; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = method; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = arg1; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = arg2; + + status = acpi_get_handle(NULL, (acpi_string) WMAB_METHOD, &handle); + if (ACPI_FAILURE(status)) { + pr_err("Cannot get handle"); + return NULL; + } + + arg.count = 3; + arg.pointer = args; + + status = acpi_evaluate_object(handle, NULL, &arg, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_err(handle, "WMAB: call failed.\n"); + return NULL; + } + + return buffer.pointer; +} + +static union acpi_object *lg_wmbb(u32 method_id, u32 arg1, u32 arg2) +{ + union acpi_object args[3]; + acpi_status status; + acpi_handle handle; + struct acpi_object_list arg; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + u8 buf[32]; + + *(u32 *)buf = method_id; + *(u32 *)(buf + 4) = arg1; + *(u32 *)(buf + 16) = arg2; + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = 0; /* ignored */ + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 1; /* Must be 1 or 2. Does not matter which */ + args[2].type = ACPI_TYPE_BUFFER; + args[2].buffer.length = 32; + args[2].buffer.pointer = buf; + + status = acpi_get_handle(NULL, (acpi_string)WMBB_METHOD, &handle); + if (ACPI_FAILURE(status)) { + pr_err("Cannot get handle"); + return NULL; + } + + arg.count = 3; + arg.pointer = args; + + status = acpi_evaluate_object(handle, NULL, &arg, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_err(handle, "WMAB: call failed.\n"); + return NULL; + } + + return (union acpi_object *)buffer.pointer; +} + +static void wmi_notify(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + long data = (long)context; + + pr_debug("event guid %li\n", data); + status = wmi_get_event_data(value, &response); + if (ACPI_FAILURE(status)) { + pr_err("Bad event status 0x%x\n", status); + return; + } + + obj = (union acpi_object *)response.pointer; + if (!obj) + return; + + if (obj->type == ACPI_TYPE_INTEGER) { + int eventcode = obj->integer.value; + struct key_entry *key; + + key = + sparse_keymap_entry_from_scancode(wmi_input_dev, eventcode); + if (key && key->type == KE_KEY) + sparse_keymap_report_entry(wmi_input_dev, key, 1, true); + } + + pr_debug("Type: %i Eventcode: 0x%llx\n", obj->type, + obj->integer.value); + kfree(response.pointer); +} + +static void wmi_input_setup(void) +{ + acpi_status status; + + wmi_input_dev = input_allocate_device(); + if (wmi_input_dev) { + wmi_input_dev->name = "LG WMI hotkeys"; + wmi_input_dev->phys = "wmi/input0"; + wmi_input_dev->id.bustype = BUS_HOST; + + if (sparse_keymap_setup(wmi_input_dev, wmi_keymap, NULL) || + input_register_device(wmi_input_dev)) { + pr_info("Cannot initialize input device"); + input_free_device(wmi_input_dev); + return; + } + + inited |= INIT_SPARSE_KEYMAP; + status = wmi_install_notify_handler(WMI_EVENT_GUID0, wmi_notify, + (void *)0); + if (ACPI_SUCCESS(status)) + inited |= INIT_INPUT_WMI_0; + + status = wmi_install_notify_handler(WMI_EVENT_GUID2, wmi_notify, + (void *)2); + if (ACPI_SUCCESS(status)) + inited |= INIT_INPUT_WMI_2; + } else { + pr_info("Cannot allocate input device"); + } +} + +static void acpi_notify(struct acpi_device *device, u32 event) +{ + struct key_entry *key; + + acpi_handle_debug(device->handle, "notify: %d\n", event); + if (inited & INIT_SPARSE_KEYMAP) { + key = sparse_keymap_entry_from_scancode(wmi_input_dev, 0x80); + if (key && key->type == KE_KEY) + sparse_keymap_report_entry(wmi_input_dev, key, 1, true); + } +} + +static ssize_t fan_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + bool value; + union acpi_object *r; + u32 m; + int ret; + + ret = kstrtobool(buffer, &value); + if (ret) + return ret; + + r = lg_wmab(WM_FAN_MODE, WM_GET, 0); + if (!r) + return -EIO; + + if (r->type != ACPI_TYPE_INTEGER) { + kfree(r); + return -EIO; + } + + m = r->integer.value; + kfree(r); + r = lg_wmab(WM_FAN_MODE, WM_SET, (m & 0xffffff0f) | (value << 4)); + kfree(r); + r = lg_wmab(WM_FAN_MODE, WM_SET, (m & 0xfffffff0) | value); + kfree(r); + + return count; +} + +static ssize_t fan_mode_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int status; + union acpi_object *r; + + r = lg_wmab(WM_FAN_MODE, WM_GET, 0); + if (!r) + return -EIO; + + if (r->type != ACPI_TYPE_INTEGER) { + kfree(r); + return -EIO; + } + + status = r->integer.value & 0x01; + kfree(r); + + return snprintf(buffer, PAGE_SIZE, "%d\n", status); +} + +static ssize_t usb_charge_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + bool value; + union acpi_object *r; + int ret; + + ret = kstrtobool(buffer, &value); + if (ret) + return ret; + + r = lg_wmbb(WMBB_USB_CHARGE, WM_SET, value); + if (!r) + return -EIO; + + kfree(r); + return count; +} + +static ssize_t usb_charge_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int status; + union acpi_object *r; + + r = lg_wmbb(WMBB_USB_CHARGE, WM_GET, 0); + if (!r) + return -EIO; + + if (r->type != ACPI_TYPE_BUFFER) { + kfree(r); + return -EIO; + } + + status = !!r->buffer.pointer[0x10]; + + kfree(r); + + return snprintf(buffer, PAGE_SIZE, "%d\n", status); +} + +static ssize_t reader_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + bool value; + union acpi_object *r; + int ret; + + ret = kstrtobool(buffer, &value); + if (ret) + return ret; + + r = lg_wmab(WM_READER_MODE, WM_SET, value); + if (!r) + return -EIO; + + kfree(r); + return count; +} + +static ssize_t reader_mode_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int status; + union acpi_object *r; + + r = lg_wmab(WM_READER_MODE, WM_GET, 0); + if (!r) + return -EIO; + + if (r->type != ACPI_TYPE_INTEGER) { + kfree(r); + return -EIO; + } + + status = !!r->integer.value; + + kfree(r); + + return snprintf(buffer, PAGE_SIZE, "%d\n", status); +} + +static ssize_t fn_lock_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + bool value; + union acpi_object *r; + int ret; + + ret = kstrtobool(buffer, &value); + if (ret) + return ret; + + r = lg_wmab(WM_FN_LOCK, WM_SET, value); + if (!r) + return -EIO; + + kfree(r); + return count; +} + +static ssize_t fn_lock_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int status; + union acpi_object *r; + + r = lg_wmab(WM_FN_LOCK, WM_GET, 0); + if (!r) + return -EIO; + + if (r->type != ACPI_TYPE_BUFFER) { + kfree(r); + return -EIO; + } + + status = !!r->buffer.pointer[0]; + kfree(r); + + return snprintf(buffer, PAGE_SIZE, "%d\n", status); +} + +static ssize_t battery_care_limit_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned long value; + int ret; + + ret = kstrtoul(buffer, 10, &value); + if (ret) + return ret; + + if (value == 100 || value == 80) { + union acpi_object *r; + + r = lg_wmab(WM_BATT_LIMIT, WM_SET, value); + if (!r) + return -EIO; + + kfree(r); + return count; + } + + return -EINVAL; +} + +static ssize_t battery_care_limit_show(struct device *dev, + struct device_attribute *attr, + char *buffer) +{ + unsigned int status; + union acpi_object *r; + + r = lg_wmab(WM_BATT_LIMIT, WM_GET, 0); + if (!r) + return -EIO; + + if (r->type != ACPI_TYPE_INTEGER) { + kfree(r); + return -EIO; + } + + status = r->integer.value; + kfree(r); + if (status != 80 && status != 100) + status = 0; + + return snprintf(buffer, PAGE_SIZE, "%d\n", status); +} + +static DEVICE_ATTR_RW(fan_mode); +static DEVICE_ATTR_RW(usb_charge); +static DEVICE_ATTR_RW(reader_mode); +static DEVICE_ATTR_RW(fn_lock); +static DEVICE_ATTR_RW(battery_care_limit); + +static struct attribute *dev_attributes[] = { + &dev_attr_fan_mode.attr, + &dev_attr_usb_charge.attr, + &dev_attr_reader_mode.attr, + &dev_attr_fn_lock.attr, + &dev_attr_battery_care_limit.attr, + NULL +}; + +static const struct attribute_group dev_attribute_group = { + .attrs = dev_attributes, +}; + +static void tpad_led_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + union acpi_object *r; + + r = lg_wmab(WM_TLED, WM_SET, brightness > LED_OFF); + kfree(r); +} + +static enum led_brightness tpad_led_get(struct led_classdev *cdev) +{ + return ggov(GOV_TLED) > 0 ? LED_ON : LED_OFF; +} + +static LED_DEVICE(tpad_led, 1); + +static void kbd_backlight_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + u32 val; + union acpi_object *r; + + val = 0x22; + if (brightness <= LED_OFF) + val = 0; + if (brightness >= LED_FULL) + val = 0x24; + r = lg_wmab(WM_KEY_LIGHT, WM_SET, val); + kfree(r); +} + +static enum led_brightness kbd_backlight_get(struct led_classdev *cdev) +{ + union acpi_object *r; + int val; + + r = lg_wmab(WM_KEY_LIGHT, WM_GET, 0); + + if (!r) + return LED_OFF; + + if (r->type != ACPI_TYPE_BUFFER || r->buffer.pointer[1] != 0x05) { + kfree(r); + return LED_OFF; + } + + switch (r->buffer.pointer[0] & 0x27) { + case 0x24: + val = LED_FULL; + break; + case 0x22: + val = LED_HALF; + break; + default: + val = LED_OFF; + } + + kfree(r); + + return val; +} + +static LED_DEVICE(kbd_backlight, 255); + +static void wmi_input_destroy(void) +{ + if (inited & INIT_INPUT_WMI_2) + wmi_remove_notify_handler(WMI_EVENT_GUID2); + + if (inited & INIT_INPUT_WMI_0) + wmi_remove_notify_handler(WMI_EVENT_GUID0); + + if (inited & INIT_SPARSE_KEYMAP) + input_unregister_device(wmi_input_dev); + + inited &= ~(INIT_INPUT_WMI_0 | INIT_INPUT_WMI_2 | INIT_SPARSE_KEYMAP); +} + +static struct platform_driver pf_driver = { + .driver = { + .name = PLATFORM_NAME, + } +}; + +static int acpi_add(struct acpi_device *device) +{ + int ret; + + if (pf_device) + return 0; + + ret = platform_driver_register(&pf_driver); + if (ret) + return ret; + + pf_device = platform_device_register_simple(PLATFORM_NAME, + PLATFORM_DEVID_NONE, + NULL, 0); + if (IS_ERR(pf_device)) { + ret = PTR_ERR(pf_device); + pf_device = NULL; + pr_err("unable to register platform device\n"); + goto out_platform_registered; + } + + ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group); + if (ret) + goto out_platform_device; + + if (!led_classdev_register(&pf_device->dev, &kbd_backlight)) + inited |= INIT_KBD_LED; + + if (!led_classdev_register(&pf_device->dev, &tpad_led)) + inited |= INIT_TPAD_LED; + + wmi_input_setup(); + + return 0; + +out_platform_device: + platform_device_unregister(pf_device); +out_platform_registered: + platform_driver_unregister(&pf_driver); + return ret; +} + +static int acpi_remove(struct acpi_device *device) +{ + sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group); + if (inited & INIT_KBD_LED) + led_classdev_unregister(&kbd_backlight); + + if (inited & INIT_TPAD_LED) + led_classdev_unregister(&tpad_led); + + wmi_input_destroy(); + platform_device_unregister(pf_device); + pf_device = NULL; + platform_driver_unregister(&pf_driver); + + return 0; +} + +static const struct acpi_device_id device_ids[] = { + {"LGEX0815", 0}, + {"", 0} +}; +MODULE_DEVICE_TABLE(acpi, device_ids); + +static struct acpi_driver acpi_driver = { + .name = "LG Gram Laptop Support", + .class = "lg-laptop", + .ids = device_ids, + .ops = { + .add = acpi_add, + .remove = acpi_remove, + .notify = acpi_notify, + }, + .owner = THIS_MODULE, +}; + +static int __init acpi_init(void) +{ + int result; + + result = acpi_bus_register_driver(&acpi_driver); + if (result < 0) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error registering driver\n")); + return -ENODEV; + } + + return 0; +} + +static void __exit acpi_exit(void) +{ + acpi_bus_unregister_driver(&acpi_driver); +} + +module_init(acpi_init); +module_exit(acpi_exit); -- cgit From 8d98b1ef368feeb7720b8b9b6f3bd93f2ad892bc Mon Sep 17 00:00:00 2001 From: Rajneesh Bhardwaj Date: Sat, 6 Oct 2018 12:21:13 +0530 Subject: platform/x86: intel_telemetry: report debugfs failure On some Goldmont based systems such as ASRock J3455M the BIOS may not enable the IPC1 device that provides access to the PMC and PUNIT. In such scenarios, the IOSS and PSS resources from the platform device can not be obtained and result in a invalid telemetry_plt_config which is an internal data structure that holds platform config and is maintained by the telemetry platform driver. This is also applicable to the platforms where the BIOS supports IPC1 device under debug configurations but IPC1 is disabled by user or the policy. This change allows user to know the reason for not seeing entries under /sys/kernel/debug/telemetry/* when there is no apparent failure at boot. Cc: Matt Turner Cc: Len Brown Cc: Souvik Kumar Chakravarty Cc: Kuppuswamy Sathyanarayanan Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=198779 Acked-by: Matt Turner Signed-off-by: Rajneesh Bhardwaj Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_telemetry_debugfs.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_telemetry_debugfs.c b/drivers/platform/x86/intel_telemetry_debugfs.c index badd1682f9c1..c279490f9488 100644 --- a/drivers/platform/x86/intel_telemetry_debugfs.c +++ b/drivers/platform/x86/intel_telemetry_debugfs.c @@ -943,12 +943,16 @@ static int __init telemetry_debugfs_init(void) debugfs_conf = (struct telemetry_debugfs_conf *)id->driver_data; err = telemetry_pltconfig_valid(); - if (err < 0) + if (err < 0) { + pr_info("Invalid pltconfig, ensure IPC1 device is enabled in BIOS\n"); return -ENODEV; + } err = telemetry_debugfs_check_evts(); - if (err < 0) + if (err < 0) { + pr_info("telemetry_debugfs_check_evts failed\n"); return -EINVAL; + } register_pm_notifier(&pm_notifier); -- cgit From 1a7938a632ce476b01a2dd7676246ca319eb295a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 31 Aug 2018 11:34:31 +0300 Subject: platform/x86: intel_telemetry: Get rid of custom macro Replace custom grown macro with generic INTEL_CPU_FAM6() one. No functional change intended. Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_telemetry_debugfs.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_telemetry_debugfs.c b/drivers/platform/x86/intel_telemetry_debugfs.c index c279490f9488..30988e31c713 100644 --- a/drivers/platform/x86/intel_telemetry_debugfs.c +++ b/drivers/platform/x86/intel_telemetry_debugfs.c @@ -64,9 +64,6 @@ #define TELEM_IOSS_DX_D0IX_EVTS 25 #define TELEM_IOSS_PG_EVTS 30 -#define TELEM_DEBUGFS_CPU(model, data) \ - { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&data} - #define TELEM_CHECK_AND_PARSE_EVTS(EVTID, EVTNUM, BUF, EVTLOG, EVTDAT, MASK) { \ if (evtlog[index].telem_evtid == (EVTID)) { \ for (idx = 0; idx < (EVTNUM); idx++) \ @@ -311,8 +308,8 @@ static struct telemetry_debugfs_conf telem_apl_debugfs_conf = { }; static const struct x86_cpu_id telemetry_debugfs_cpu_ids[] = { - TELEM_DEBUGFS_CPU(INTEL_FAM6_ATOM_GOLDMONT, telem_apl_debugfs_conf), - TELEM_DEBUGFS_CPU(INTEL_FAM6_ATOM_GEMINI_LAKE, telem_apl_debugfs_conf), + INTEL_CPU_FAM6(ATOM_GOLDMONT, telem_apl_debugfs_conf), + INTEL_CPU_FAM6(ATOM_GEMINI_LAKE, telem_apl_debugfs_conf), {} }; -- cgit From 30394a8e8c5601eb6936402c0f94f619d002fa97 Mon Sep 17 00:00:00 2001 From: Marian Cepok Date: Mon, 8 Oct 2018 09:25:05 +0200 Subject: platform/x86: touchscreen_dmi: Add info for the Trekstor Primetab T13B tablet Add touchscreen info for the Trekstor Primetab T13B tablet. Reviewed-by: Hans de Goede Signed-off-by: Marian Cepok Signed-off-by: Andy Shevchenko --- drivers/platform/x86/touchscreen_dmi.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index c4f04a753120..f037ad8d084e 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -397,6 +397,22 @@ static const struct ts_dmi_data trekstor_primebook_c13_data = { .properties = trekstor_primebook_c13_props, }; +static const struct property_entry trekstor_primetab_t13b_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 2500), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1900), + PROPERTY_ENTRY_STRING("firmware-name", + "gsl1680-trekstor-primetab-t13b.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), + { } +}; + +static const struct ts_dmi_data trekstor_primetab_t13b_data = { + .acpi_name = "MSSL1680:00", + .properties = trekstor_primetab_t13b_props, +}; + static const struct property_entry trekstor_surftab_twin_10_1_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1900), PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), @@ -672,6 +688,14 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Primebook C13"), }, }, + { + /* Trekstor Primetab T13B */ + .driver_data = (void *)&trekstor_primetab_t13b_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TREKSTOR"), + DMI_MATCH(DMI_PRODUCT_NAME, "Primetab T13B"), + }, + }, { /* TrekStor SurfTab twin 10.1 ST10432-8 */ .driver_data = (void *)&trekstor_surftab_twin_10_1_data, -- cgit From 01b6b7e6e01e18b2b54bba478fd16b787f3635dd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 10 Oct 2018 15:16:40 +0200 Subject: platform/x86: touchscreen_dmi: Add info for the Onda V80 Plus v3 tablet Add touchscreen platform data for the Onda V80 Plus v3 tablet. Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/platform/x86/touchscreen_dmi.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index f037ad8d084e..462b5e6fcb9e 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -237,6 +237,24 @@ static const struct ts_dmi_data onda_obook_20_plus_data = { .properties = onda_obook_20_plus_props, }; +static const struct property_entry onda_v80_plus_v3_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 22), + PROPERTY_ENTRY_U32("touchscreen-min-y", 15), + PROPERTY_ENTRY_U32("touchscreen-size-x", 1698), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_STRING("firmware-name", + "gsl3676-onda-v80-plus-v3.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct ts_dmi_data onda_v80_plus_v3_data = { + .acpi_name = "MSSL1680:00", + .properties = onda_v80_plus_v3_props, +}; + static const struct property_entry onda_v820w_32g_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1665), PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), @@ -587,6 +605,14 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "OBOOK 20 PLUS"), }, }, + { + /* ONDA V80 plus v3 (P80PSBG9V3A01501) */ + .driver_data = (void *)&onda_v80_plus_v3_data, + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONDA"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "V80 PLUS") + }, + }, { /* ONDA V820w DualOS */ .driver_data = (void *)&onda_v820w_32g_data, -- cgit From 4104916d8cad141faa6c24003540e0faeed859fd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Oct 2018 12:21:00 +0200 Subject: platform/x86: touchscreen_dmi: Add min-x and min-y settings for various models Add min-x and min-y settings now that we've support for this and for some models also update the width/height settings with slighly more accurate values. This fixes touches along the edges registering at the wrong coordinates. While at it also set max-fingers to 10 in a couple of cases where the touchpad can handle 10 fingers (rather then the default 5) and that was missing. Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/platform/x86/touchscreen_dmi.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 462b5e6fcb9e..5f2d7ea912b5 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -42,10 +42,13 @@ static const struct ts_dmi_data chuwi_hi8_data = { }; static const struct property_entry chuwi_hi8_pro_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 6), + PROPERTY_ENTRY_U32("touchscreen-min-y", 3), PROPERTY_ENTRY_U32("touchscreen-size-x", 1728), PROPERTY_ENTRY_U32("touchscreen-size-y", 1148), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-chuwi-hi8-pro.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -56,6 +59,8 @@ static const struct ts_dmi_data chuwi_hi8_pro_data = { }; static const struct property_entry chuwi_vi8_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 4), + PROPERTY_ENTRY_U32("touchscreen-min-y", 6), PROPERTY_ENTRY_U32("touchscreen-size-x", 1724), PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), @@ -88,9 +93,9 @@ static const struct ts_dmi_data chuwi_vi10_data = { static const struct property_entry connect_tablet9_props[] = { PROPERTY_ENTRY_U32("touchscreen-min-x", 9), - PROPERTY_ENTRY_U32("touchscreen-min-y", 8), + PROPERTY_ENTRY_U32("touchscreen-min-y", 10), PROPERTY_ENTRY_U32("touchscreen-size-x", 1664), - PROPERTY_ENTRY_U32("touchscreen-size-y", 878), + PROPERTY_ENTRY_U32("touchscreen-size-y", 880), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-connect-tablet9.fw"), @@ -104,8 +109,10 @@ static const struct ts_dmi_data connect_tablet9_data = { }; static const struct property_entry cube_iwork8_air_props[] = { - PROPERTY_ENTRY_U32("touchscreen-size-x", 1660), - PROPERTY_ENTRY_U32("touchscreen-size-y", 900), + PROPERTY_ENTRY_U32("touchscreen-min-x", 1), + PROPERTY_ENTRY_U32("touchscreen-min-y", 3), + PROPERTY_ENTRY_U32("touchscreen-size-x", 1664), + PROPERTY_ENTRY_U32("touchscreen-size-y", 896), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-cube-iwork8-air.fw"), PROPERTY_ENTRY_U32("silead,max-fingers", 10), @@ -179,11 +186,14 @@ static const struct ts_dmi_data gp_electronic_t701_data = { }; static const struct property_entry itworks_tw891_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 1), + PROPERTY_ENTRY_U32("touchscreen-min-y", 5), PROPERTY_ENTRY_U32("touchscreen-size-x", 1600), - PROPERTY_ENTRY_U32("touchscreen-size-y", 890), + PROPERTY_ENTRY_U32("touchscreen-size-y", 896), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-itworks-tw891.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -207,8 +217,10 @@ static const struct ts_dmi_data jumper_ezpad_6_pro_data = { }; static const struct property_entry jumper_ezpad_mini3_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 23), + PROPERTY_ENTRY_U32("touchscreen-min-y", 16), PROPERTY_ENTRY_U32("touchscreen-size-x", 1700), - PROPERTY_ENTRY_U32("touchscreen-size-y", 1150), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1138), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-jumper-ezpad-mini3.fw"), PROPERTY_ENTRY_U32("silead,max-fingers", 10), @@ -340,11 +352,14 @@ static const struct ts_dmi_data pov_mobii_wintab_p800w_v20_data = { }; static const struct property_entry pov_mobii_wintab_p800w_v21_props[] = { - PROPERTY_ENTRY_U32("touchscreen-size-x", 1800), - PROPERTY_ENTRY_U32("touchscreen-size-y", 1150), + PROPERTY_ENTRY_U32("touchscreen-min-x", 1), + PROPERTY_ENTRY_U32("touchscreen-min-y", 8), + PROPERTY_ENTRY_U32("touchscreen-size-x", 1794), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1148), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-pov-mobii-wintab-p800w.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -447,6 +462,8 @@ static const struct ts_dmi_data trekstor_surftab_twin_10_1_data = { }; static const struct property_entry trekstor_surftab_wintron70_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 12), + PROPERTY_ENTRY_U32("touchscreen-min-y", 8), PROPERTY_ENTRY_U32("touchscreen-size-x", 884), PROPERTY_ENTRY_U32("touchscreen-size-y", 632), PROPERTY_ENTRY_STRING("firmware-name", -- cgit From 49ad712afa88c502831d37f7089d98eac441fb80 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 14 Oct 2018 19:54:27 +0200 Subject: platform/x86: Add Intel AtomISP2 dummy / power-management driver The Image Signal Processor found on Cherry Trail devices is brought up in D0 state on devices which have camera sensors attached to it. The ISP will not enter D3 state again without some massaging of its registers beforehand and the ISP not being in D3 state blocks the SoC from entering S0ix modes. There was a driver for the ISP in drivers/staging but that got removed again because it never worked. It does not seem likely that a real driver for the ISP will be added to the mainline kernel anytime soon. This commit adds a dummy driver which contains the necessary magic from the staging driver to powerdown the ISP, so that Cherry Trail devices where the ISP is used will properly use S0ix modes when suspended. Together with other recent S0ix related fixes this allows S0ix modes to be entered on e.g. a Chuwi Hi8 Pro and a HP x2 210. BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=196915 Signed-off-by: Hans de Goede Reviewed-by: Alan Cox Signed-off-by: Andy Shevchenko --- drivers/platform/x86/Kconfig | 12 ++++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/intel_atomisp2_pm.c | 119 +++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 drivers/platform/x86/intel_atomisp2_pm.c (limited to 'drivers') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 1991608549d5..27a27fca3be1 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1274,6 +1274,18 @@ config I2C_MULTI_INSTANTIATE To compile this driver as a module, choose M here: the module will be called i2c-multi-instantiate. +config INTEL_ATOMISP2_PM + tristate "Intel AtomISP2 dummy / power-management driver" + depends on PCI && IOSF_MBI && PM + help + Power-management driver for Intel's Image Signal Processor found on + Bay and Cherry Trail devices. This dummy driver's sole purpose is to + turn the ISP off (put it in D3) to save power and to allow entering + of S0ix modes. + + To compile this driver as a module, choose M here: the module + will be called intel_atomisp2_pm. + endif # X86_PLATFORM_DEVICES config PMC_ATOM diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 395c8ff12bd1..39ae94135406 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -95,3 +95,4 @@ obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o +obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o diff --git a/drivers/platform/x86/intel_atomisp2_pm.c b/drivers/platform/x86/intel_atomisp2_pm.c new file mode 100644 index 000000000000..9371603a0ac9 --- /dev/null +++ b/drivers/platform/x86/intel_atomisp2_pm.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Dummy driver for Intel's Image Signal Processor found on Bay and Cherry + * Trail devices. The sole purpose of this driver is to allow the ISP to + * be put in D3. + * + * Copyright (C) 2018 Hans de Goede + * + * Based on various non upstream patches for ISP support: + * Copyright (C) 2010-2017 Intel Corporation. All rights reserved. + * Copyright (c) 2010 Silicon Hive www.siliconhive.com. + */ + +#include +#include +#include +#include +#include +#include + +/* PCI configuration regs */ +#define PCI_INTERRUPT_CTRL 0x9c + +#define PCI_CSI_CONTROL 0xe8 +#define PCI_CSI_CONTROL_PORTS_OFF_MASK 0x7 + +/* IOSF BT_MBI_UNIT_PMC regs */ +#define ISPSSPM0 0x39 +#define ISPSSPM0_ISPSSC_OFFSET 0 +#define ISPSSPM0_ISPSSC_MASK 0x00000003 +#define ISPSSPM0_ISPSSS_OFFSET 24 +#define ISPSSPM0_ISPSSS_MASK 0x03000000 +#define ISPSSPM0_IUNIT_POWER_ON 0x0 +#define ISPSSPM0_IUNIT_POWER_OFF 0x3 + +static int isp_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + unsigned long timeout; + u32 val; + + pci_write_config_dword(dev, PCI_INTERRUPT_CTRL, 0); + + /* + * MRFLD IUNIT DPHY is located in an always-power-on island + * MRFLD HW design need all CSI ports are disabled before + * powering down the IUNIT. + */ + pci_read_config_dword(dev, PCI_CSI_CONTROL, &val); + val |= PCI_CSI_CONTROL_PORTS_OFF_MASK; + pci_write_config_dword(dev, PCI_CSI_CONTROL, val); + + /* Write 0x3 to ISPSSPM0 bit[1:0] to power off the IUNIT */ + iosf_mbi_modify(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM0, + ISPSSPM0_IUNIT_POWER_OFF, ISPSSPM0_ISPSSC_MASK); + + /* + * There should be no IUNIT access while power-down is + * in progress HW sighting: 4567865 + * Wait up to 50 ms for the IUNIT to shut down. + */ + timeout = jiffies + msecs_to_jiffies(50); + while (1) { + /* Wait until ISPSSPM0 bit[25:24] shows 0x3 */ + iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM0, &val); + val = (val & ISPSSPM0_ISPSSS_MASK) >> ISPSSPM0_ISPSSS_OFFSET; + if (val == ISPSSPM0_IUNIT_POWER_OFF) + break; + + if (time_after(jiffies, timeout)) { + dev_err(&dev->dev, "IUNIT power-off timeout.\n"); + return -EBUSY; + } + usleep_range(1000, 2000); + } + + pm_runtime_allow(&dev->dev); + pm_runtime_put_sync_suspend(&dev->dev); + + return 0; +} + +static void isp_remove(struct pci_dev *dev) +{ + pm_runtime_get_sync(&dev->dev); + pm_runtime_forbid(&dev->dev); +} + +static int isp_pci_suspend(struct device *dev) +{ + return 0; +} + +static int isp_pci_resume(struct device *dev) +{ + return 0; +} + +static UNIVERSAL_DEV_PM_OPS(isp_pm_ops, isp_pci_suspend, + isp_pci_resume, NULL); + +static const struct pci_device_id isp_id_table[] = { + { PCI_VDEVICE(INTEL, 0x22b8), }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, isp_id_table); + +static struct pci_driver isp_pci_driver = { + .name = "intel_atomisp2_pm", + .id_table = isp_id_table, + .probe = isp_probe, + .remove = isp_remove, + .driver.pm = &isp_pm_ops, +}; + +module_pci_driver(isp_pci_driver); + +MODULE_DESCRIPTION("Intel AtomISP2 dummy / power-management drv (for suspend)"); +MODULE_AUTHOR("Hans de Goede "); +MODULE_LICENSE("GPL v2"); -- cgit From 0252894f53fc2693672308d5453d5076b7516637 Mon Sep 17 00:00:00 2001 From: Misha Komarovskiy Date: Sat, 27 Oct 2018 02:07:23 +0300 Subject: platform/x86: ideapad: Add Y530-15ICH to no_hw_rfkill Lenovo Legion Y530-15ICH is another model without hardware radio switch. Add it to no_hw_rfkill to enable wireless. Signed-off-by: Misha Komarovskiy Signed-off-by: Andy Shevchenko --- drivers/platform/x86/ideapad-laptop.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index f856d59361f2..b6489cba2985 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -1146,6 +1146,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y520-15IKBM"), }, }, + { + .ident = "Lenovo Legion Y530-15ICH", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Legion Y530-15ICH"), + }, + }, { .ident = "Lenovo Legion Y720-15IKB", .matches = { -- cgit From 69372c1dbdee8cab3c3892eda932c624af59a494 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Sun, 28 Oct 2018 11:37:00 +0530 Subject: platform/x86: wmi: declare device_type structure as constant The only usage of device_type structure is getting stored as a reference in the type field of device structure. This type field is declared const. Therefore, the device_type structure can never be modified and can be declared as const. Signed-off-by: Bhumika Goyal Signed-off-by: Andy Shevchenko --- drivers/platform/x86/wmi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 04791ea5d97b..bea35be68706 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -987,19 +987,19 @@ static struct bus_type wmi_bus_type = { .remove = wmi_dev_remove, }; -static struct device_type wmi_type_event = { +static const struct device_type wmi_type_event = { .name = "event", .groups = wmi_event_groups, .release = wmi_dev_release, }; -static struct device_type wmi_type_method = { +static const struct device_type wmi_type_method = { .name = "method", .groups = wmi_method_groups, .release = wmi_dev_release, }; -static struct device_type wmi_type_data = { +static const struct device_type wmi_type_data = { .name = "data", .groups = wmi_data_groups, .release = wmi_dev_release, -- cgit From 29f6eb533c3be9b9c74cdeacc7bf14d927a8adb4 Mon Sep 17 00:00:00 2001 From: Jian-Hong Pan Date: Mon, 22 Oct 2018 18:00:04 +0800 Subject: platform/x86: asus-wmi: Only notify kbd LED hw_change by fn-key pressed Since commit dbb3d78f61ba ("platform/x86: asus-wmi: Call led hw_changed API on kbd brightness change"), asus-wmi directly changes the keyboard LED brightness when the keyboard brightness keys are pressed, raising the appropriate notification. However, this notification was unintentionally also being raised during boot and resume from suspend. This was resulting in userspace showing the keyboard LED OSD on resume for no good reason. Move the keyboard LED brightness changed notification from kbd_led_update to the new kbd_led_set_by_kbd function which is only called from the keyboard brightness function keys codepath. Signed-off-by: Jian-Hong Pan Signed-off-by: Daniel Drake Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 9441cce636e6..a805227b31e1 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -467,7 +467,6 @@ static void kbd_led_update(struct asus_wmi *asus) ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F); asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL); - led_classdev_notify_brightness_hw_changed(&asus->kbd_led, asus->kbd_led_wk); } static int kbd_led_read(struct asus_wmi *asus, int *level, int *env) @@ -521,6 +520,14 @@ static void kbd_led_set(struct led_classdev *led_cdev, do_kbd_led_set(led_cdev, value); } +static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value) +{ + struct led_classdev *led_cdev = &asus->kbd_led; + + do_kbd_led_set(led_cdev, value); + led_classdev_notify_brightness_hw_changed(led_cdev, asus->kbd_led_wk); +} + static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) { struct asus_wmi *asus; @@ -1759,18 +1766,18 @@ static void asus_wmi_notify(u32 value, void *context) } if (code == NOTIFY_KBD_BRTUP) { - do_kbd_led_set(&asus->kbd_led, asus->kbd_led_wk + 1); + kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1); goto exit; } if (code == NOTIFY_KBD_BRTDWN) { - do_kbd_led_set(&asus->kbd_led, asus->kbd_led_wk - 1); + kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1); goto exit; } if (code == NOTIFY_KBD_BRTTOGGLE) { if (asus->kbd_led_wk == asus->kbd_led.max_brightness) - do_kbd_led_set(&asus->kbd_led, 0); + kbd_led_set_by_kbd(asus, 0); else - do_kbd_led_set(&asus->kbd_led, asus->kbd_led_wk + 1); + kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1); goto exit; } -- cgit From ffb6ce7086ee2d68d8d6d987882f1c5e923fee7e Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Tue, 9 Oct 2018 14:40:55 +0800 Subject: platform/x86: asus-wmi: export function for evaluating WMI methods Export asus_wmi_evaluate_method() and related headers for use by other drivers. hid-asus is going to use this to avoid advertising that it has a keyboard backlight when the keyboard backlight is controlled via WMI. Signed-off-by: Daniel Drake Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-wmi.c | 88 ++--------------------------------------- 1 file changed, 3 insertions(+), 85 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index a805227b31e1..b52b192a4f16 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -69,89 +70,6 @@ MODULE_LICENSE("GPL"); #define NOTIFY_KBD_BRTDWN 0xc5 #define NOTIFY_KBD_BRTTOGGLE 0xc7 -/* WMI Methods */ -#define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */ -#define ASUS_WMI_METHODID_SFBD 0x44424653 /* Set First Boot Device */ -#define ASUS_WMI_METHODID_GLCD 0x44434C47 /* Get LCD status */ -#define ASUS_WMI_METHODID_GPID 0x44495047 /* Get Panel ID?? (Resol) */ -#define ASUS_WMI_METHODID_QMOD 0x444F4D51 /* Quiet MODe */ -#define ASUS_WMI_METHODID_SPLV 0x4C425053 /* Set Panel Light Value */ -#define ASUS_WMI_METHODID_AGFN 0x4E464741 /* FaN? */ -#define ASUS_WMI_METHODID_SFUN 0x4E554653 /* FUNCtionalities */ -#define ASUS_WMI_METHODID_SDSP 0x50534453 /* Set DiSPlay output */ -#define ASUS_WMI_METHODID_GDSP 0x50534447 /* Get DiSPlay output */ -#define ASUS_WMI_METHODID_DEVP 0x50564544 /* DEVice Policy */ -#define ASUS_WMI_METHODID_OSVR 0x5256534F /* OS VeRsion */ -#define ASUS_WMI_METHODID_DSTS 0x53544344 /* Device STatuS */ -#define ASUS_WMI_METHODID_DSTS2 0x53545344 /* Device STatuS #2*/ -#define ASUS_WMI_METHODID_BSTS 0x53545342 /* Bios STatuS ? */ -#define ASUS_WMI_METHODID_DEVS 0x53564544 /* DEVice Set */ -#define ASUS_WMI_METHODID_CFVS 0x53564643 /* CPU Frequency Volt Set */ -#define ASUS_WMI_METHODID_KBFT 0x5446424B /* KeyBoard FilTer */ -#define ASUS_WMI_METHODID_INIT 0x54494E49 /* INITialize */ -#define ASUS_WMI_METHODID_HKEY 0x59454B48 /* Hot KEY ?? */ - -#define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE - -/* Wireless */ -#define ASUS_WMI_DEVID_HW_SWITCH 0x00010001 -#define ASUS_WMI_DEVID_WIRELESS_LED 0x00010002 -#define ASUS_WMI_DEVID_CWAP 0x00010003 -#define ASUS_WMI_DEVID_WLAN 0x00010011 -#define ASUS_WMI_DEVID_WLAN_LED 0x00010012 -#define ASUS_WMI_DEVID_BLUETOOTH 0x00010013 -#define ASUS_WMI_DEVID_GPS 0x00010015 -#define ASUS_WMI_DEVID_WIMAX 0x00010017 -#define ASUS_WMI_DEVID_WWAN3G 0x00010019 -#define ASUS_WMI_DEVID_UWB 0x00010021 - -/* Leds */ -/* 0x000200XX and 0x000400XX */ -#define ASUS_WMI_DEVID_LED1 0x00020011 -#define ASUS_WMI_DEVID_LED2 0x00020012 -#define ASUS_WMI_DEVID_LED3 0x00020013 -#define ASUS_WMI_DEVID_LED4 0x00020014 -#define ASUS_WMI_DEVID_LED5 0x00020015 -#define ASUS_WMI_DEVID_LED6 0x00020016 - -/* Backlight and Brightness */ -#define ASUS_WMI_DEVID_ALS_ENABLE 0x00050001 /* Ambient Light Sensor */ -#define ASUS_WMI_DEVID_BACKLIGHT 0x00050011 -#define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 -#define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 -#define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */ -#define ASUS_WMI_DEVID_LIGHTBAR 0x00050025 - -/* Misc */ -#define ASUS_WMI_DEVID_CAMERA 0x00060013 - -/* Storage */ -#define ASUS_WMI_DEVID_CARDREADER 0x00080013 - -/* Input */ -#define ASUS_WMI_DEVID_TOUCHPAD 0x00100011 -#define ASUS_WMI_DEVID_TOUCHPAD_LED 0x00100012 - -/* Fan, Thermal */ -#define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011 -#define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 - -/* Power */ -#define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012 - -/* Deep S3 / Resume on LID open */ -#define ASUS_WMI_DEVID_LID_RESUME 0x00120031 - -/* DSTS masks */ -#define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 -#define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002 -#define ASUS_WMI_DSTS_PRESENCE_BIT 0x00010000 -#define ASUS_WMI_DSTS_USER_BIT 0x00020000 -#define ASUS_WMI_DSTS_BIOS_BIT 0x00040000 -#define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF -#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 -#define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F - #define ASUS_FAN_DESC "cpu_fan" #define ASUS_FAN_MFUN 0x13 #define ASUS_FAN_SFUN_READ 0x06 @@ -301,8 +219,7 @@ static void asus_wmi_input_exit(struct asus_wmi *asus) asus->inputdev = NULL; } -static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, - u32 *retval) +int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) { struct bios_args args = { .arg0 = arg0, @@ -338,6 +255,7 @@ exit: return 0; } +EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method); static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args) { -- cgit From 3b692c55e58d06ba9b17c66784cab5a95ba5be9b Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Tue, 9 Oct 2018 14:40:56 +0800 Subject: HID: asus: only support backlight when it's not driven by WMI The Asus GL502VSK has the same 0B05:1837 keyboard as we've seen in several Republic of Gamers laptops. However, in this model, the keybard backlight control exposed by hid-asus has no effect on the keyboard backlight. Instead, the keyboard backlight is correctly driven by asus-wmi. With two keyboard backlight devices available (and only the acer-wmi one working), GNOME is picking the wrong one to drive in the UI. Avoid this problem by not creating the backlight interface when we detect a WMI-driven keyboard backlight. We have also tested Asus GL702VMK which does have the hid-asus backlight present, and it still works fine with this patch (WMI method call returns UNSUPPORTED_METHOD). A direct "depends on ASUS_WMI" is intentionally avoided so that HID_ASUS users who have ASUS_WMI=n will not quietly lose their HID_ASUS driver on a kernel upgrade. Signed-off-by: Daniel Drake Reviewed-by: Benjamin Tissoires Signed-off-by: Andy Shevchenko --- drivers/hid/Kconfig | 1 + drivers/hid/hid-asus.c | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 61e1953ff921..5c84b34c142e 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -149,6 +149,7 @@ config HID_APPLEIR config HID_ASUS tristate "Asus" depends on LEDS_CLASS + depends on ASUS_WMI || ASUS_WMI=n ---help--- Support for Asus notebook built-in keyboard and touchpad via i2c, and the Asus Republic of Gamers laptop keyboard special keys. diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 88a5672f42cd..dc6d6477e961 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include /* For to_usb_interface for T100 touchpad intf check */ @@ -349,6 +350,24 @@ static void asus_kbd_backlight_work(struct work_struct *work) hid_err(led->hdev, "Asus failed to set keyboard backlight: %d\n", ret); } +/* WMI-based keyboard backlight LED control (via asus-wmi driver) takes + * precedence. We only activate HID-based backlight control when the + * WMI control is not available. + */ +static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev) +{ + u32 value; + int ret; + + ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS2, + ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value); + hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value); + if (ret) + return false; + + return !!(value & ASUS_WMI_DSTS_PRESENCE_BIT); +} + static int asus_kbd_register_leds(struct hid_device *hdev) { struct asus_drvdata *drvdata = hid_get_drvdata(hdev); @@ -436,7 +455,9 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi) drvdata->input = input; - if (drvdata->enable_backlight && asus_kbd_register_leds(hdev)) + if (drvdata->enable_backlight && + !asus_kbd_wmi_led_control_present(hdev) && + asus_kbd_register_leds(hdev)) hid_warn(hdev, "Failed to initialize backlight.\n"); return 0; -- cgit