cros_ec.c 4.4 KB

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