diff options
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/chrome/chromeos_laptop.c | 896 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_debugfs.c | 76 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_lpc.c | 16 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_sysfs.c | 141 | ||||
-rw-r--r-- | drivers/platform/mellanox/mlxreg-hotplug.c | 31 | ||||
-rw-r--r-- | drivers/platform/x86/Kconfig | 4 | ||||
-rw-r--r-- | drivers/platform/x86/dell-smbios-base.c | 4 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/fujitsu-laptop.c | 199 | ||||
-rw-r--r-- | drivers/platform/x86/gpd-pocket-fan.c | 4 | ||||
-rw-r--r-- | drivers/platform/x86/intel-hid.c | 14 | ||||
-rw-r--r-- | drivers/platform/x86/intel_cht_int33fe.c | 24 | ||||
-rw-r--r-- | drivers/platform/x86/intel_turbo_max_3.c | 3 | ||||
-rw-r--r-- | drivers/platform/x86/mlx-platform.c | 68 | ||||
-rw-r--r-- | drivers/platform/x86/silead_dmi.c | 17 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 397 | ||||
-rw-r--r-- | drivers/platform/x86/topstar-laptop.c | 363 | ||||
-rw-r--r-- | drivers/platform/x86/wmi.c | 23 |
18 files changed, 1587 insertions, 695 deletions
diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index d8599736a41a..5c47f451e43b 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -1,33 +1,20 @@ -/* - * chromeos_laptop.c - Driver to instantiate Chromebook i2c/smbus devices. - * - * Author : Benson Leung <bleung@chromium.org> - * - * Copyright (C) 2012 Google, Inc. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// Driver to instantiate Chromebook i2c/smbus devices. +// +// Copyright (C) 2012 Google, Inc. +// Author: Benson Leung <bleung@chromium.org> + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/dmi.h> #include <linux/i2c.h> -#include <linux/platform_data/atmel_mxt_ts.h> #include <linux/input.h> #include <linux/interrupt.h> +#include <linux/ioport.h> #include <linux/module.h> +#include <linux/pci.h> #include <linux/platform_device.h> +#include <linux/property.h> #define ATMEL_TP_I2C_ADDR 0x4b #define ATMEL_TP_I2C_BL_ADDR 0x25 @@ -38,18 +25,11 @@ #define ISL_ALS_I2C_ADDR 0x44 #define TAOS_ALS_I2C_ADDR 0x29 -#define MAX_I2C_DEVICE_DEFERRALS 5 - -static struct i2c_client *als; -static struct i2c_client *tp; -static struct i2c_client *ts; - static const char *i2c_adapter_names[] = { "SMBus I801 adapter", "i915 gmbus vga", "i915 gmbus panel", "Synopsys DesignWare I2C adapter", - "Synopsys DesignWare I2C adapter", }; /* Keep this enum consistent with i2c_adapter_names */ @@ -57,126 +37,41 @@ enum i2c_adapter_type { I2C_ADAPTER_SMBUS = 0, I2C_ADAPTER_VGADDC, I2C_ADAPTER_PANEL, - I2C_ADAPTER_DESIGNWARE_0, - I2C_ADAPTER_DESIGNWARE_1, -}; - -enum i2c_peripheral_state { - UNPROBED = 0, - PROBED, - TIMEDOUT, + I2C_ADAPTER_DESIGNWARE, }; struct i2c_peripheral { - int (*add)(enum i2c_adapter_type type); - enum i2c_adapter_type type; - enum i2c_peripheral_state state; - int tries; -}; - -#define MAX_I2C_PERIPHERALS 4 + struct i2c_board_info board_info; + unsigned short alt_addr; -struct chromeos_laptop { - struct i2c_peripheral i2c_peripherals[MAX_I2C_PERIPHERALS]; -}; - -static struct chromeos_laptop *cros_laptop; - -static struct i2c_board_info cyapa_device = { - I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), - .flags = I2C_CLIENT_WAKE, -}; + const char *dmi_name; + unsigned long irqflags; + struct resource irq_resource; -static struct i2c_board_info elantech_device = { - I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR), - .flags = I2C_CLIENT_WAKE, -}; - -static struct i2c_board_info isl_als_device = { - I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), -}; - -static struct i2c_board_info tsl2583_als_device = { - I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR), -}; - -static struct i2c_board_info tsl2563_als_device = { - I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR), -}; - -static int mxt_t19_keys[] = { - KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - BTN_LEFT -}; - -static struct mxt_platform_data atmel_224s_tp_platform_data = { - .irqflags = IRQF_TRIGGER_FALLING, - .t19_num_keys = ARRAY_SIZE(mxt_t19_keys), - .t19_keymap = mxt_t19_keys, - .suspend_mode = MXT_SUSPEND_T9_CTRL, -}; + enum i2c_adapter_type type; + u32 pci_devid; -static struct i2c_board_info atmel_224s_tp_device = { - I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR), - .platform_data = &atmel_224s_tp_platform_data, - .flags = I2C_CLIENT_WAKE, + struct i2c_client *client; }; -static struct mxt_platform_data atmel_1664s_platform_data = { - .irqflags = IRQF_TRIGGER_FALLING, - .suspend_mode = MXT_SUSPEND_T9_CTRL, +struct chromeos_laptop { + /* + * Note that we can't mark this pointer as const because + * i2c_new_probed_device() changes passed in I2C board info, so. + */ + struct i2c_peripheral *i2c_peripherals; + unsigned int num_i2c_peripherals; }; -static struct i2c_board_info atmel_1664s_device = { - I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR), - .platform_data = &atmel_1664s_platform_data, - .flags = I2C_CLIENT_WAKE, -}; +static const struct chromeos_laptop *cros_laptop; -static struct i2c_client *__add_probed_i2c_device( - const char *name, - int bus, - struct i2c_board_info *info, - const unsigned short *alt_addr_list) +static struct i2c_client * +chromes_laptop_instantiate_i2c_device(struct i2c_adapter *adapter, + struct i2c_board_info *info, + unsigned short alt_addr) { - const struct dmi_device *dmi_dev; - const struct dmi_dev_onboard *dev_data; - struct i2c_adapter *adapter; - struct i2c_client *client = NULL; const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END }; - - if (bus < 0) - return NULL; - /* - * If a name is specified, look for irq platform information stashed - * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware. - */ - if (name) { - dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL); - if (!dmi_dev) { - pr_err("%s failed to dmi find device %s.\n", - __func__, - name); - return NULL; - } - dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data; - if (!dev_data) { - pr_err("%s failed to get data from dmi for %s.\n", - __func__, name); - return NULL; - } - info->irq = dev_data->instance; - } - - adapter = i2c_get_adapter(bus); - if (!adapter) { - pr_err("%s failed to get i2c adapter %d.\n", __func__, bus); - return NULL; - } + struct i2c_client *client; /* * Add the i2c device. If we can't detect it at the primary @@ -184,339 +79,345 @@ static struct i2c_client *__add_probed_i2c_device( * structure gets assigned primary address. */ client = i2c_new_probed_device(adapter, info, addr_list, NULL); - if (!client && alt_addr_list) { + if (!client && alt_addr) { struct i2c_board_info dummy_info = { I2C_BOARD_INFO("dummy", info->addr), }; + const unsigned short alt_addr_list[] = { + alt_addr, I2C_CLIENT_END + }; struct i2c_client *dummy; dummy = i2c_new_probed_device(adapter, &dummy_info, alt_addr_list, NULL); if (dummy) { - pr_debug("%s %d-%02x is probed at %02x\n", - __func__, bus, info->addr, dummy->addr); + pr_debug("%d-%02x is probed at %02x\n", + adapter->nr, info->addr, dummy->addr); i2c_unregister_device(dummy); client = i2c_new_device(adapter, info); } } if (!client) - pr_notice("%s failed to register device %d-%02x\n", - __func__, bus, info->addr); + pr_debug("failed to register device %d-%02x\n", + adapter->nr, info->addr); else - pr_debug("%s added i2c device %d-%02x\n", - __func__, bus, info->addr); + pr_debug("added i2c device %d-%02x\n", + adapter->nr, info->addr); - i2c_put_adapter(adapter); return client; } -struct i2c_lookup { - const char *name; - int instance; - int n; -}; - -static int __find_i2c_adap(struct device *dev, void *data) -{ - struct i2c_lookup *lookup = data; - static const char *prefix = "i2c-"; - struct i2c_adapter *adapter; - - if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0) - return 0; - adapter = to_i2c_adapter(dev); - if (strncmp(adapter->name, lookup->name, strlen(lookup->name)) == 0 && - lookup->n++ == lookup->instance) - return 1; - return 0; -} - -static int find_i2c_adapter_num(enum i2c_adapter_type type) -{ - struct device *dev = NULL; - struct i2c_adapter *adapter; - struct i2c_lookup lookup; - - memset(&lookup, 0, sizeof(lookup)); - lookup.name = i2c_adapter_names[type]; - lookup.instance = (type == I2C_ADAPTER_DESIGNWARE_1) ? 1 : 0; - - /* find the adapter by name */ - dev = bus_find_device(&i2c_bus_type, NULL, &lookup, __find_i2c_adap); - if (!dev) { - /* Adapters may appear later. Deferred probing will retry */ - pr_notice("%s: i2c adapter %s not found on system.\n", __func__, - lookup.name); - return -ENODEV; - } - adapter = to_i2c_adapter(dev); - return adapter->nr; -} - -/* - * Takes a list of addresses in addrs as such : - * { addr1, ... , addrn, I2C_CLIENT_END }; - * add_probed_i2c_device will use i2c_new_probed_device - * and probe for devices at all of the addresses listed. - * Returns NULL if no devices found. - * See Documentation/i2c/instantiating-devices for more information. - */ -static struct i2c_client *add_probed_i2c_device( - const char *name, - enum i2c_adapter_type type, - struct i2c_board_info *info, - const unsigned short *addrs) +static bool chromeos_laptop_match_adapter_devid(struct device *dev, u32 devid) { - return __add_probed_i2c_device(name, - find_i2c_adapter_num(type), - info, - addrs); -} + struct pci_dev *pdev; -/* - * Probes for a device at a single address, the one provided by - * info->addr. - * Returns NULL if no device found. - */ -static struct i2c_client *add_i2c_device(const char *name, - enum i2c_adapter_type type, - struct i2c_board_info *info) -{ - return __add_probed_i2c_device(name, - find_i2c_adapter_num(type), - info, - NULL); -} - -static int setup_cyapa_tp(enum i2c_adapter_type type) -{ - if (tp) - return 0; + if (!dev_is_pci(dev)) + return false; - /* add cyapa touchpad */ - tp = add_i2c_device("trackpad", type, &cyapa_device); - return (!tp) ? -EAGAIN : 0; + pdev = to_pci_dev(dev); + return devid == PCI_DEVID(pdev->bus->number, pdev->devfn); } -static int setup_atmel_224s_tp(enum i2c_adapter_type type) +static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter) { - const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR, - I2C_CLIENT_END }; - if (tp) - return 0; - - /* add atmel mxt touchpad */ - tp = add_probed_i2c_device("trackpad", type, - &atmel_224s_tp_device, addr_list); - return (!tp) ? -EAGAIN : 0; -} + struct i2c_peripheral *i2c_dev; + int i; -static int setup_elantech_tp(enum i2c_adapter_type type) -{ - if (tp) - return 0; + for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) { + i2c_dev = &cros_laptop->i2c_peripherals[i]; - /* add elantech touchpad */ - tp = add_i2c_device("trackpad", type, &elantech_device); - return (!tp) ? -EAGAIN : 0; -} + /* Skip devices already created */ + if (i2c_dev->client) + continue; -static int setup_atmel_1664s_ts(enum i2c_adapter_type type) -{ - const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR, - I2C_CLIENT_END }; - if (ts) - return 0; - - /* add atmel mxt touch device */ - ts = add_probed_i2c_device("touchscreen", type, - &atmel_1664s_device, addr_list); - return (!ts) ? -EAGAIN : 0; -} + if (strncmp(adapter->name, i2c_adapter_names[i2c_dev->type], + strlen(i2c_adapter_names[i2c_dev->type]))) + continue; -static int setup_isl29018_als(enum i2c_adapter_type type) -{ - if (als) - return 0; + if (i2c_dev->pci_devid && + !chromeos_laptop_match_adapter_devid(adapter->dev.parent, + i2c_dev->pci_devid)) { + continue; + } - /* add isl29018 light sensor */ - als = add_i2c_device("lightsensor", type, &isl_als_device); - return (!als) ? -EAGAIN : 0; + i2c_dev->client = + chromes_laptop_instantiate_i2c_device(adapter, + &i2c_dev->board_info, + i2c_dev->alt_addr); + } } -static int setup_tsl2583_als(enum i2c_adapter_type type) +static void chromeos_laptop_detach_i2c_client(struct i2c_client *client) { - if (als) - return 0; - - /* add tsl2583 light sensor */ - als = add_i2c_device(NULL, type, &tsl2583_als_device); - return (!als) ? -EAGAIN : 0; -} + struct i2c_peripheral *i2c_dev; + int i; -static int setup_tsl2563_als(enum i2c_adapter_type type) -{ - if (als) - return 0; + for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) { + i2c_dev = &cros_laptop->i2c_peripherals[i]; - /* add tsl2563 light sensor */ - als = add_i2c_device(NULL, type, &tsl2563_als_device); - return (!als) ? -EAGAIN : 0; + if (i2c_dev->client == client) + i2c_dev->client = NULL; + } } -static int __init chromeos_laptop_dmi_matched(const struct dmi_system_id *id) +static int chromeos_laptop_i2c_notifier_call(struct notifier_block *nb, + unsigned long action, void *data) { - cros_laptop = (void *)id->driver_data; - pr_debug("DMI Matched %s.\n", id->ident); + struct device *dev = data; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + if (dev->type == &i2c_adapter_type) + chromeos_laptop_check_adapter(to_i2c_adapter(dev)); + break; + + case BUS_NOTIFY_REMOVED_DEVICE: + if (dev->type == &i2c_client_type) + chromeos_laptop_detach_i2c_client(to_i2c_client(dev)); + break; + } - /* Indicate to dmi_scan that processing is done. */ - return 1; + return 0; } -static int chromeos_laptop_probe(struct platform_device *pdev) -{ - int i; - int ret = 0; - - for (i = 0; i < MAX_I2C_PERIPHERALS; i++) { - struct i2c_peripheral *i2c_dev; - - i2c_dev = &cros_laptop->i2c_peripherals[i]; - - /* No more peripherals. */ - if (i2c_dev->add == NULL) - break; - - if (i2c_dev->state == TIMEDOUT || i2c_dev->state == PROBED) - continue; - - /* - * Check that the i2c adapter is present. - * -EPROBE_DEFER if missing as the adapter may appear much - * later. - */ - if (find_i2c_adapter_num(i2c_dev->type) == -ENODEV) { - ret = -EPROBE_DEFER; - continue; - } - - /* Add the device. */ - if (i2c_dev->add(i2c_dev->type) == -EAGAIN) { - /* - * Set -EPROBE_DEFER a limited num of times - * if device is not successfully added. - */ - if (++i2c_dev->tries < MAX_I2C_DEVICE_DEFERRALS) { - ret = -EPROBE_DEFER; - } else { - /* Ran out of tries. */ - pr_notice("%s: Ran out of tries for device.\n", - __func__); - i2c_dev->state = TIMEDOUT; - } - } else { - i2c_dev->state = PROBED; - } - } +static struct notifier_block chromeos_laptop_i2c_notifier = { + .notifier_call = chromeos_laptop_i2c_notifier_call, +}; - return ret; +#define DECLARE_CROS_LAPTOP(_name) \ +static const struct chromeos_laptop _name __initconst = { \ + .i2c_peripherals = _name##_peripherals, \ + .num_i2c_peripherals = ARRAY_SIZE(_name##_peripherals), \ } -static struct chromeos_laptop samsung_series_5_550 = { - .i2c_peripherals = { - /* Touchpad. */ - { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS }, - /* Light Sensor. */ - { .add = setup_isl29018_als, I2C_ADAPTER_SMBUS }, +static struct i2c_peripheral samsung_series_5_550_peripherals[] __initdata = { + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_SMBUS, + }, + /* Light Sensor. */ + { + .board_info = { + I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), + }, + .dmi_name = "lightsensor", + .type = I2C_ADAPTER_SMBUS, }, }; +DECLARE_CROS_LAPTOP(samsung_series_5_550); -static struct chromeos_laptop samsung_series_5 = { - .i2c_peripherals = { - /* Light Sensor. */ - { .add = setup_tsl2583_als, I2C_ADAPTER_SMBUS }, +static struct i2c_peripheral samsung_series_5_peripherals[] __initdata = { + /* Light Sensor. */ + { + .board_info = { + I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR), + }, + .type = I2C_ADAPTER_SMBUS, }, }; +DECLARE_CROS_LAPTOP(samsung_series_5); -static struct chromeos_laptop chromebook_pixel = { - .i2c_peripherals = { - /* Touch Screen. */ - { .add = setup_atmel_1664s_ts, I2C_ADAPTER_PANEL }, - /* Touchpad. */ - { .add = setup_atmel_224s_tp, I2C_ADAPTER_VGADDC }, - /* Light Sensor. */ - { .add = setup_isl29018_als, I2C_ADAPTER_PANEL }, - }, +static const int chromebook_pixel_tp_keys[] __initconst = { + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + BTN_LEFT }; -static struct chromeos_laptop hp_chromebook_14 = { - .i2c_peripherals = { - /* Touchpad. */ - { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 }, +static const struct property_entry +chromebook_pixel_trackpad_props[] __initconst = { + PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", chromebook_pixel_tp_keys), + { } +}; + +static struct i2c_peripheral chromebook_pixel_peripherals[] __initdata = { + /* Touch Screen. */ + { + .board_info = { + I2C_BOARD_INFO("atmel_mxt_ts", + ATMEL_TS_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "touchscreen", + .irqflags = IRQF_TRIGGER_FALLING, + .type = I2C_ADAPTER_PANEL, + .alt_addr = ATMEL_TS_I2C_BL_ADDR, + }, + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("atmel_mxt_tp", + ATMEL_TP_I2C_ADDR), + .properties = + chromebook_pixel_trackpad_props, + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .irqflags = IRQF_TRIGGER_FALLING, + .type = I2C_ADAPTER_VGADDC, + .alt_addr = ATMEL_TP_I2C_BL_ADDR, + }, + /* Light Sensor. */ + { + .board_info = { + I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), + }, + .dmi_name = "lightsensor", + .type = I2C_ADAPTER_PANEL, }, }; +DECLARE_CROS_LAPTOP(chromebook_pixel); -static struct chromeos_laptop dell_chromebook_11 = { - .i2c_peripherals = { - /* Touchpad. */ - { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 }, - /* Elan Touchpad option. */ - { .add = setup_elantech_tp, I2C_ADAPTER_DESIGNWARE_0 }, +static struct i2c_peripheral hp_chromebook_14_peripherals[] __initdata = { + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE, }, }; +DECLARE_CROS_LAPTOP(hp_chromebook_14); -static struct chromeos_laptop toshiba_cb35 = { - .i2c_peripherals = { - /* Touchpad. */ - { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 }, +static struct i2c_peripheral dell_chromebook_11_peripherals[] __initdata = { + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE, + }, + /* Elan Touchpad option. */ + { + .board_info = { + I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE, }, }; +DECLARE_CROS_LAPTOP(dell_chromebook_11); -static struct chromeos_laptop acer_c7_chromebook = { - .i2c_peripherals = { - /* Touchpad. */ - { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS }, +static struct i2c_peripheral toshiba_cb35_peripherals[] __initdata = { + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE, }, }; +DECLARE_CROS_LAPTOP(toshiba_cb35); -static struct chromeos_laptop acer_ac700 = { - .i2c_peripherals = { - /* Light Sensor. */ - { .add = setup_tsl2563_als, I2C_ADAPTER_SMBUS }, +static struct i2c_peripheral acer_c7_chromebook_peripherals[] __initdata = { + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_SMBUS, }, }; +DECLARE_CROS_LAPTOP(acer_c7_chromebook); -static struct chromeos_laptop acer_c720 = { - .i2c_peripherals = { - /* Touchscreen. */ - { .add = setup_atmel_1664s_ts, I2C_ADAPTER_DESIGNWARE_1 }, - /* Touchpad. */ - { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 }, - /* Elan Touchpad option. */ - { .add = setup_elantech_tp, I2C_ADAPTER_DESIGNWARE_0 }, - /* Light Sensor. */ - { .add = setup_isl29018_als, I2C_ADAPTER_DESIGNWARE_1 }, +static struct i2c_peripheral acer_ac700_peripherals[] __initdata = { + /* Light Sensor. */ + { + .board_info = { + I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR), + }, + .type = I2C_ADAPTER_SMBUS, }, }; +DECLARE_CROS_LAPTOP(acer_ac700); -static struct chromeos_laptop hp_pavilion_14_chromebook = { - .i2c_peripherals = { - /* Touchpad. */ - { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS }, +static struct i2c_peripheral acer_c720_peripherals[] __initdata = { + /* Touchscreen. */ + { + .board_info = { + I2C_BOARD_INFO("atmel_mxt_ts", + ATMEL_TS_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "touchscreen", + .irqflags = IRQF_TRIGGER_FALLING, + .type = I2C_ADAPTER_DESIGNWARE, + .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)), + .alt_addr = ATMEL_TS_I2C_BL_ADDR, + }, + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE, + .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)), + }, + /* Elan Touchpad option. */ + { + .board_info = { + I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE, + .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)), + }, + /* Light Sensor. */ + { + .board_info = { + I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), + }, + .dmi_name = "lightsensor", + .type = I2C_ADAPTER_DESIGNWARE, + .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)), }, }; +DECLARE_CROS_LAPTOP(acer_c720); -static struct chromeos_laptop cr48 = { - .i2c_peripherals = { - /* Light Sensor. */ - { .add = setup_tsl2563_als, I2C_ADAPTER_SMBUS }, +static struct i2c_peripheral +hp_pavilion_14_chromebook_peripherals[] __initdata = { + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_SMBUS, }, }; +DECLARE_CROS_LAPTOP(hp_pavilion_14_chromebook); -#define _CBDD(board_) \ - .callback = chromeos_laptop_dmi_matched, \ - .driver_data = (void *)&board_ +static struct i2c_peripheral cr48_peripherals[] __initdata = { + /* Light Sensor. */ + { + .board_info = { + I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR), + }, + .type = I2C_ADAPTER_SMBUS, + }, +}; +DECLARE_CROS_LAPTOP(cr48); static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { { @@ -525,14 +426,14 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"), DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"), }, - _CBDD(samsung_series_5_550), + .driver_data = (void *)&samsung_series_5_550, }, { .ident = "Samsung Series 5", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Alex"), }, - _CBDD(samsung_series_5), + .driver_data = (void *)&samsung_series_5, }, { .ident = "Chromebook Pixel", @@ -540,7 +441,7 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), DMI_MATCH(DMI_PRODUCT_NAME, "Link"), }, - _CBDD(chromebook_pixel), + .driver_data = (void *)&chromebook_pixel, }, { .ident = "Wolf", @@ -548,7 +449,7 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), DMI_MATCH(DMI_PRODUCT_NAME, "Wolf"), }, - _CBDD(dell_chromebook_11), + .driver_data = (void *)&dell_chromebook_11, }, { .ident = "HP Chromebook 14", @@ -556,7 +457,7 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), DMI_MATCH(DMI_PRODUCT_NAME, "Falco"), }, - _CBDD(hp_chromebook_14), + .driver_data = (void *)&hp_chromebook_14, }, { .ident = "Toshiba CB35", @@ -564,99 +465,214 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), DMI_MATCH(DMI_PRODUCT_NAME, "Leon"), }, - _CBDD(toshiba_cb35), + .driver_data = (void *)&toshiba_cb35, }, { .ident = "Acer C7 Chromebook", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"), }, - _CBDD(acer_c7_chromebook), + .driver_data = (void *)&acer_c7_chromebook, }, { .ident = "Acer AC700", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"), }, - _CBDD(acer_ac700), + .driver_data = (void *)&acer_ac700, }, { .ident = "Acer C720", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"), }, - _CBDD(acer_c720), + .driver_data = (void *)&acer_c720, }, { .ident = "HP Pavilion 14 Chromebook", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"), }, - _CBDD(hp_pavilion_14_chromebook), + .driver_data = (void *)&hp_pavilion_14_chromebook, }, { .ident = "Cr-48", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Mario"), }, - _CBDD(cr48), + .driver_data = (void *)&cr48, }, { } }; MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table); -static struct platform_device *cros_platform_device; +static int __init chromeos_laptop_scan_adapter(struct device *dev, void *data) +{ + struct i2c_adapter *adapter; -static struct platform_driver cros_platform_driver = { - .driver = { - .name = "chromeos_laptop", - }, - .probe = chromeos_laptop_probe, -}; + adapter = i2c_verify_adapter(dev); + if (adapter) + chromeos_laptop_check_adapter(adapter); + + return 0; +} + +static int __init chromeos_laptop_get_irq_from_dmi(const char *dmi_name) +{ + const struct dmi_device *dmi_dev; + const struct dmi_dev_onboard *dev_data; + + dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, dmi_name, NULL); + if (!dmi_dev) { + pr_err("failed to find DMI device '%s'\n", dmi_name); + return -ENOENT; + } + + dev_data = dmi_dev->device_data; + if (!dev_data) { + pr_err("failed to get data from DMI for '%s'\n", dmi_name); + return -EINVAL; + } + + return dev_data->instance; +} + +static int __init chromeos_laptop_setup_irq(struct i2c_peripheral *i2c_dev) +{ + int irq; + + if (i2c_dev->dmi_name) { + irq = chromeos_laptop_get_irq_from_dmi(i2c_dev->dmi_name); + if (irq < 0) + return irq; + + i2c_dev->irq_resource = (struct resource) + DEFINE_RES_NAMED(irq, 1, NULL, + IORESOURCE_IRQ | i2c_dev->irqflags); + i2c_dev->board_info.resources = &i2c_dev->irq_resource; + i2c_dev->board_info.num_resources = 1; + } + + return 0; +} + +static struct chromeos_laptop * __init +chromeos_laptop_prepare(const struct chromeos_laptop *src) +{ + struct chromeos_laptop *cros_laptop; + struct i2c_peripheral *i2c_dev; + struct i2c_board_info *info; + int error; + int i; + + cros_laptop = kzalloc(sizeof(*cros_laptop), GFP_KERNEL); + if (!cros_laptop) + return ERR_PTR(-ENOMEM); + + cros_laptop->i2c_peripherals = kmemdup(src->i2c_peripherals, + src->num_i2c_peripherals * + sizeof(*src->i2c_peripherals), + GFP_KERNEL); + if (!cros_laptop->i2c_peripherals) { + error = -ENOMEM; + goto err_free_cros_laptop; + } + + cros_laptop->num_i2c_peripherals = src->num_i2c_peripherals; + + for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) { + i2c_dev = &cros_laptop->i2c_peripherals[i]; + info = &i2c_dev->board_info; + + error = chromeos_laptop_setup_irq(i2c_dev); + if (error) + goto err_destroy_cros_peripherals; + + /* We need to deep-copy properties */ + if (info->properties) { + info->properties = + property_entries_dup(info->properties); + if (IS_ERR(info->properties)) { + error = PTR_ERR(info->properties); + goto err_destroy_cros_peripherals; + } + } + } + + return cros_laptop; + +err_destroy_cros_peripherals: + while (--i >= 0) { + i2c_dev = &cros_laptop->i2c_peripherals[i]; + info = &i2c_dev->board_info; + if (info->properties) + property_entries_free(info->properties); + } + kfree(cros_laptop->i2c_peripherals); +err_free_cros_laptop: + kfree(cros_laptop); + return ERR_PTR(error); +} + +static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop) +{ + struct i2c_peripheral *i2c_dev; + struct i2c_board_info *info; + int i; + + for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) { + i2c_dev = &cros_laptop->i2c_peripherals[i]; + info = &i2c_dev->board_info; + + if (i2c_dev->client) + i2c_unregister_device(i2c_dev->client); + + if (info->properties) + property_entries_free(info->properties); + } + + kfree(cros_laptop->i2c_peripherals); + kfree(cros_laptop); +} static int __init chromeos_laptop_init(void) { - int ret; + const struct dmi_system_id *dmi_id; + int error; - if (!dmi_check_system(chromeos_laptop_dmi_table)) { - pr_debug("%s unsupported system.\n", __func__); + dmi_id = dmi_first_match(chromeos_laptop_dmi_table); + if (!dmi_id) { + pr_debug("unsupported system\n"); return -ENODEV; } - ret = platform_driver_register(&cros_platform_driver); - if (ret) - return ret; + pr_debug("DMI Matched %s\n", dmi_id->ident); + + cros_laptop = chromeos_laptop_prepare((void *)dmi_id->driver_data); + if (IS_ERR(cros_laptop)) + return PTR_ERR(cros_laptop); - cros_platform_device = platform_device_alloc("chromeos_laptop", -1); - if (!cros_platform_device) { - ret = -ENOMEM; - goto fail_platform_device1; + error = bus_register_notifier(&i2c_bus_type, + &chromeos_laptop_i2c_notifier); + if (error) { + pr_err("failed to register i2c bus notifier: %d\n", error); + chromeos_laptop_destroy(cros_laptop); + return error; } - ret = platform_device_add(cros_platform_device); - if (ret) - goto fail_platform_device2; + /* + * Scan adapters that have been registered before we installed + * the notifier to make sure we do not miss any devices. + */ + i2c_for_each_dev(NULL, chromeos_laptop_scan_adapter); return 0; - -fail_platform_device2: - platform_device_put(cros_platform_device); -fail_platform_device1: - platform_driver_unregister(&cros_platform_driver); - return ret; } static void __exit chromeos_laptop_exit(void) { - if (als) - i2c_unregister_device(als); - if (tp) - i2c_unregister_device(tp); - if (ts) - i2c_unregister_device(ts); - - platform_device_unregister(cros_platform_device); - platform_driver_unregister(&cros_platform_driver); + bus_unregister_notifier(&i2c_bus_type, &chromeos_laptop_i2c_notifier); + chromeos_laptop_destroy(cros_laptop); } module_init(chromeos_laptop_init); diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c index 0e88e18362c1..cc265ed8deb7 100644 --- a/drivers/platform/chrome/cros_ec_debugfs.c +++ b/drivers/platform/chrome/cros_ec_debugfs.c @@ -211,6 +211,58 @@ static int cros_ec_console_log_release(struct inode *inode, struct file *file) return 0; } +static ssize_t cros_ec_pdinfo_read(struct file *file, + char __user *user_buf, + size_t count, + loff_t *ppos) +{ + char read_buf[EC_USB_PD_MAX_PORTS * 40], *p = read_buf; + struct cros_ec_debugfs *debug_info = file->private_data; + struct cros_ec_device *ec_dev = debug_info->ec->ec_dev; + struct { + struct cros_ec_command msg; + union { + struct ec_response_usb_pd_control_v1 resp; + struct ec_params_usb_pd_control params; + }; + } __packed ec_buf; + struct cros_ec_command *msg; + struct ec_response_usb_pd_control_v1 *resp; + struct ec_params_usb_pd_control *params; + int i; + + msg = &ec_buf.msg; + params = (struct ec_params_usb_pd_control *)msg->data; + resp = (struct ec_response_usb_pd_control_v1 *)msg->data; + + msg->command = EC_CMD_USB_PD_CONTROL; + msg->version = 1; + msg->insize = sizeof(*resp); + msg->outsize = sizeof(*params); + + /* + * Read status from all PD ports until failure, typically caused + * by attempting to read status on a port that doesn't exist. + */ + for (i = 0; i < EC_USB_PD_MAX_PORTS; ++i) { + params->port = i; + params->role = 0; + params->mux = 0; + params->swap = 0; + + if (cros_ec_cmd_xfer_status(ec_dev, msg) < 0) + break; + + p += scnprintf(p, sizeof(read_buf) + read_buf - p, + "p%d: %s en:%.2x role:%.2x pol:%.2x\n", i, + resp->state, resp->enabled, resp->role, + resp->polarity); + } + + return simple_read_from_buffer(user_buf, count, ppos, + read_buf, p - read_buf); +} + const struct file_operations cros_ec_console_log_fops = { .owner = THIS_MODULE, .open = cros_ec_console_log_open, @@ -220,6 +272,13 @@ const struct file_operations cros_ec_console_log_fops = { .release = cros_ec_console_log_release, }; +const struct file_operations cros_ec_pdinfo_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = cros_ec_pdinfo_read, + .llseek = default_llseek, +}; + static int ec_read_version_supported(struct cros_ec_dev *ec) { struct ec_params_get_cmd_versions_v1 *params; @@ -288,7 +347,7 @@ static int cros_ec_create_console_log(struct cros_ec_debugfs *debug_info) init_waitqueue_head(&debug_info->log_wq); if (!debugfs_create_file("console_log", - S_IFREG | S_IRUGO, + S_IFREG | 0444, debug_info->dir, debug_info, &cros_ec_console_log_fops)) @@ -341,7 +400,7 @@ static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info) debug_info->panicinfo_blob.size = ret; if (!debugfs_create_blob("panicinfo", - S_IFREG | S_IRUGO, + S_IFREG | 0444, debug_info->dir, &debug_info->panicinfo_blob)) { ret = -ENOMEM; @@ -355,6 +414,15 @@ free: return ret; } +static int cros_ec_create_pdinfo(struct cros_ec_debugfs *debug_info) +{ + if (!debugfs_create_file("pdinfo", 0444, debug_info->dir, debug_info, + &cros_ec_pdinfo_fops)) + return -ENOMEM; + + return 0; +} + int cros_ec_debugfs_init(struct cros_ec_dev *ec) { struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev); @@ -379,6 +447,10 @@ int cros_ec_debugfs_init(struct cros_ec_dev *ec) if (ret) goto remove_debugfs; + ret = cros_ec_create_pdinfo(debug_info); + if (ret) + goto remove_debugfs; + ec->debug_info = debug_info; return 0; diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index af89e82eecd2..3682e1539251 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -31,6 +31,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/printk.h> +#include <linux/suspend.h> #define DRV_NAME "cros_ec_lpcs" #define ACPI_DRV_NAME "GOOG0004" @@ -235,6 +236,9 @@ static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data) cros_ec_get_next_event(ec_dev, NULL) > 0) blocking_notifier_call_chain(&ec_dev->event_notifier, 0, ec_dev); + + if (value == ACPI_NOTIFY_DEVICE_WAKE) + pm_system_wakeup(); } static int cros_ec_lpc_probe(struct platform_device *pdev) @@ -342,6 +346,18 @@ static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = { }, }, { + /* + * If the box is running custom coreboot firmware then the + * DMI BIOS version string will not be matched by "Google_", + * but the system vendor string will still be matched by + * "GOOGLE". + */ + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), + DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), + }, + }, + { /* x86-link, the Chromebook Pixel. */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index da0a719d32f7..5a6db3fe213a 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -34,10 +34,12 @@ #include <linux/types.h> #include <linux/uaccess.h> +#define to_cros_ec_dev(dev) container_of(dev, struct cros_ec_dev, class_dev) + /* Accessor functions */ -static ssize_t show_ec_reboot(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t reboot_show(struct device *dev, + struct device_attribute *attr, char *buf) { int count = 0; @@ -48,9 +50,9 @@ static ssize_t show_ec_reboot(struct device *dev, return count; } -static ssize_t store_ec_reboot(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t reboot_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { static const struct { const char * const str; @@ -70,8 +72,7 @@ static ssize_t store_ec_reboot(struct device *dev, int got_cmd = 0, offset = 0; int i; int ret; - struct cros_ec_dev *ec = container_of(dev, - struct cros_ec_dev, class_dev); + struct cros_ec_dev *ec = to_cros_ec_dev(dev); msg = kmalloc(sizeof(*msg) + sizeof(*param), GFP_KERNEL); if (!msg) @@ -114,22 +115,16 @@ static ssize_t store_ec_reboot(struct device *dev, msg->command = EC_CMD_REBOOT_EC + ec->cmd_offset; msg->outsize = sizeof(*param); msg->insize = 0; - ret = cros_ec_cmd_xfer(ec->ec_dev, msg); - if (ret < 0) { + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); + if (ret < 0) count = ret; - goto exit; - } - if (msg->result != EC_RES_SUCCESS) { - dev_dbg(ec->dev, "EC result %d\n", msg->result); - count = -EINVAL; - } exit: kfree(msg); return count; } -static ssize_t show_ec_version(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t version_show(struct device *dev, + struct device_attribute *attr, char *buf) { static const char * const image_names[] = {"unknown", "RO", "RW"}; struct ec_response_get_version *r_ver; @@ -138,8 +133,7 @@ static ssize_t show_ec_version(struct device *dev, struct cros_ec_command *msg; int ret; int count = 0; - struct cros_ec_dev *ec = container_of(dev, - struct cros_ec_dev, class_dev); + struct cros_ec_dev *ec = to_cros_ec_dev(dev); msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL); if (!msg) @@ -150,17 +144,11 @@ static ssize_t show_ec_version(struct device *dev, msg->command = EC_CMD_GET_VERSION + ec->cmd_offset; msg->insize = sizeof(*r_ver); msg->outsize = 0; - ret = cros_ec_cmd_xfer(ec->ec_dev, msg); + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); if (ret < 0) { count = ret; goto exit; } - if (msg->result != EC_RES_SUCCESS) { - count = scnprintf(buf, PAGE_SIZE, - "ERROR: EC returned %d\n", msg->result); - goto exit; - } - r_ver = (struct ec_response_get_version *)msg->data; /* Strings should be null-terminated, but let's be sure. */ r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0'; @@ -237,14 +225,13 @@ exit: return count; } -static ssize_t show_ec_flashinfo(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t flashinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct ec_response_flash_info *resp; struct cros_ec_command *msg; int ret; - struct cros_ec_dev *ec = container_of(dev, - struct cros_ec_dev, class_dev); + struct cros_ec_dev *ec = to_cros_ec_dev(dev); msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL); if (!msg) @@ -255,14 +242,9 @@ static ssize_t show_ec_flashinfo(struct device *dev, msg->command = EC_CMD_FLASH_INFO + ec->cmd_offset; msg->insize = sizeof(*resp); msg->outsize = 0; - ret = cros_ec_cmd_xfer(ec->ec_dev, msg); + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); if (ret < 0) goto exit; - if (msg->result != EC_RES_SUCCESS) { - ret = scnprintf(buf, PAGE_SIZE, - "ERROR: EC returned %d\n", msg->result); - goto exit; - } resp = (struct ec_response_flash_info *)msg->data; @@ -276,21 +258,102 @@ exit: return ret; } +/* Keyboard wake angle control */ +static ssize_t kb_wake_angle_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cros_ec_dev *ec = to_cros_ec_dev(dev); + struct ec_response_motion_sense *resp; + struct ec_params_motion_sense *param; + struct cros_ec_command *msg; + int ret; + + msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + param = (struct ec_params_motion_sense *)msg->data; + msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; + msg->version = 2; + param->cmd = MOTIONSENSE_CMD_KB_WAKE_ANGLE; + param->kb_wake_angle.data = EC_MOTION_SENSE_NO_VALUE; + msg->outsize = sizeof(*param); + msg->insize = sizeof(*resp); + + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); + if (ret < 0) + goto exit; + + resp = (struct ec_response_motion_sense *)msg->data; + ret = scnprintf(buf, PAGE_SIZE, "%d\n", resp->kb_wake_angle.ret); +exit: + kfree(msg); + return ret; +} + +static ssize_t kb_wake_angle_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cros_ec_dev *ec = to_cros_ec_dev(dev); + struct ec_params_motion_sense *param; + struct cros_ec_command *msg; + u16 angle; + int ret; + + ret = kstrtou16(buf, 0, &angle); + if (ret) + return ret; + + msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + param = (struct ec_params_motion_sense *)msg->data; + msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; + msg->version = 2; + param->cmd = MOTIONSENSE_CMD_KB_WAKE_ANGLE; + param->kb_wake_angle.data = angle; + msg->outsize = sizeof(*param); + msg->insize = sizeof(struct ec_response_motion_sense); + + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); + kfree(msg); + if (ret < 0) + return ret; + return count; +} + /* Module initialization */ -static DEVICE_ATTR(reboot, S_IWUSR | S_IRUGO, show_ec_reboot, store_ec_reboot); -static DEVICE_ATTR(version, S_IRUGO, show_ec_version, NULL); -static DEVICE_ATTR(flashinfo, S_IRUGO, show_ec_flashinfo, NULL); +static DEVICE_ATTR_RW(reboot); +static DEVICE_ATTR_RO(version); +static DEVICE_ATTR_RO(flashinfo); +static DEVICE_ATTR_RW(kb_wake_angle); static struct attribute *__ec_attrs[] = { + &dev_attr_kb_wake_angle.attr, &dev_attr_reboot.attr, &dev_attr_version.attr, &dev_attr_flashinfo.attr, NULL, }; +static umode_t cros_ec_ctrl_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct cros_ec_dev *ec = to_cros_ec_dev(dev); + + if (a == &dev_attr_kb_wake_angle.attr && !ec->has_kb_wake_angle) + return 0; + + return a->mode; +} + struct attribute_group cros_ec_attr_group = { .attrs = __ec_attrs, + .is_visible = cros_ec_ctrl_visible, }; EXPORT_SYMBOL(cros_ec_attr_group); diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c index 313cf8ad77bf..ea9e7f4479ca 100644 --- a/drivers/platform/mellanox/mlxreg-hotplug.c +++ b/drivers/platform/mellanox/mlxreg-hotplug.c @@ -93,9 +93,11 @@ struct mlxreg_hotplug_priv_data { bool after_probe; }; -static int mlxreg_hotplug_device_create(struct device *dev, +static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data *priv, struct mlxreg_core_data *data) { + struct mlxreg_core_hotplug_platform_data *pdata; + /* * Return if adapter number is negative. It could be in case hotplug * event is not associated with hotplug device. @@ -103,19 +105,21 @@ static int mlxreg_hotplug_device_create(struct device *dev, if (data->hpdev.nr < 0) return 0; - data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr); + pdata = dev_get_platdata(&priv->pdev->dev); + data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr + + pdata->shift_nr); if (!data->hpdev.adapter) { - dev_err(dev, "Failed to get adapter for bus %d\n", - data->hpdev.nr); + dev_err(priv->dev, "Failed to get adapter for bus %d\n", + data->hpdev.nr + pdata->shift_nr); return -EFAULT; } data->hpdev.client = i2c_new_device(data->hpdev.adapter, data->hpdev.brdinfo); if (!data->hpdev.client) { - dev_err(dev, "Failed to create client %s at bus %d at addr 0x%02x\n", - data->hpdev.brdinfo->type, data->hpdev.nr, - data->hpdev.brdinfo->addr); + dev_err(priv->dev, "Failed to create client %s at bus %d at addr 0x%02x\n", + data->hpdev.brdinfo->type, data->hpdev.nr + + pdata->shift_nr, data->hpdev.brdinfo->addr); i2c_put_adapter(data->hpdev.adapter); data->hpdev.adapter = NULL; @@ -270,10 +274,10 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv, if (item->inversed) mlxreg_hotplug_device_destroy(data); else - mlxreg_hotplug_device_create(priv->dev, data); + mlxreg_hotplug_device_create(priv, data); } else { if (item->inversed) - mlxreg_hotplug_device_create(priv->dev, data); + mlxreg_hotplug_device_create(priv, data); else mlxreg_hotplug_device_destroy(data); } @@ -319,7 +323,7 @@ mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data *priv, if (regval == MLXREG_HOTPLUG_HEALTH_MASK) { if ((data->health_cntr++ == MLXREG_HOTPLUG_RST_CNTR) || !priv->after_probe) { - mlxreg_hotplug_device_create(priv->dev, data); + mlxreg_hotplug_device_create(priv, data); data->attached = true; } } else { @@ -550,6 +554,7 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev) { struct mlxreg_core_hotplug_platform_data *pdata; struct mlxreg_hotplug_priv_data *priv; + struct i2c_adapter *deferred_adap; int err; pdata = dev_get_platdata(&pdev->dev); @@ -558,6 +563,12 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev) return -EINVAL; } + /* Defer probing if the necessary adapter is not configured yet. */ + deferred_adap = i2c_get_adapter(pdata->deferred_nr); + if (!deferred_adap) + return -EPROBE_DEFER; + i2c_put_adapter(deferred_adap); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 51ebc5a6053f..39d06dd1f63a 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -439,6 +439,7 @@ config SURFACE3_WMI config THINKPAD_ACPI tristate "ThinkPad ACPI Laptop Extras" depends on ACPI + depends on ACPI_BATTERY depends on INPUT depends on RFKILL || RFKILL = n depends on ACPI_VIDEO || ACPI_VIDEO = n @@ -756,6 +757,8 @@ config TOPSTAR_LAPTOP depends on ACPI depends on INPUT select INPUT_SPARSEKMAP + select LEDS_CLASS + select NEW_LEDS ---help--- This driver adds support for hotkeys found on Topstar laptops. @@ -1173,6 +1176,7 @@ config INTEL_TELEMETRY config MLX_PLATFORM tristate "Mellanox Technologies platform support" + depends on I2C && REGMAP ---help--- This option enables system support for the Mellanox Technologies platform. The Mellanox systems provide data center networking diff --git a/drivers/platform/x86/dell-smbios-base.c b/drivers/platform/x86/dell-smbios-base.c index 2485c80a9fdd..33fb2a20458a 100644 --- a/drivers/platform/x86/dell-smbios-base.c +++ b/drivers/platform/x86/dell-smbios-base.c @@ -514,7 +514,7 @@ static int build_tokens_sysfs(struct platform_device *dev) continue; loop_fail_create_value: - kfree(value_name); + kfree(location_name); goto out_unwind_strings; } smbios_attribute_group.attrs = token_attrs; @@ -525,7 +525,7 @@ loop_fail_create_value: return 0; out_unwind_strings: - for (i = i-1; i > 0; i--) { + while (i--) { kfree(token_location_attrs[i].attr.name); kfree(token_value_attrs[i].attr.name); } diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 5a681962899c..4c38904a8a32 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -492,7 +492,7 @@ static void eeepc_platform_exit(struct eeepc_laptop *eeepc) * potentially bad time, such as a timer interrupt. */ static void tpd_led_update(struct work_struct *work) - { +{ struct eeepc_laptop *eeepc; eeepc = container_of(work, struct eeepc_laptop, tpd_led_work); diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 2cfbd3fa5136..cd95b6f3a064 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -53,6 +53,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/acpi.h> +#include <linux/bitops.h> #include <linux/dmi.h> #include <linux/backlight.h> #include <linux/fb.h> @@ -61,12 +62,11 @@ #include <linux/kfifo.h> #include <linux/leds.h> #include <linux/platform_device.h> -#include <linux/slab.h> #include <acpi/video.h> -#define FUJITSU_DRIVER_VERSION "0.6.0" +#define FUJITSU_DRIVER_VERSION "0.6.0" -#define FUJITSU_LCD_N_LEVELS 8 +#define FUJITSU_LCD_N_LEVELS 8 #define ACPI_FUJITSU_CLASS "fujitsu" #define ACPI_FUJITSU_BL_HID "FUJ02B1" @@ -76,41 +76,51 @@ #define ACPI_FUJITSU_LAPTOP_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver" #define ACPI_FUJITSU_LAPTOP_DEVICE_NAME "Fujitsu FUJ02E3" -#define ACPI_FUJITSU_NOTIFY_CODE1 0x80 +#define ACPI_FUJITSU_NOTIFY_CODE 0x80 /* FUNC interface - command values */ -#define FUNC_FLAGS 0x1000 -#define FUNC_LEDS 0x1001 -#define FUNC_BUTTONS 0x1002 -#define FUNC_BACKLIGHT 0x1004 +#define FUNC_FLAGS BIT(12) +#define FUNC_LEDS (BIT(12) | BIT(0)) +#define FUNC_BUTTONS (BIT(12) | BIT(1)) +#define FUNC_BACKLIGHT (BIT(12) | BIT(2)) /* FUNC interface - responses */ -#define UNSUPPORTED_CMD 0x80000000 +#define UNSUPPORTED_CMD 0x80000000 /* FUNC interface - status flags */ -#define FLAG_RFKILL 0x020 -#define FLAG_LID 0x100 -#define FLAG_DOCK 0x200 +#define FLAG_RFKILL BIT(5) +#define FLAG_LID BIT(8) +#define FLAG_DOCK BIT(9) /* FUNC interface - LED control */ -#define FUNC_LED_OFF 0x1 -#define FUNC_LED_ON 0x30001 -#define KEYBOARD_LAMPS 0x100 -#define LOGOLAMP_POWERON 0x2000 -#define LOGOLAMP_ALWAYS 0x4000 -#define RADIO_LED_ON 0x20 -#define ECO_LED 0x10000 -#define ECO_LED_ON 0x80000 - -/* Hotkey details */ -#define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */ -#define KEY2_CODE 0x411 -#define KEY3_CODE 0x412 -#define KEY4_CODE 0x413 -#define KEY5_CODE 0x420 - -#define MAX_HOTKEY_RINGBUFFER_SIZE 100 -#define RINGBUFFERSIZE 40 +#define FUNC_LED_OFF BIT(0) +#define FUNC_LED_ON (BIT(0) | BIT(16) | BIT(17)) +#define LOGOLAMP_POWERON BIT(13) +#define LOGOLAMP_ALWAYS BIT(14) +#define KEYBOARD_LAMPS BIT(8) +#define RADIO_LED_ON BIT(5) +#define ECO_LED BIT(16) +#define ECO_LED_ON BIT(19) + +/* FUNC interface - backlight power control */ +#define BACKLIGHT_PARAM_POWER BIT(2) +#define BACKLIGHT_OFF (BIT(0) | BIT(1)) +#define BACKLIGHT_ON 0 + +/* Scancodes read from the GIRB register */ +#define KEY1_CODE 0x410 +#define KEY2_CODE 0x411 +#define KEY3_CODE 0x412 +#define KEY4_CODE 0x413 +#define KEY5_CODE 0x420 + +/* Hotkey ringbuffer limits */ +#define MAX_HOTKEY_RINGBUFFER_SIZE 100 +#define RINGBUFFERSIZE 40 + +/* Module parameters */ +static int use_alt_lcd_levels = -1; +static bool disable_brightness_adjust; /* Device controlling the backlight and associated keys */ struct fujitsu_bl { @@ -122,8 +132,6 @@ struct fujitsu_bl { }; static struct fujitsu_bl *fujitsu_bl; -static int use_alt_lcd_levels = -1; -static bool disable_brightness_adjust; /* Device used to access hotkeys and other features on the laptop */ struct fujitsu_laptop { @@ -256,9 +264,11 @@ static int bl_update_status(struct backlight_device *b) if (fext) { if (b->props.power == FB_BLANK_POWERDOWN) - call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x3); + call_fext_func(fext, FUNC_BACKLIGHT, 0x1, + BACKLIGHT_PARAM_POWER, BACKLIGHT_OFF); else - call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x0); + call_fext_func(fext, FUNC_BACKLIGHT, 0x1, + BACKLIGHT_PARAM_POWER, BACKLIGHT_ON); } return set_lcd_level(device, b->props.brightness); @@ -385,7 +395,7 @@ static int fujitsu_backlight_register(struct acpi_device *device) static int acpi_fujitsu_bl_add(struct acpi_device *device) { struct fujitsu_bl *priv; - int error; + int ret; if (acpi_video_get_backlight_type() != acpi_backlight_vendor) return -ENODEV; @@ -399,10 +409,6 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device) strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); device->driver_data = priv; - error = acpi_fujitsu_bl_input_setup(device); - if (error) - return error; - pr_info("ACPI: %s [%s]\n", acpi_device_name(device), acpi_device_bid(device)); @@ -410,11 +416,11 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device) priv->max_brightness = FUJITSU_LCD_N_LEVELS; get_lcd_level(device); - error = fujitsu_backlight_register(device); - if (error) - return error; + ret = acpi_fujitsu_bl_input_setup(device); + if (ret) + return ret; - return 0; + return fujitsu_backlight_register(device); } /* Brightness notify */ @@ -424,7 +430,7 @@ static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event) struct fujitsu_bl *priv = acpi_driver_data(device); int oldb, newb; - if (event != ACPI_FUJITSU_NOTIFY_CODE1) { + if (event != ACPI_FUJITSU_NOTIFY_CODE) { acpi_handle_info(device->handle, "unsupported event [0x%x]\n", event); sparse_keymap_report_event(priv->input, -1, 1, true); @@ -455,7 +461,9 @@ static const struct key_entry keymap_default[] = { { KE_KEY, KEY3_CODE, { KEY_PROG3 } }, { KE_KEY, KEY4_CODE, { KEY_PROG4 } }, { KE_KEY, KEY5_CODE, { KEY_RFKILL } }, + { KE_KEY, BIT(5), { KEY_RFKILL } }, { KE_KEY, BIT(26), { KEY_TOUCHPAD_TOGGLE } }, + { KE_KEY, BIT(29), { KEY_MICMUTE } }, { KE_END, 0 } }; @@ -693,7 +701,7 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) { struct fujitsu_laptop *priv = acpi_driver_data(device); struct led_classdev *led; - int result; + int ret; if (call_fext_func(device, FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { @@ -704,9 +712,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) led->name = "fujitsu::logolamp"; led->brightness_set_blocking = logolamp_set; led->brightness_get = logolamp_get; - result = devm_led_classdev_register(&device->dev, led); - if (result) - return result; + ret = devm_led_classdev_register(&device->dev, led); + if (ret) + return ret; } if ((call_fext_func(device, @@ -719,9 +727,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) led->name = "fujitsu::kblamps"; led->brightness_set_blocking = kblamps_set; led->brightness_get = kblamps_get; - result = devm_led_classdev_register(&device->dev, led); - if (result) - return result; + ret = devm_led_classdev_register(&device->dev, led); + if (ret) + return ret; } /* @@ -742,9 +750,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) led->brightness_set_blocking = radio_led_set; led->brightness_get = radio_led_get; led->default_trigger = "rfkill-any"; - result = devm_led_classdev_register(&device->dev, led); - if (result) - return result; + ret = devm_led_classdev_register(&device->dev, led); + if (ret) + return ret; } /* Support for eco led is not always signaled in bit corresponding @@ -762,9 +770,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) led->name = "fujitsu::eco_led"; led->brightness_set_blocking = eco_led_set; led->brightness_get = eco_led_get; - result = devm_led_classdev_register(&device->dev, led); - if (result) - return result; + ret = devm_led_classdev_register(&device->dev, led); + if (ret) + return ret; } return 0; @@ -773,8 +781,7 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) static int acpi_fujitsu_laptop_add(struct acpi_device *device) { struct fujitsu_laptop *priv; - int error; - int i; + int ret, i = 0; priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -789,23 +796,16 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) /* kfifo */ spin_lock_init(&priv->fifo_lock); - error = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int), - GFP_KERNEL); - if (error) { - pr_err("kfifo_alloc failed\n"); - goto err_stop; - } - - error = acpi_fujitsu_laptop_input_setup(device); - if (error) - goto err_free_fifo; + ret = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int), + GFP_KERNEL); + if (ret) + return ret; pr_info("ACPI: %s [%s]\n", acpi_device_name(device), acpi_device_bid(device)); - i = 0; - while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 - && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) + while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 && + i++ < MAX_HOTKEY_RINGBUFFER_SIZE) ; /* No action, result is discarded */ acpi_handle_debug(device->handle, "Discarded %i ringbuffer entries\n", i); @@ -829,26 +829,31 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) /* Sync backlight power status */ if (fujitsu_bl && fujitsu_bl->bl_device && acpi_video_get_backlight_type() == acpi_backlight_vendor) { - if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3) + if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2, + BACKLIGHT_PARAM_POWER, 0x0) == BACKLIGHT_OFF) fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN; else fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK; } - error = acpi_fujitsu_laptop_leds_register(device); - if (error) + ret = acpi_fujitsu_laptop_input_setup(device); + if (ret) + goto err_free_fifo; + + ret = acpi_fujitsu_laptop_leds_register(device); + if (ret) goto err_free_fifo; - error = fujitsu_laptop_platform_add(device); - if (error) + ret = fujitsu_laptop_platform_add(device); + if (ret) goto err_free_fifo; return 0; err_free_fifo: kfifo_free(&priv->fifo); -err_stop: - return error; + + return ret; } static int acpi_fujitsu_laptop_remove(struct acpi_device *device) @@ -865,11 +870,11 @@ static int acpi_fujitsu_laptop_remove(struct acpi_device *device) static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode) { struct fujitsu_laptop *priv = acpi_driver_data(device); - int status; + int ret; - status = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode, - sizeof(scancode), &priv->fifo_lock); - if (status != sizeof(scancode)) { + ret = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode, + sizeof(scancode), &priv->fifo_lock); + if (ret != sizeof(scancode)) { dev_info(&priv->input->dev, "Could not push scancode [0x%x]\n", scancode); return; @@ -882,13 +887,12 @@ static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode) static void acpi_fujitsu_laptop_release(struct acpi_device *device) { struct fujitsu_laptop *priv = acpi_driver_data(device); - int scancode, status; + int scancode, ret; while (true) { - status = kfifo_out_locked(&priv->fifo, - (unsigned char *)&scancode, - sizeof(scancode), &priv->fifo_lock); - if (status != sizeof(scancode)) + ret = kfifo_out_locked(&priv->fifo, (unsigned char *)&scancode, + sizeof(scancode), &priv->fifo_lock); + if (ret != sizeof(scancode)) return; sparse_keymap_report_event(priv->input, scancode, 0, false); dev_dbg(&priv->input->dev, @@ -899,10 +903,10 @@ static void acpi_fujitsu_laptop_release(struct acpi_device *device) static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event) { struct fujitsu_laptop *priv = acpi_driver_data(device); - int scancode, i = 0; + int scancode, i = 0, ret; unsigned int irb; - if (event != ACPI_FUJITSU_NOTIFY_CODE1) { + if (event != ACPI_FUJITSU_NOTIFY_CODE) { acpi_handle_info(device->handle, "Unsupported event [0x%x]\n", event); sparse_keymap_report_event(priv->input, -1, 1, true); @@ -930,9 +934,18 @@ static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event) * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is * handled in software; its state is queried using FUNC_FLAGS */ - if ((priv->flags_supported & BIT(26)) && - (call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26))) - sparse_keymap_report_event(priv->input, BIT(26), 1, true); + if (priv->flags_supported & (BIT(5) | BIT(26) | BIT(29))) { + ret = call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0); + if (ret & BIT(5)) + sparse_keymap_report_event(priv->input, + BIT(5), 1, true); + if (ret & BIT(26)) + sparse_keymap_report_event(priv->input, + BIT(26), 1, true); + if (ret & BIT(29)) + sparse_keymap_report_event(priv->input, + BIT(29), 1, true); + } } /* Initialization */ diff --git a/drivers/platform/x86/gpd-pocket-fan.c b/drivers/platform/x86/gpd-pocket-fan.c index 2d645c505f81..be85ed966bf3 100644 --- a/drivers/platform/x86/gpd-pocket-fan.c +++ b/drivers/platform/x86/gpd-pocket-fan.c @@ -19,12 +19,12 @@ static int temp_limits[3] = { 55000, 60000, 65000 }; module_param_array(temp_limits, int, NULL, 0444); MODULE_PARM_DESC(temp_limits, - "Milli-celcius values above which the fan speed increases"); + "Millicelsius values above which the fan speed increases"); static int hysteresis = 3000; module_param(hysteresis, int, 0444); MODULE_PARM_DESC(hysteresis, - "Hysteresis in milli-celcius before lowering the fan speed"); + "Hysteresis in millicelsius before lowering the fan speed"); static int speed_on_ac = 2; module_param(speed_on_ac, int, 0444); diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index 5e3df194723e..b5adba227783 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -16,16 +16,14 @@ * */ +#include <linux/acpi.h> +#include <linux/dmi.h> +#include <linux/input.h> +#include <linux/input/sparse-keymap.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> -#include <linux/input.h> #include <linux/platform_device.h> -#include <linux/input/sparse-keymap.h> -#include <linux/acpi.h> #include <linux/suspend.h> -#include <acpi/acpi_bus.h> -#include <linux/dmi.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Alex Hung"); @@ -67,8 +65,8 @@ static const struct key_entry intel_array_keymap[] = { { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* Release */ { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* Press */ { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* Release */ - { KE_SW, 0xC8, { .sw = { SW_ROTATE_LOCK, 1 } } }, /* Press */ - { KE_SW, 0xC9, { .sw = { SW_ROTATE_LOCK, 0 } } }, /* Release */ + { KE_KEY, 0xC8, { KEY_ROTATE_LOCK_TOGGLE } }, /* Press */ + { KE_IGNORE, 0xC9, { KEY_ROTATE_LOCK_TOGGLE } }, /* Release */ { KE_KEY, 0xCE, { KEY_POWER } }, /* Press */ { KE_IGNORE, 0xCF, { KEY_POWER } }, /* Release */ { KE_END }, diff --git a/drivers/platform/x86/intel_cht_int33fe.c b/drivers/platform/x86/intel_cht_int33fe.c index 380ef7ec094f..39d4100c60a2 100644 --- a/drivers/platform/x86/intel_cht_int33fe.c +++ b/drivers/platform/x86/intel_cht_int33fe.c @@ -33,6 +33,8 @@ struct cht_int33fe_data { struct i2c_client *max17047; struct i2c_client *fusb302; struct i2c_client *pi3usb30532; + /* Contain a list-head must be per device */ + struct device_connection connections[3]; }; /* @@ -172,6 +174,20 @@ static int cht_int33fe_probe(struct i2c_client *client) return -EPROBE_DEFER; /* Wait for i2c-adapter to load */ } + data->connections[0].endpoint[0] = "i2c-fusb302"; + data->connections[0].endpoint[1] = "i2c-pi3usb30532"; + data->connections[0].id = "typec-switch"; + data->connections[1].endpoint[0] = "i2c-fusb302"; + data->connections[1].endpoint[1] = "i2c-pi3usb30532"; + data->connections[1].id = "typec-mux"; + data->connections[2].endpoint[0] = "i2c-fusb302"; + data->connections[2].endpoint[1] = "intel_xhci_usb_sw-role-switch"; + data->connections[2].id = "usb-role-switch"; + + device_connection_add(&data->connections[0]); + device_connection_add(&data->connections[1]); + device_connection_add(&data->connections[2]); + memset(&board_info, 0, sizeof(board_info)); strlcpy(board_info.type, "typec_fusb302", I2C_NAME_SIZE); board_info.dev_name = "fusb302"; @@ -201,6 +217,10 @@ out_unregister_max17047: if (data->max17047) i2c_unregister_device(data->max17047); + device_connection_remove(&data->connections[2]); + device_connection_remove(&data->connections[1]); + device_connection_remove(&data->connections[0]); + return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */ } @@ -213,6 +233,10 @@ static int cht_int33fe_remove(struct i2c_client *i2c) if (data->max17047) i2c_unregister_device(data->max17047); + device_connection_remove(&data->connections[2]); + device_connection_remove(&data->connections[1]); + device_connection_remove(&data->connections[0]); + return 0; } diff --git a/drivers/platform/x86/intel_turbo_max_3.c b/drivers/platform/x86/intel_turbo_max_3.c index d4ea01805879..a6d5aa0c3c47 100644 --- a/drivers/platform/x86/intel_turbo_max_3.c +++ b/drivers/platform/x86/intel_turbo_max_3.c @@ -138,9 +138,6 @@ static int __init itmt_legacy_init(void) if (!id) return -ENODEV; - if (boot_cpu_has(X86_FEATURE_HWP)) - return -ENODEV; - ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "platform/x86/turbo_max_3:online", itmt_legacy_cpu_online, NULL); diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 454e14f02285..7a0bd24c1ae2 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -85,6 +85,15 @@ #define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0) #define MLXPLAT_CPLD_FAN_NG_MASK GENMASK(5, 0) +/* Default I2C parent bus number */ +#define MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR 1 + +/* Maximum number of possible physical buses equipped on system */ +#define MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM 16 + +/* Number of channels in group */ +#define MLXPLAT_CPLD_GRP_CHNL_NUM 8 + /* Start channel numbers */ #define MLXPLAT_CPLD_CH1 2 #define MLXPLAT_CPLD_CH2 10 @@ -124,7 +133,7 @@ static const struct resource mlxplat_lpc_resources[] = { }; /* Platform default channels */ -static const int mlxplat_default_channels[][8] = { +static const int mlxplat_default_channels[][MLXPLAT_CPLD_GRP_CHNL_NUM] = { { MLXPLAT_CPLD_CH1, MLXPLAT_CPLD_CH1 + 1, MLXPLAT_CPLD_CH1 + 2, MLXPLAT_CPLD_CH1 + 3, MLXPLAT_CPLD_CH1 + 4, MLXPLAT_CPLD_CH1 + @@ -694,6 +703,8 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) ARRAY_SIZE(mlxplat_default_channels[i]); } mlxplat_hotplug = &mlxplat_mlxcpld_default_data; + mlxplat_hotplug->deferred_nr = + mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; return 1; }; @@ -708,6 +719,8 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) ARRAY_SIZE(mlxplat_msn21xx_channels); } mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data; + mlxplat_hotplug->deferred_nr = + mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; return 1; }; @@ -722,6 +735,8 @@ static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi) ARRAY_SIZE(mlxplat_msn21xx_channels); } mlxplat_hotplug = &mlxplat_mlxcpld_msn274x_data; + mlxplat_hotplug->deferred_nr = + mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; return 1; }; @@ -736,6 +751,8 @@ static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi) ARRAY_SIZE(mlxplat_msn21xx_channels); } mlxplat_hotplug = &mlxplat_mlxcpld_msn201x_data; + mlxplat_hotplug->deferred_nr = + mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; return 1; }; @@ -750,6 +767,8 @@ static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi) ARRAY_SIZE(mlxplat_msn21xx_channels); } mlxplat_hotplug = &mlxplat_mlxcpld_default_ng_data; + mlxplat_hotplug->deferred_nr = + mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; return 1; }; @@ -830,10 +849,48 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { MODULE_DEVICE_TABLE(dmi, mlxplat_dmi_table); +static int mlxplat_mlxcpld_verify_bus_topology(int *nr) +{ + struct i2c_adapter *search_adap; + int shift, i; + + /* Scan adapters from expected id to verify it is free. */ + *nr = MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR; + for (i = MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR; i < + MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM; i++) { + search_adap = i2c_get_adapter(i); + if (search_adap) { + i2c_put_adapter(search_adap); + continue; + } + + /* Return if expected parent adapter is free. */ + if (i == MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR) + return 0; + break; + } + + /* Return with error if free id for adapter is not found. */ + if (i == MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM) + return -ENODEV; + + /* Shift adapter ids, since expected parent adapter is not free. */ + *nr = i; + for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) { + shift = *nr - mlxplat_mux_data[i].parent; + mlxplat_mux_data[i].parent = *nr; + mlxplat_mux_data[i].base_nr += shift; + if (shift > 0) + mlxplat_hotplug->shift_nr = shift; + } + + return 0; +} + static int __init mlxplat_init(void) { struct mlxplat_priv *priv; - int i, err; + int i, nr, err; if (!dmi_check_system(mlxplat_dmi_table)) return -ENODEV; @@ -853,7 +910,12 @@ static int __init mlxplat_init(void) } platform_set_drvdata(mlxplat_dev, priv); - priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", -1, + err = mlxplat_mlxcpld_verify_bus_topology(&nr); + if (nr < 0) + goto fail_alloc; + + nr = (nr == MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM) ? -1 : nr; + priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", nr, NULL, 0); if (IS_ERR(priv->pdev_i2c)) { err = PTR_ERR(priv->pdev_i2c); diff --git a/drivers/platform/x86/silead_dmi.c b/drivers/platform/x86/silead_dmi.c index 3a624090191d..452aacabaa8e 100644 --- a/drivers/platform/x86/silead_dmi.c +++ b/drivers/platform/x86/silead_dmi.c @@ -446,6 +446,23 @@ static const struct dmi_system_id silead_ts_dmi_table[] = { DMI_MATCH(DMI_BOARD_NAME, "X3 Plus"), }, }, + { + /* I.T.Works TW701 */ + .driver_data = (void *)&surftab_wintron70_st70416_6_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_MATCH(DMI_PRODUCT_NAME, "i71c"), + DMI_MATCH(DMI_BIOS_VERSION, "itWORKS.G.WI71C.JGBMRB"), + }, + }, + { + /* Yours Y8W81, same case and touchscreen as Chuwi Vi8 */ + .driver_data = (void *)&chuwi_vi8_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "YOURS"), + DMI_MATCH(DMI_PRODUCT_NAME, "Y8W81"), + }, + }, { }, }; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index d5eaf3b1edba..da1ca4856ea1 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -23,7 +23,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define TPACPI_VERSION "0.25" +#define TPACPI_VERSION "0.26" #define TPACPI_SYSFS_VERSION 0x030000 /* @@ -66,6 +66,7 @@ #include <linux/seq_file.h> #include <linux/sysfs.h> #include <linux/backlight.h> +#include <linux/bitops.h> #include <linux/fb.h> #include <linux/platform_device.h> #include <linux/hwmon.h> @@ -78,11 +79,13 @@ #include <linux/workqueue.h> #include <linux/acpi.h> #include <linux/pci_ids.h> +#include <linux/power_supply.h> #include <linux/thinkpad_acpi.h> #include <sound/core.h> #include <sound/control.h> #include <sound/initval.h> #include <linux/uaccess.h> +#include <acpi/battery.h> #include <acpi/video.h> /* ThinkPad CMOS commands */ @@ -335,6 +338,7 @@ static struct { u32 sensors_pdev_attrs_registered:1; u32 hotkey_poll_active:1; u32 has_adaptive_kbd:1; + u32 battery:1; } tp_features; static struct { @@ -8699,16 +8703,24 @@ static const struct attribute_group fan_attr_group = { .ec = TPID(__id1, __id2), \ .quirks = __quirks } +#define TPACPI_FAN_QB(__id1, __id2, __quirks) \ + { .vendor = PCI_VENDOR_ID_LENOVO, \ + .bios = TPID(__id1, __id2), \ + .ec = TPACPI_MATCH_ANY, \ + .quirks = __quirks } + static const struct tpacpi_quirk fan_quirk_table[] __initconst = { TPACPI_FAN_QI('1', 'Y', TPACPI_FAN_Q1), TPACPI_FAN_QI('7', '8', TPACPI_FAN_Q1), TPACPI_FAN_QI('7', '6', TPACPI_FAN_Q1), TPACPI_FAN_QI('7', '0', TPACPI_FAN_Q1), TPACPI_FAN_QL('7', 'M', TPACPI_FAN_2FAN), + TPACPI_FAN_QB('N', '1', TPACPI_FAN_2FAN), }; #undef TPACPI_FAN_QL #undef TPACPI_FAN_QI +#undef TPACPI_FAN_QB static int __init fan_init(struct ibm_init_struct *iibm) { @@ -9209,6 +9221,385 @@ static struct ibm_struct mute_led_driver_data = { .resume = mute_led_resume, }; +/* + * Battery Wear Control Driver + * Contact: Ognjen Galic <smclt30p@gmail.com> + */ + +/* Metadata */ + +#define GET_START "BCTG" +#define SET_START "BCCS" +#define GET_STOP "BCSG" +#define SET_STOP "BCSS" + +#define START_ATTR "charge_start_threshold" +#define STOP_ATTR "charge_stop_threshold" + +enum { + BAT_ANY = 0, + BAT_PRIMARY = 1, + BAT_SECONDARY = 2 +}; + +enum { + /* Error condition bit */ + METHOD_ERR = BIT(31), +}; + +enum { + /* This is used in the get/set helpers */ + THRESHOLD_START, + THRESHOLD_STOP, +}; + +struct tpacpi_battery_data { + int charge_start; + int start_support; + int charge_stop; + int stop_support; +}; + +struct tpacpi_battery_driver_data { + struct tpacpi_battery_data batteries[3]; + int individual_addressing; +}; + +static struct tpacpi_battery_driver_data battery_info; + +/* ACPI helpers/functions/probes */ + +/** + * This evaluates a ACPI method call specific to the battery + * ACPI extension. The specifics are that an error is marked + * in the 32rd bit of the response, so we just check that here. + */ +static acpi_status tpacpi_battery_acpi_eval(char *method, int *ret, int param) +{ + int response; + + if (!acpi_evalf(hkey_handle, &response, method, "dd", param)) { + acpi_handle_err(hkey_handle, "%s: evaluate failed", method); + return AE_ERROR; + } + if (response & METHOD_ERR) { + acpi_handle_err(hkey_handle, + "%s evaluated but flagged as error", method); + return AE_ERROR; + } + *ret = response; + return AE_OK; +} + +static int tpacpi_battery_get(int what, int battery, int *ret) +{ + switch (what) { + case THRESHOLD_START: + if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_START, ret, battery)) + return -ENODEV; + + /* The value is in the low 8 bits of the response */ + *ret = *ret & 0xFF; + return 0; + case THRESHOLD_STOP: + if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_STOP, ret, battery)) + return -ENODEV; + /* Value is in lower 8 bits */ + *ret = *ret & 0xFF; + /* + * On the stop value, if we return 0 that + * does not make any sense. 0 means Default, which + * means that charging stops at 100%, so we return + * that. + */ + if (*ret == 0) + *ret = 100; + return 0; + default: + pr_crit("wrong parameter: %d", what); + return -EINVAL; + } +} + +static int tpacpi_battery_set(int what, int battery, int value) +{ + int param, ret; + /* The first 8 bits are the value of the threshold */ + param = value; + /* The battery ID is in bits 8-9, 2 bits */ + param |= battery << 8; + + switch (what) { + case THRESHOLD_START: + if ACPI_FAILURE(tpacpi_battery_acpi_eval(SET_START, &ret, param)) { + pr_err("failed to set charge threshold on battery %d", + battery); + return -ENODEV; + } + return 0; + case THRESHOLD_STOP: + if ACPI_FAILURE(tpacpi_battery_acpi_eval(SET_STOP, &ret, param)) { + pr_err("failed to set stop threshold: %d", battery); + return -ENODEV; + } + return 0; + default: + pr_crit("wrong parameter: %d", what); + return -EINVAL; + } +} + +static int tpacpi_battery_probe(int battery) +{ + int ret = 0; + + memset(&battery_info, 0, sizeof(struct tpacpi_battery_driver_data)); + /* + * 1) Get the current start threshold + * 2) Check for support + * 3) Get the current stop threshold + * 4) Check for support + */ + if (acpi_has_method(hkey_handle, GET_START)) { + if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_START, &ret, battery)) { + pr_err("Error probing battery %d\n", battery); + return -ENODEV; + } + /* Individual addressing is in bit 9 */ + if (ret & BIT(9)) + battery_info.individual_addressing = true; + /* Support is marked in bit 8 */ + if (ret & BIT(8)) + battery_info.batteries[battery].start_support = 1; + else + return -ENODEV; + if (tpacpi_battery_get(THRESHOLD_START, battery, + &battery_info.batteries[battery].charge_start)) { + pr_err("Error probing battery %d\n", battery); + return -ENODEV; + } + } + if (acpi_has_method(hkey_handle, GET_STOP)) { + if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_STOP, &ret, battery)) { + pr_err("Error probing battery stop; %d\n", battery); + return -ENODEV; + } + /* Support is marked in bit 8 */ + if (ret & BIT(8)) + battery_info.batteries[battery].stop_support = 1; + else + return -ENODEV; + if (tpacpi_battery_get(THRESHOLD_STOP, battery, + &battery_info.batteries[battery].charge_stop)) { + pr_err("Error probing battery stop: %d\n", battery); + return -ENODEV; + } + } + pr_info("battery %d registered (start %d, stop %d)", + battery, + battery_info.batteries[battery].charge_start, + battery_info.batteries[battery].charge_stop); + + return 0; +} + +/* General helper functions */ + +static int tpacpi_battery_get_id(const char *battery_name) +{ + + if (strcmp(battery_name, "BAT0") == 0) + return BAT_PRIMARY; + if (strcmp(battery_name, "BAT1") == 0) + return BAT_SECONDARY; + /* + * If for some reason the battery is not BAT0 nor is it + * BAT1, we will assume it's the default, first battery, + * AKA primary. + */ + pr_warn("unknown battery %s, assuming primary", battery_name); + return BAT_PRIMARY; +} + +/* sysfs interface */ + +static ssize_t tpacpi_battery_store(int what, + struct device *dev, + const char *buf, size_t count) +{ + struct power_supply *supply = to_power_supply(dev); + unsigned long value; + int battery, rval; + /* + * Some systems have support for more than + * one battery. If that is the case, + * tpacpi_battery_probe marked that addressing + * them individually is supported, so we do that + * based on the device struct. + * + * On systems that are not supported, we assume + * the primary as most of the ACPI calls fail + * with "Any Battery" as the parameter. + */ + if (battery_info.individual_addressing) + /* BAT_PRIMARY or BAT_SECONDARY */ + battery = tpacpi_battery_get_id(supply->desc->name); + else + battery = BAT_PRIMARY; + + rval = kstrtoul(buf, 10, &value); + if (rval) + return rval; + + switch (what) { + case THRESHOLD_START: + if (!battery_info.batteries[battery].start_support) + return -ENODEV; + /* valid values are [0, 99] */ + if (value < 0 || value > 99) + return -EINVAL; + if (value > battery_info.batteries[battery].charge_stop) + return -EINVAL; + if (tpacpi_battery_set(THRESHOLD_START, battery, value)) + return -ENODEV; + battery_info.batteries[battery].charge_start = value; + return count; + + case THRESHOLD_STOP: + if (!battery_info.batteries[battery].stop_support) + return -ENODEV; + /* valid values are [1, 100] */ + if (value < 1 || value > 100) + return -EINVAL; + if (value < battery_info.batteries[battery].charge_start) + return -EINVAL; + battery_info.batteries[battery].charge_stop = value; + /* + * When 100 is passed to stop, we need to flip + * it to 0 as that the EC understands that as + * "Default", which will charge to 100% + */ + if (value == 100) + value = 0; + if (tpacpi_battery_set(THRESHOLD_STOP, battery, value)) + return -EINVAL; + return count; + default: + pr_crit("Wrong parameter: %d", what); + return -EINVAL; + } + return count; +} + +static ssize_t tpacpi_battery_show(int what, + struct device *dev, + char *buf) +{ + struct power_supply *supply = to_power_supply(dev); + int ret, battery; + /* + * Some systems have support for more than + * one battery. If that is the case, + * tpacpi_battery_probe marked that addressing + * them individually is supported, so we; + * based on the device struct. + * + * On systems that are not supported, we assume + * the primary as most of the ACPI calls fail + * with "Any Battery" as the parameter. + */ + if (battery_info.individual_addressing) + /* BAT_PRIMARY or BAT_SECONDARY */ + battery = tpacpi_battery_get_id(supply->desc->name); + else + battery = BAT_PRIMARY; + if (tpacpi_battery_get(what, battery, &ret)) + return -ENODEV; + return sprintf(buf, "%d\n", ret); +} + +static ssize_t charge_start_threshold_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + return tpacpi_battery_show(THRESHOLD_START, device, buf); +} + +static ssize_t charge_stop_threshold_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + return tpacpi_battery_show(THRESHOLD_STOP, device, buf); +} + +static ssize_t charge_start_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return tpacpi_battery_store(THRESHOLD_START, dev, buf, count); +} + +static ssize_t charge_stop_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return tpacpi_battery_store(THRESHOLD_STOP, dev, buf, count); +} + +static DEVICE_ATTR_RW(charge_start_threshold); +static DEVICE_ATTR_RW(charge_stop_threshold); + +static struct attribute *tpacpi_battery_attrs[] = { + &dev_attr_charge_start_threshold.attr, + &dev_attr_charge_stop_threshold.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(tpacpi_battery); + +/* ACPI battery hooking */ + +static int tpacpi_battery_add(struct power_supply *battery) +{ + int batteryid = tpacpi_battery_get_id(battery->desc->name); + + if (tpacpi_battery_probe(batteryid)) + return -ENODEV; + if (device_add_groups(&battery->dev, tpacpi_battery_groups)) + return -ENODEV; + return 0; +} + +static int tpacpi_battery_remove(struct power_supply *battery) +{ + device_remove_groups(&battery->dev, tpacpi_battery_groups); + return 0; +} + +static struct acpi_battery_hook battery_hook = { + .add_battery = tpacpi_battery_add, + .remove_battery = tpacpi_battery_remove, + .name = "ThinkPad Battery Extension", +}; + +/* Subdriver init/exit */ + +static int __init tpacpi_battery_init(struct ibm_init_struct *ibm) +{ + battery_hook_register(&battery_hook); + return 0; +} + +static void tpacpi_battery_exit(void) +{ + battery_hook_unregister(&battery_hook); +} + +static struct ibm_struct battery_driver_data = { + .name = "battery", + .exit = tpacpi_battery_exit, +}; + /**************************************************************************** **************************************************************************** * @@ -9655,6 +10046,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { .init = mute_led_init, .data = &mute_led_driver_data, }, + { + .init = tpacpi_battery_init, + .data = &battery_driver_data, + }, }; static int __init set_ibm_param(const char *val, const struct kernel_param *kp) diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index 1032c00b907b..f7761d98c0fd 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -1,14 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * ACPI driver for Topstar notebooks (hotkeys support only) + * Topstar Laptop ACPI Extras driver * * Copyright (c) 2009 Herton Ronaldo Krzesinski <herton@mandriva.com.br> + * Copyright (c) 2018 Guillaume Douézan-Grard * * Implementation inspired by existing x86 platform drivers, in special - * asus/eepc/fujitsu-laptop, thanks to their authors - * - * 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. + * asus/eepc/fujitsu-laptop, thanks to their authors. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -18,15 +16,93 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/acpi.h> +#include <linux/dmi.h> #include <linux/input.h> #include <linux/input/sparse-keymap.h> +#include <linux/leds.h> +#include <linux/platform_device.h> -#define ACPI_TOPSTAR_CLASS "topstar" +#define TOPSTAR_LAPTOP_CLASS "topstar" -struct topstar_hkey { - struct input_dev *inputdev; +struct topstar_laptop { + struct acpi_device *device; + struct platform_device *platform; + struct input_dev *input; + struct led_classdev led; }; +/* + * LED + */ + +static enum led_brightness topstar_led_get(struct led_classdev *led) +{ + return led->brightness; +} + +static int topstar_led_set(struct led_classdev *led, + enum led_brightness state) +{ + struct topstar_laptop *topstar = container_of(led, + struct topstar_laptop, led); + + struct acpi_object_list params; + union acpi_object in_obj; + unsigned long long int ret; + acpi_status status; + + params.count = 1; + params.pointer = &in_obj; + in_obj.type = ACPI_TYPE_INTEGER; + in_obj.integer.value = 0x83; + + /* + * Topstar ACPI returns 0x30001 when the LED is ON and 0x30000 when it + * is OFF. + */ + status = acpi_evaluate_integer(topstar->device->handle, + "GETX", ¶ms, &ret); + if (ACPI_FAILURE(status)) + return -1; + + /* + * FNCX(0x83) toggles the LED (more precisely, it is supposed to + * act as an hardware switch and disconnect the WLAN adapter but + * it seems to be faulty on some models like the Topstar U931 + * Notebook). + */ + if ((ret == 0x30001 && state == LED_OFF) + || (ret == 0x30000 && state != LED_OFF)) { + status = acpi_execute_simple_method(topstar->device->handle, + "FNCX", 0x83); + if (ACPI_FAILURE(status)) + return -1; + } + + return 0; +} + +static int topstar_led_init(struct topstar_laptop *topstar) +{ + topstar->led = (struct led_classdev) { + .default_trigger = "rfkill0", + .brightness_get = topstar_led_get, + .brightness_set_blocking = topstar_led_set, + .name = TOPSTAR_LAPTOP_CLASS "::wlan", + }; + + return led_classdev_register(&topstar->platform->dev, &topstar->led); +} + +static void topstar_led_exit(struct topstar_laptop *topstar) +{ + led_classdev_unregister(&topstar->led); +} + +/* + * Input + */ + static const struct key_entry topstar_keymap[] = { { KE_KEY, 0x80, { KEY_BRIGHTNESSUP } }, { KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } }, @@ -57,107 +133,217 @@ static const struct key_entry topstar_keymap[] = { { KE_END, 0 } }; -static void acpi_topstar_notify(struct acpi_device *device, u32 event) +static void topstar_input_notify(struct topstar_laptop *topstar, int event) { - static bool dup_evnt[2]; - bool *dup; - struct topstar_hkey *hkey = acpi_driver_data(device); - - /* 0x83 and 0x84 key events comes duplicated... */ - if (event == 0x83 || event == 0x84) { - dup = &dup_evnt[event - 0x83]; - if (*dup) { - *dup = false; - return; - } - *dup = true; - } - - if (!sparse_keymap_report_event(hkey->inputdev, event, 1, true)) + if (!sparse_keymap_report_event(topstar->input, event, 1, true)) pr_info("unknown event = 0x%02x\n", event); } -static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) -{ - acpi_status status; - - status = acpi_execute_simple_method(device->handle, "FNCX", - state ? 0x86 : 0x87); - if (ACPI_FAILURE(status)) { - pr_err("Unable to switch FNCX notifications\n"); - return -ENODEV; - } - - return 0; -} - -static int acpi_topstar_init_hkey(struct topstar_hkey *hkey) +static int topstar_input_init(struct topstar_laptop *topstar) { struct input_dev *input; - int error; + int err; input = input_allocate_device(); if (!input) return -ENOMEM; input->name = "Topstar Laptop extra buttons"; - input->phys = "topstar/input0"; + input->phys = TOPSTAR_LAPTOP_CLASS "/input0"; input->id.bustype = BUS_HOST; + input->dev.parent = &topstar->platform->dev; - error = sparse_keymap_setup(input, topstar_keymap, NULL); - if (error) { + err = sparse_keymap_setup(input, topstar_keymap, NULL); + if (err) { pr_err("Unable to setup input device keymap\n"); goto err_free_dev; } - error = input_register_device(input); - if (error) { + err = input_register_device(input); + if (err) { pr_err("Unable to register input device\n"); goto err_free_dev; } - hkey->inputdev = input; + topstar->input = input; return 0; - err_free_dev: +err_free_dev: input_free_device(input); - return error; + return err; } -static int acpi_topstar_add(struct acpi_device *device) +static void topstar_input_exit(struct topstar_laptop *topstar) { - struct topstar_hkey *tps_hkey; + input_unregister_device(topstar->input); +} - tps_hkey = kzalloc(sizeof(struct topstar_hkey), GFP_KERNEL); - if (!tps_hkey) +/* + * Platform + */ + +static struct platform_driver topstar_platform_driver = { + .driver = { + .name = TOPSTAR_LAPTOP_CLASS, + }, +}; + +static int topstar_platform_init(struct topstar_laptop *topstar) +{ + int err; + + topstar->platform = platform_device_alloc(TOPSTAR_LAPTOP_CLASS, -1); + if (!topstar->platform) return -ENOMEM; - strcpy(acpi_device_name(device), "Topstar TPSACPI"); - strcpy(acpi_device_class(device), ACPI_TOPSTAR_CLASS); + platform_set_drvdata(topstar->platform, topstar); + + err = platform_device_add(topstar->platform); + if (err) + goto err_device_put; + + return 0; - if (acpi_topstar_fncx_switch(device, true)) - goto add_err; +err_device_put: + platform_device_put(topstar->platform); + return err; +} + +static void topstar_platform_exit(struct topstar_laptop *topstar) +{ + platform_device_unregister(topstar->platform); +} + +/* + * ACPI + */ + +static int topstar_acpi_fncx_switch(struct acpi_device *device, bool state) +{ + acpi_status status; + u64 arg = state ? 0x86 : 0x87; - if (acpi_topstar_init_hkey(tps_hkey)) - goto add_err; + status = acpi_execute_simple_method(device->handle, "FNCX", arg); + if (ACPI_FAILURE(status)) { + pr_err("Unable to switch FNCX notifications\n"); + return -ENODEV; + } - device->driver_data = tps_hkey; return 0; +} -add_err: - kfree(tps_hkey); - return -ENODEV; +static void topstar_acpi_notify(struct acpi_device *device, u32 event) +{ + struct topstar_laptop *topstar = acpi_driver_data(device); + static bool dup_evnt[2]; + bool *dup; + + /* 0x83 and 0x84 key events comes duplicated... */ + if (event == 0x83 || event == 0x84) { + dup = &dup_evnt[event - 0x83]; + if (*dup) { + *dup = false; + return; + } + *dup = true; + } + + topstar_input_notify(topstar, event); } -static int acpi_topstar_remove(struct acpi_device *device) +static int topstar_acpi_init(struct topstar_laptop *topstar) { - struct topstar_hkey *tps_hkey = acpi_driver_data(device); + return topstar_acpi_fncx_switch(topstar->device, true); +} - acpi_topstar_fncx_switch(device, false); +static void topstar_acpi_exit(struct topstar_laptop *topstar) +{ + topstar_acpi_fncx_switch(topstar->device, false); +} - input_unregister_device(tps_hkey->inputdev); - kfree(tps_hkey); +/* + * Enable software-based WLAN LED control on systems with defective + * hardware switch. + */ +static bool led_workaround; +static int dmi_led_workaround(const struct dmi_system_id *id) +{ + led_workaround = true; + return 0; +} + +static const struct dmi_system_id topstar_dmi_ids[] = { + { + .callback = dmi_led_workaround, + .ident = "Topstar U931/RVP7", + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "U931"), + DMI_MATCH(DMI_BOARD_VERSION, "RVP7"), + }, + }, + {} +}; + +static int topstar_acpi_add(struct acpi_device *device) +{ + struct topstar_laptop *topstar; + int err; + + dmi_check_system(topstar_dmi_ids); + + topstar = kzalloc(sizeof(struct topstar_laptop), GFP_KERNEL); + if (!topstar) + return -ENOMEM; + + strcpy(acpi_device_name(device), "Topstar TPSACPI"); + strcpy(acpi_device_class(device), TOPSTAR_LAPTOP_CLASS); + device->driver_data = topstar; + topstar->device = device; + + err = topstar_acpi_init(topstar); + if (err) + goto err_free; + + err = topstar_platform_init(topstar); + if (err) + goto err_acpi_exit; + + err = topstar_input_init(topstar); + if (err) + goto err_platform_exit; + + if (led_workaround) { + err = topstar_led_init(topstar); + if (err) + goto err_input_exit; + } + + return 0; + +err_input_exit: + topstar_input_exit(topstar); +err_platform_exit: + topstar_platform_exit(topstar); +err_acpi_exit: + topstar_acpi_exit(topstar); +err_free: + kfree(topstar); + return err; +} + +static int topstar_acpi_remove(struct acpi_device *device) +{ + struct topstar_laptop *topstar = acpi_driver_data(device); + + if (led_workaround) + topstar_led_exit(topstar); + + topstar_input_exit(topstar); + topstar_platform_exit(topstar); + topstar_acpi_exit(topstar); + + kfree(topstar); return 0; } @@ -168,18 +354,47 @@ static const struct acpi_device_id topstar_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, topstar_device_ids); -static struct acpi_driver acpi_topstar_driver = { +static struct acpi_driver topstar_acpi_driver = { .name = "Topstar laptop ACPI driver", - .class = ACPI_TOPSTAR_CLASS, + .class = TOPSTAR_LAPTOP_CLASS, .ids = topstar_device_ids, .ops = { - .add = acpi_topstar_add, - .remove = acpi_topstar_remove, - .notify = acpi_topstar_notify, + .add = topstar_acpi_add, + .remove = topstar_acpi_remove, + .notify = topstar_acpi_notify, }, }; -module_acpi_driver(acpi_topstar_driver); + +static int __init topstar_laptop_init(void) +{ + int ret; + + ret = platform_driver_register(&topstar_platform_driver); + if (ret < 0) + return ret; + + ret = acpi_bus_register_driver(&topstar_acpi_driver); + if (ret < 0) + goto err_driver_unreg; + + pr_info("ACPI extras driver loaded\n"); + return 0; + +err_driver_unreg: + platform_driver_unregister(&topstar_platform_driver); + return ret; +} + +static void __exit topstar_laptop_exit(void) +{ + acpi_bus_unregister_driver(&topstar_acpi_driver); + platform_driver_unregister(&topstar_platform_driver); +} + +module_init(topstar_laptop_init); +module_exit(topstar_laptop_exit); MODULE_AUTHOR("Herton Ronaldo Krzesinski"); +MODULE_AUTHOR("Guillaume Douézan-Grard"); MODULE_DESCRIPTION("Topstar Laptop ACPI Extras driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 8796211ef24a..8e3d0146ff8c 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -130,13 +130,11 @@ static bool find_guid(const char *guid_string, struct wmi_block **out) uuid_le guid_input; struct wmi_block *wblock; struct guid_block *block; - struct list_head *p; if (uuid_le_to_bin(guid_string, &guid_input)) return false; - list_for_each(p, &wmi_block_list) { - wblock = list_entry(p, struct wmi_block, list); + list_for_each_entry(wblock, &wmi_block_list, list) { block = &wblock->gblock; if (memcmp(block->guid, &guid_input, 16) == 0) { @@ -519,7 +517,6 @@ wmi_notify_handler handler, void *data) struct wmi_block *block; acpi_status status = AE_NOT_EXIST; uuid_le guid_input; - struct list_head *p; if (!guid || !handler) return AE_BAD_PARAMETER; @@ -527,9 +524,8 @@ wmi_notify_handler handler, void *data) if (uuid_le_to_bin(guid, &guid_input)) return AE_BAD_PARAMETER; - list_for_each(p, &wmi_block_list) { + list_for_each_entry(block, &wmi_block_list, list) { acpi_status wmi_status; - block = list_entry(p, struct wmi_block, list); if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { if (block->handler && @@ -560,7 +556,6 @@ acpi_status wmi_remove_notify_handler(const char *guid) struct wmi_block *block; acpi_status status = AE_NOT_EXIST; uuid_le guid_input; - struct list_head *p; if (!guid) return AE_BAD_PARAMETER; @@ -568,9 +563,8 @@ acpi_status wmi_remove_notify_handler(const char *guid) if (uuid_le_to_bin(guid, &guid_input)) return AE_BAD_PARAMETER; - list_for_each(p, &wmi_block_list) { + list_for_each_entry(block, &wmi_block_list, list) { acpi_status wmi_status; - block = list_entry(p, struct wmi_block, list); if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { if (!block->handler || @@ -610,15 +604,13 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) union acpi_object params[1]; struct guid_block *gblock; struct wmi_block *wblock; - struct list_head *p; input.count = 1; input.pointer = params; params[0].type = ACPI_TYPE_INTEGER; params[0].integer.value = event; - list_for_each(p, &wmi_block_list) { - wblock = list_entry(p, struct wmi_block, list); + list_for_each_entry(wblock, &wmi_block_list, list) { gblock = &wblock->gblock; if ((gblock->flags & ACPI_WMI_EVENT) && @@ -933,12 +925,11 @@ static int wmi_dev_probe(struct device *dev) goto probe_failure; } - buf = kmalloc(strlen(wdriver->driver.name) + 5, GFP_KERNEL); + buf = kasprintf(GFP_KERNEL, "wmi/%s", wdriver->driver.name); if (!buf) { ret = -ENOMEM; goto probe_string_failure; } - sprintf(buf, "wmi/%s", wdriver->driver.name); wblock->char_dev.minor = MISC_DYNAMIC_MINOR; wblock->char_dev.name = buf; wblock->char_dev.fops = &wmi_fops; @@ -1261,11 +1252,9 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, { struct guid_block *block; struct wmi_block *wblock; - struct list_head *p; bool found_it = false; - list_for_each(p, &wmi_block_list) { - wblock = list_entry(p, struct wmi_block, list); + list_for_each_entry(wblock, &wmi_block_list, list) { block = &wblock->gblock; if (wblock->acpi_device->handle == handle && |