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 |
4 files changed, 648 insertions, 481 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); |