Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.10-rc6 207 lines 5.2 kB view raw
1/* 2 * ChromeOS EC multi-function device 3 * 4 * Copyright (C) 2012 Google, Inc 5 * 6 * This software is licensed under the terms of the GNU General Public 7 * License version 2, as published by the Free Software Foundation, and 8 * may be copied, distributed, and modified under those terms. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * The ChromeOS EC multi function device is used to mux all the requests 16 * to the EC device for its multiple features: keyboard controller, 17 * battery charging and regulator control, firmware update. 18 */ 19 20#include <linux/of_platform.h> 21#include <linux/interrupt.h> 22#include <linux/slab.h> 23#include <linux/module.h> 24#include <linux/mfd/core.h> 25#include <linux/mfd/cros_ec.h> 26#include <asm/unaligned.h> 27 28#define CROS_EC_DEV_EC_INDEX 0 29#define CROS_EC_DEV_PD_INDEX 1 30 31static struct cros_ec_platform ec_p = { 32 .ec_name = CROS_EC_DEV_NAME, 33 .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_EC_INDEX), 34}; 35 36static struct cros_ec_platform pd_p = { 37 .ec_name = CROS_EC_DEV_PD_NAME, 38 .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX), 39}; 40 41static const struct mfd_cell ec_cell = { 42 .name = "cros-ec-ctl", 43 .platform_data = &ec_p, 44 .pdata_size = sizeof(ec_p), 45}; 46 47static const struct mfd_cell ec_pd_cell = { 48 .name = "cros-ec-ctl", 49 .platform_data = &pd_p, 50 .pdata_size = sizeof(pd_p), 51}; 52 53static irqreturn_t ec_irq_thread(int irq, void *data) 54{ 55 struct cros_ec_device *ec_dev = data; 56 int ret; 57 58 if (device_may_wakeup(ec_dev->dev)) 59 pm_wakeup_event(ec_dev->dev, 0); 60 61 ret = cros_ec_get_next_event(ec_dev); 62 if (ret > 0) 63 blocking_notifier_call_chain(&ec_dev->event_notifier, 64 0, ec_dev); 65 return IRQ_HANDLED; 66} 67 68int cros_ec_register(struct cros_ec_device *ec_dev) 69{ 70 struct device *dev = ec_dev->dev; 71 int err = 0; 72 73 BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier); 74 75 ec_dev->max_request = sizeof(struct ec_params_hello); 76 ec_dev->max_response = sizeof(struct ec_response_get_protocol_info); 77 ec_dev->max_passthru = 0; 78 79 ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); 80 if (!ec_dev->din) 81 return -ENOMEM; 82 83 ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL); 84 if (!ec_dev->dout) 85 return -ENOMEM; 86 87 mutex_init(&ec_dev->lock); 88 89 cros_ec_query_all(ec_dev); 90 91 if (ec_dev->irq) { 92 err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread, 93 IRQF_TRIGGER_LOW | IRQF_ONESHOT, 94 "chromeos-ec", ec_dev); 95 if (err) { 96 dev_err(dev, "Failed to request IRQ %d: %d", 97 ec_dev->irq, err); 98 return err; 99 } 100 } 101 102 err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, 1, 103 NULL, ec_dev->irq, NULL); 104 if (err) { 105 dev_err(dev, 106 "Failed to register Embedded Controller subdevice %d\n", 107 err); 108 goto fail_mfd; 109 } 110 111 if (ec_dev->max_passthru) { 112 /* 113 * Register a PD device as well on top of this device. 114 * We make the following assumptions: 115 * - behind an EC, we have a pd 116 * - only one device added. 117 * - the EC is responsive at init time (it is not true for a 118 * sensor hub. 119 */ 120 err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, 121 &ec_pd_cell, 1, NULL, ec_dev->irq, NULL); 122 if (err) { 123 dev_err(dev, 124 "Failed to register Power Delivery subdevice %d\n", 125 err); 126 goto fail_mfd; 127 } 128 } 129 130 if (IS_ENABLED(CONFIG_OF) && dev->of_node) { 131 err = of_platform_populate(dev->of_node, NULL, NULL, dev); 132 if (err) { 133 mfd_remove_devices(dev); 134 dev_err(dev, "Failed to register sub-devices\n"); 135 goto fail_mfd; 136 } 137 } 138 139 dev_info(dev, "Chrome EC device registered\n"); 140 141 return 0; 142 143fail_mfd: 144 if (ec_dev->irq) 145 free_irq(ec_dev->irq, ec_dev); 146 return err; 147} 148EXPORT_SYMBOL(cros_ec_register); 149 150int cros_ec_remove(struct cros_ec_device *ec_dev) 151{ 152 mfd_remove_devices(ec_dev->dev); 153 154 return 0; 155} 156EXPORT_SYMBOL(cros_ec_remove); 157 158#ifdef CONFIG_PM_SLEEP 159int cros_ec_suspend(struct cros_ec_device *ec_dev) 160{ 161 struct device *dev = ec_dev->dev; 162 163 if (device_may_wakeup(dev)) 164 ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq); 165 166 disable_irq(ec_dev->irq); 167 ec_dev->was_wake_device = ec_dev->wake_enabled; 168 169 return 0; 170} 171EXPORT_SYMBOL(cros_ec_suspend); 172 173static void cros_ec_drain_events(struct cros_ec_device *ec_dev) 174{ 175 while (cros_ec_get_next_event(ec_dev) > 0) 176 blocking_notifier_call_chain(&ec_dev->event_notifier, 177 1, ec_dev); 178} 179 180int cros_ec_resume(struct cros_ec_device *ec_dev) 181{ 182 enable_irq(ec_dev->irq); 183 184 /* 185 * In some cases, we need to distinguish between events that occur 186 * during suspend if the EC is not a wake source. For example, 187 * keypresses during suspend should be discarded if it does not wake 188 * the system. 189 * 190 * If the EC is not a wake source, drain the event queue and mark them 191 * as "queued during suspend". 192 */ 193 if (ec_dev->wake_enabled) { 194 disable_irq_wake(ec_dev->irq); 195 ec_dev->wake_enabled = 0; 196 } else { 197 cros_ec_drain_events(ec_dev); 198 } 199 200 return 0; 201} 202EXPORT_SYMBOL(cros_ec_resume); 203 204#endif 205 206MODULE_LICENSE("GPL"); 207MODULE_DESCRIPTION("ChromeOS EC core driver");