cros_ec.c 4.4 KB

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