linux/drivers/mfd/cros_ec.c
<<
>>
Prefs
   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/interrupt.h>
  21#include <linux/slab.h>
  22#include <linux/module.h>
  23#include <linux/mfd/core.h>
  24#include <linux/mfd/cros_ec.h>
  25#include <linux/mfd/cros_ec_commands.h>
  26
  27int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
  28                       struct cros_ec_msg *msg)
  29{
  30        uint8_t *out;
  31        int csum, i;
  32
  33        BUG_ON(msg->out_len > EC_HOST_PARAM_SIZE);
  34        out = ec_dev->dout;
  35        out[0] = EC_CMD_VERSION0 + msg->version;
  36        out[1] = msg->cmd;
  37        out[2] = msg->out_len;
  38        csum = out[0] + out[1] + out[2];
  39        for (i = 0; i < msg->out_len; i++)
  40                csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->out_buf[i];
  41        out[EC_MSG_TX_HEADER_BYTES + msg->out_len] = (uint8_t)(csum & 0xff);
  42
  43        return EC_MSG_TX_PROTO_BYTES + msg->out_len;
  44}
  45EXPORT_SYMBOL(cros_ec_prepare_tx);
  46
  47static int cros_ec_command_sendrecv(struct cros_ec_device *ec_dev,
  48                uint16_t cmd, void *out_buf, int out_len,
  49                void *in_buf, int in_len)
  50{
  51        struct cros_ec_msg msg;
  52
  53        msg.version = cmd >> 8;
  54        msg.cmd = cmd & 0xff;
  55        msg.out_buf = out_buf;
  56        msg.out_len = out_len;
  57        msg.in_buf = in_buf;
  58        msg.in_len = in_len;
  59
  60        return ec_dev->command_xfer(ec_dev, &msg);
  61}
  62
  63static int cros_ec_command_recv(struct cros_ec_device *ec_dev,
  64                uint16_t cmd, void *buf, int buf_len)
  65{
  66        return cros_ec_command_sendrecv(ec_dev, cmd, NULL, 0, buf, buf_len);
  67}
  68
  69static int cros_ec_command_send(struct cros_ec_device *ec_dev,
  70                uint16_t cmd, void *buf, int buf_len)
  71{
  72        return cros_ec_command_sendrecv(ec_dev, cmd, buf, buf_len, NULL, 0);
  73}
  74
  75static irqreturn_t ec_irq_thread(int irq, void *data)
  76{
  77        struct cros_ec_device *ec_dev = data;
  78
  79        if (device_may_wakeup(ec_dev->dev))
  80                pm_wakeup_event(ec_dev->dev, 0);
  81
  82        blocking_notifier_call_chain(&ec_dev->event_notifier, 1, ec_dev);
  83
  84        return IRQ_HANDLED;
  85}
  86
  87static struct mfd_cell cros_devs[] = {
  88        {
  89                .name = "cros-ec-keyb",
  90                .id = 1,
  91                .of_compatible = "google,cros-ec-keyb",
  92        },
  93};
  94
  95int cros_ec_register(struct cros_ec_device *ec_dev)
  96{
  97        struct device *dev = ec_dev->dev;
  98        int err = 0;
  99
 100        BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier);
 101
 102        ec_dev->command_send = cros_ec_command_send;
 103        ec_dev->command_recv = cros_ec_command_recv;
 104        ec_dev->command_sendrecv = cros_ec_command_sendrecv;
 105
 106        if (ec_dev->din_size) {
 107                ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
 108                if (!ec_dev->din)
 109                        return -ENOMEM;
 110        }
 111        if (ec_dev->dout_size) {
 112                ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL);
 113                if (!ec_dev->dout)
 114                        return -ENOMEM;
 115        }
 116
 117        if (!ec_dev->irq) {
 118                dev_dbg(dev, "no valid IRQ: %d\n", ec_dev->irq);
 119                return err;
 120        }
 121
 122        err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread,
 123                                   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
 124                                   "chromeos-ec", ec_dev);
 125        if (err) {
 126                dev_err(dev, "request irq %d: error %d\n", ec_dev->irq, err);
 127                return err;
 128        }
 129
 130        err = mfd_add_devices(dev, 0, cros_devs,
 131                              ARRAY_SIZE(cros_devs),
 132                              NULL, ec_dev->irq, NULL);
 133        if (err) {
 134                dev_err(dev, "failed to add mfd devices\n");
 135                goto fail_mfd;
 136        }
 137
 138        dev_info(dev, "Chrome EC (%s)\n", ec_dev->name);
 139
 140        return 0;
 141
 142fail_mfd:
 143        free_irq(ec_dev->irq, ec_dev);
 144
 145        return err;
 146}
 147EXPORT_SYMBOL(cros_ec_register);
 148
 149int cros_ec_remove(struct cros_ec_device *ec_dev)
 150{
 151        mfd_remove_devices(ec_dev->dev);
 152        free_irq(ec_dev->irq, ec_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
 173int cros_ec_resume(struct cros_ec_device *ec_dev)
 174{
 175        enable_irq(ec_dev->irq);
 176
 177        if (ec_dev->wake_enabled) {
 178                disable_irq_wake(ec_dev->irq);
 179                ec_dev->wake_enabled = 0;
 180        }
 181
 182        return 0;
 183}
 184EXPORT_SYMBOL(cros_ec_resume);
 185
 186#endif
 187
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.