755 lines · c
1// SPDX-License-Identifier: GPL-2.02// ISHTP interface for ChromeOS Embedded Controller3//4// Copyright (c) 2019, Intel Corporation.5//6// ISHTP client driver for talking to the Chrome OS EC firmware running7// on Intel Integrated Sensor Hub (ISH) using the ISH Transport protocol8// (ISH-TP).9 10#include <linux/delay.h>11#include <linux/module.h>12#include <linux/pci.h>13#include <linux/platform_data/cros_ec_commands.h>14#include <linux/platform_data/cros_ec_proto.h>15#include <linux/intel-ish-client-if.h>16 17#include "cros_ec.h"18 19/*20 * ISH TX/RX ring buffer pool size21 *22 * The AP->ISH messages and corresponding ISH->AP responses are23 * serialized. We need 1 TX and 1 RX buffer for these.24 *25 * The MKBP ISH->AP events are serialized. We need one additional RX26 * buffer for them.27 */28#define CROS_ISH_CL_TX_RING_SIZE 829#define CROS_ISH_CL_RX_RING_SIZE 830 31/* ISH CrOS EC Host Commands */32enum cros_ec_ish_channel {33 CROS_EC_COMMAND = 1, /* AP->ISH message */34 CROS_MKBP_EVENT = 2, /* ISH->AP events */35};36 37/*38 * ISH firmware timeout for 1 message send failure is 1Hz, and the39 * firmware will retry 2 times, so 3Hz is used for timeout.40 */41#define ISHTP_SEND_TIMEOUT (3 * HZ)42 43/* ISH Transport CrOS EC ISH client unique GUID */44static const struct ishtp_device_id cros_ec_ishtp_id_table[] = {45 { .guid = GUID_INIT(0x7b7154d0, 0x56f4, 0x4bdc,46 0xb0, 0xd8, 0x9e, 0x7c, 0xda, 0xe0, 0xd6, 0xa0), },47 { }48};49MODULE_DEVICE_TABLE(ishtp, cros_ec_ishtp_id_table);50 51struct header {52 u8 channel;53 u8 status;54 u8 token;55 u8 reserved;56} __packed;57 58struct cros_ish_out_msg {59 struct header hdr;60 struct ec_host_request ec_request;61} __packed;62 63struct cros_ish_in_msg {64 struct header hdr;65 struct ec_host_response ec_response;66} __packed;67 68#define IN_MSG_EC_RESPONSE_PREAMBLE \69 offsetof(struct cros_ish_in_msg, ec_response)70 71#define OUT_MSG_EC_REQUEST_PREAMBLE \72 offsetof(struct cros_ish_out_msg, ec_request)73 74#define cl_data_to_dev(client_data) ishtp_device((client_data)->cl_device)75 76/*77 * The Read-Write Semaphore is used to prevent message TX or RX while78 * the ishtp client is being initialized or undergoing reset.79 *80 * The readers are the kernel function calls responsible for IA->ISH81 * and ISH->AP messaging.82 *83 * The writers are .reset() and .probe() function.84 */85static DECLARE_RWSEM(init_lock);86 87/**88 * struct response_info - Encapsulate firmware response related89 * information for passing between function ish_send() and90 * process_recv() callback.91 *92 * @data: Copy the data received from firmware here.93 * @max_size: Max size allocated for the @data buffer. If the received94 * data exceeds this value, we log an error.95 * @size: Actual size of data received from firmware.96 * @error: 0 for success, negative error code for a failure in process_recv().97 * @token: Expected token for response that we are waiting on.98 * @received: Set to true on receiving a valid firmware response to host command99 * @wait_queue: Wait queue for host to wait for firmware response.100 */101struct response_info {102 void *data;103 size_t max_size;104 size_t size;105 int error;106 u8 token;107 bool received;108 wait_queue_head_t wait_queue;109};110 111/**112 * struct ishtp_cl_data - Encapsulate per ISH TP Client.113 *114 * @cros_ish_cl: ISHTP firmware client instance.115 * @cl_device: ISHTP client device instance.116 * @response: Response info passing between ish_send() and process_recv().117 * @work_ishtp_reset: Work queue reset handling.118 * @work_ec_evt: Work queue for EC events.119 * @ec_dev: CrOS EC MFD device.120 *121 * This structure is used to store per client data.122 */123struct ishtp_cl_data {124 struct ishtp_cl *cros_ish_cl;125 struct ishtp_cl_device *cl_device;126 127 /*128 * Used for passing firmware response information between129 * ish_send() and process_recv() callback.130 */131 struct response_info response;132 133 struct work_struct work_ishtp_reset;134 struct work_struct work_ec_evt;135 struct cros_ec_device *ec_dev;136};137 138/**139 * ish_evt_handler - ISH to AP event handler140 * @work: Work struct141 */142static void ish_evt_handler(struct work_struct *work)143{144 struct ishtp_cl_data *client_data =145 container_of(work, struct ishtp_cl_data, work_ec_evt);146 147 cros_ec_irq_thread(0, client_data->ec_dev);148}149 150/**151 * ish_send() - Send message from host to firmware152 *153 * @client_data: Client data instance154 * @out_msg: Message buffer to be sent to firmware155 * @out_size: Size of out going message156 * @in_msg: Message buffer where the incoming data is copied. This buffer157 * is allocated by calling158 * @in_size: Max size of incoming message159 *160 * Return: Number of bytes copied in the in_msg on success, negative161 * error code on failure.162 */163static int ish_send(struct ishtp_cl_data *client_data,164 u8 *out_msg, size_t out_size,165 u8 *in_msg, size_t in_size)166{167 static u8 next_token;168 int rv;169 struct header *out_hdr = (struct header *)out_msg;170 struct ishtp_cl *cros_ish_cl = client_data->cros_ish_cl;171 172 dev_dbg(cl_data_to_dev(client_data),173 "%s: channel=%02u status=%02u\n",174 __func__, out_hdr->channel, out_hdr->status);175 176 /* Setup for incoming response */177 client_data->response.data = in_msg;178 client_data->response.max_size = in_size;179 client_data->response.error = 0;180 client_data->response.token = next_token++;181 client_data->response.received = false;182 183 out_hdr->token = client_data->response.token;184 185 rv = ishtp_cl_send(cros_ish_cl, out_msg, out_size);186 if (rv) {187 dev_err(cl_data_to_dev(client_data),188 "ishtp_cl_send error %d\n", rv);189 return rv;190 }191 192 wait_event_interruptible_timeout(client_data->response.wait_queue,193 client_data->response.received,194 ISHTP_SEND_TIMEOUT);195 if (!client_data->response.received) {196 dev_err(cl_data_to_dev(client_data),197 "Timed out for response to host message\n");198 return -ETIMEDOUT;199 }200 201 if (client_data->response.error < 0)202 return client_data->response.error;203 204 return client_data->response.size;205}206 207/**208 * process_recv() - Received and parse incoming packet209 * @cros_ish_cl: Client instance to get stats210 * @rb_in_proc: Host interface message buffer211 * @timestamp: Timestamp of when parent callback started212 *213 * Parse the incoming packet. If it is a response packet then it will214 * update per instance flags and wake up the caller waiting to for the215 * response. If it is an event packet then it will schedule event work.216 */217static void process_recv(struct ishtp_cl *cros_ish_cl,218 struct ishtp_cl_rb *rb_in_proc, ktime_t timestamp)219{220 size_t data_len = rb_in_proc->buf_idx;221 struct ishtp_cl_data *client_data =222 ishtp_get_client_data(cros_ish_cl);223 struct device *dev = cl_data_to_dev(client_data);224 struct cros_ish_in_msg *in_msg =225 (struct cros_ish_in_msg *)rb_in_proc->buffer.data;226 227 /* Proceed only if reset or init is not in progress */228 if (!down_read_trylock(&init_lock)) {229 /* Free the buffer */230 ishtp_cl_io_rb_recycle(rb_in_proc);231 dev_warn(dev,232 "Host is not ready to receive incoming messages\n");233 return;234 }235 236 /*237 * All firmware messages contain a header. Check the buffer size238 * before accessing elements inside.239 */240 if (!rb_in_proc->buffer.data) {241 dev_warn(dev, "rb_in_proc->buffer.data returned null");242 client_data->response.error = -EBADMSG;243 goto end_error;244 }245 246 if (data_len < sizeof(struct header)) {247 dev_err(dev, "data size %zu is less than header %zu\n",248 data_len, sizeof(struct header));249 client_data->response.error = -EMSGSIZE;250 goto end_error;251 }252 253 dev_dbg(dev, "channel=%02u status=%02u\n",254 in_msg->hdr.channel, in_msg->hdr.status);255 256 switch (in_msg->hdr.channel) {257 case CROS_EC_COMMAND:258 if (client_data->response.received) {259 dev_err(dev,260 "Previous firmware message not yet processed\n");261 goto end_error;262 }263 264 if (client_data->response.token != in_msg->hdr.token) {265 dev_err_ratelimited(dev,266 "Dropping old response token %d\n",267 in_msg->hdr.token);268 goto end_error;269 }270 271 /* Sanity check */272 if (!client_data->response.data) {273 dev_err(dev,274 "Receiving buffer is null. Should be allocated by calling function\n");275 client_data->response.error = -EINVAL;276 goto error_wake_up;277 }278 279 if (data_len > client_data->response.max_size) {280 dev_err(dev,281 "Received buffer size %zu is larger than allocated buffer %zu\n",282 data_len, client_data->response.max_size);283 client_data->response.error = -EMSGSIZE;284 goto error_wake_up;285 }286 287 if (in_msg->hdr.status) {288 dev_err(dev, "firmware returned status %d\n",289 in_msg->hdr.status);290 client_data->response.error = -EIO;291 goto error_wake_up;292 }293 294 /* Update the actual received buffer size */295 client_data->response.size = data_len;296 297 /*298 * Copy the buffer received in firmware response for the299 * calling thread.300 */301 memcpy(client_data->response.data,302 rb_in_proc->buffer.data, data_len);303 304error_wake_up:305 /* Free the buffer since we copied data or didn't need it */306 ishtp_cl_io_rb_recycle(rb_in_proc);307 rb_in_proc = NULL;308 309 /* Set flag before waking up the caller */310 client_data->response.received = true;311 312 /* Wake the calling thread */313 wake_up_interruptible(&client_data->response.wait_queue);314 315 break;316 317 case CROS_MKBP_EVENT:318 /* Free the buffer. This is just an event without data */319 ishtp_cl_io_rb_recycle(rb_in_proc);320 rb_in_proc = NULL;321 /*322 * Set timestamp from beginning of function since we actually323 * got an incoming MKBP event324 */325 client_data->ec_dev->last_event_time = timestamp;326 schedule_work(&client_data->work_ec_evt);327 328 break;329 330 default:331 dev_err(dev, "Invalid channel=%02d\n", in_msg->hdr.channel);332 }333 334end_error:335 /* Free the buffer if we already haven't */336 if (rb_in_proc)337 ishtp_cl_io_rb_recycle(rb_in_proc);338 339 up_read(&init_lock);340}341 342/**343 * ish_event_cb() - bus driver callback for incoming message344 * @cl_device: ISHTP client device for which this message is targeted.345 *346 * Remove the packet from the list and process the message by calling347 * process_recv.348 */349static void ish_event_cb(struct ishtp_cl_device *cl_device)350{351 struct ishtp_cl_rb *rb_in_proc;352 struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device);353 ktime_t timestamp;354 355 /*356 * Take timestamp as close to hardware interrupt as possible for sensor357 * timestamps.358 */359 timestamp = cros_ec_get_time_ns();360 361 while ((rb_in_proc = ishtp_cl_rx_get_rb(cros_ish_cl)) != NULL) {362 /* Decide what to do with received data */363 process_recv(cros_ish_cl, rb_in_proc, timestamp);364 }365}366 367/**368 * cros_ish_init() - Init function for ISHTP client369 * @cros_ish_cl: ISHTP client instance370 * @reset: true if called from reset handler371 *372 * This function complete the initializtion of the client.373 *374 * Return: 0 for success, negative error code for failure.375 */376static int cros_ish_init(struct ishtp_cl *cros_ish_cl, bool reset)377{378 int rv;379 struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl);380 381 rv = ishtp_cl_establish_connection(cros_ish_cl,382 &cros_ec_ishtp_id_table[0].guid,383 CROS_ISH_CL_TX_RING_SIZE,384 CROS_ISH_CL_RX_RING_SIZE,385 reset);386 if (rv) {387 dev_err(cl_data_to_dev(client_data),388 "client connect fail\n");389 goto err_cl_disconnect;390 }391 392 ishtp_register_event_cb(client_data->cl_device, ish_event_cb);393 return 0;394 395err_cl_disconnect:396 ishtp_cl_destroy_connection(cros_ish_cl, reset);397 return rv;398}399 400/**401 * cros_ish_deinit() - Deinit function for ISHTP client402 * @cros_ish_cl: ISHTP client instance403 *404 * Unlink and free cros_ec client405 */406static void cros_ish_deinit(struct ishtp_cl *cros_ish_cl)407{408 ishtp_cl_destroy_connection(cros_ish_cl, false);409 410 /* Disband and free all Tx and Rx client-level rings */411 ishtp_cl_free(cros_ish_cl);412}413 414/**415 * prepare_cros_ec_rx() - Check & prepare receive buffer416 * @ec_dev: CrOS EC MFD device.417 * @in_msg: Incoming message buffer418 * @msg: cros_ec command used to send & receive data419 *420 * Return: 0 for success, negative error code for failure.421 *422 * Check the received buffer. Convert to cros_ec_command format.423 */424static int prepare_cros_ec_rx(struct cros_ec_device *ec_dev,425 const struct cros_ish_in_msg *in_msg,426 struct cros_ec_command *msg)427{428 u8 sum = 0;429 int i, rv, offset;430 431 /* Check response error code */432 msg->result = in_msg->ec_response.result;433 rv = cros_ec_check_result(ec_dev, msg);434 if (rv < 0)435 return rv;436 437 if (in_msg->ec_response.data_len > msg->insize) {438 dev_err(ec_dev->dev, "Packet too long (%d bytes, expected %d)",439 in_msg->ec_response.data_len, msg->insize);440 return -ENOSPC;441 }442 443 /* Copy response packet payload and compute checksum */444 for (i = 0; i < sizeof(struct ec_host_response); i++)445 sum += ((u8 *)in_msg)[IN_MSG_EC_RESPONSE_PREAMBLE + i];446 447 offset = sizeof(struct cros_ish_in_msg);448 for (i = 0; i < in_msg->ec_response.data_len; i++)449 sum += msg->data[i] = ((u8 *)in_msg)[offset + i];450 451 if (sum) {452 dev_dbg(ec_dev->dev, "Bad received packet checksum %d\n", sum);453 return -EBADMSG;454 }455 456 return 0;457}458 459static int cros_ec_pkt_xfer_ish(struct cros_ec_device *ec_dev,460 struct cros_ec_command *msg)461{462 int rv;463 struct ishtp_cl *cros_ish_cl = ec_dev->priv;464 struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl);465 struct device *dev = cl_data_to_dev(client_data);466 struct cros_ish_in_msg *in_msg = (struct cros_ish_in_msg *)ec_dev->din;467 struct cros_ish_out_msg *out_msg =468 (struct cros_ish_out_msg *)ec_dev->dout;469 size_t in_size = sizeof(struct cros_ish_in_msg) + msg->insize;470 size_t out_size = sizeof(struct cros_ish_out_msg) + msg->outsize;471 472 /* Sanity checks */473 if (in_size > ec_dev->din_size) {474 dev_err(dev,475 "Incoming payload size %zu is too large for ec_dev->din_size %d\n",476 in_size, ec_dev->din_size);477 return -EMSGSIZE;478 }479 480 if (out_size > ec_dev->dout_size) {481 dev_err(dev,482 "Outgoing payload size %zu is too large for ec_dev->dout_size %d\n",483 out_size, ec_dev->dout_size);484 return -EMSGSIZE;485 }486 487 /* Proceed only if reset-init is not in progress */488 if (!down_read_trylock(&init_lock)) {489 dev_warn(dev,490 "Host is not ready to send messages to ISH. Try again\n");491 return -EAGAIN;492 }493 494 /* Prepare the package to be sent over ISH TP */495 out_msg->hdr.channel = CROS_EC_COMMAND;496 out_msg->hdr.status = 0;497 498 ec_dev->dout += OUT_MSG_EC_REQUEST_PREAMBLE;499 rv = cros_ec_prepare_tx(ec_dev, msg);500 if (rv < 0)501 goto end_error;502 ec_dev->dout -= OUT_MSG_EC_REQUEST_PREAMBLE;503 504 dev_dbg(dev,505 "out_msg: struct_ver=0x%x checksum=0x%x command=0x%x command_ver=0x%x data_len=0x%x\n",506 out_msg->ec_request.struct_version,507 out_msg->ec_request.checksum,508 out_msg->ec_request.command,509 out_msg->ec_request.command_version,510 out_msg->ec_request.data_len);511 512 /* Send command to ISH EC firmware and read response */513 rv = ish_send(client_data,514 (u8 *)out_msg, out_size,515 (u8 *)in_msg, in_size);516 if (rv < 0)517 goto end_error;518 519 rv = prepare_cros_ec_rx(ec_dev, in_msg, msg);520 if (rv)521 goto end_error;522 523 rv = in_msg->ec_response.data_len;524 525 dev_dbg(dev,526 "in_msg: struct_ver=0x%x checksum=0x%x result=0x%x data_len=0x%x\n",527 in_msg->ec_response.struct_version,528 in_msg->ec_response.checksum,529 in_msg->ec_response.result,530 in_msg->ec_response.data_len);531 532end_error:533 if (msg->command == EC_CMD_REBOOT_EC)534 msleep(EC_REBOOT_DELAY_MS);535 536 up_read(&init_lock);537 538 return rv;539}540 541static int cros_ec_dev_init(struct ishtp_cl_data *client_data)542{543 struct cros_ec_device *ec_dev;544 struct device *dev = cl_data_to_dev(client_data);545 546 ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);547 if (!ec_dev)548 return -ENOMEM;549 550 client_data->ec_dev = ec_dev;551 dev->driver_data = ec_dev;552 553 ec_dev->dev = dev;554 ec_dev->priv = client_data->cros_ish_cl;555 ec_dev->cmd_xfer = NULL;556 ec_dev->pkt_xfer = cros_ec_pkt_xfer_ish;557 ec_dev->phys_name = dev_name(dev);558 ec_dev->din_size = sizeof(struct cros_ish_in_msg) +559 sizeof(struct ec_response_get_protocol_info);560 ec_dev->dout_size = sizeof(struct cros_ish_out_msg);561 562 return cros_ec_register(ec_dev);563}564 565static void reset_handler(struct work_struct *work)566{567 int rv;568 struct device *dev;569 struct ishtp_cl *cros_ish_cl;570 struct ishtp_cl_data *client_data =571 container_of(work, struct ishtp_cl_data, work_ishtp_reset);572 573 /* Lock for reset to complete */574 down_write(&init_lock);575 576 cros_ish_cl = client_data->cros_ish_cl;577 578 ishtp_cl_destroy_connection(cros_ish_cl, true);579 580 rv = cros_ish_init(cros_ish_cl, true);581 if (rv) {582 dev_err(cl_data_to_dev(client_data), "Reset Failed\n");583 up_write(&init_lock);584 return;585 }586 587 /* Refresh ec_dev device pointers */588 client_data->ec_dev->priv = client_data->cros_ish_cl;589 dev = cl_data_to_dev(client_data);590 dev->driver_data = client_data->ec_dev;591 592 dev_info(cl_data_to_dev(client_data), "Chrome EC ISH reset done\n");593 594 up_write(&init_lock);595}596 597/**598 * cros_ec_ishtp_probe() - ISHTP client driver probe callback599 * @cl_device: ISHTP client device instance600 *601 * Return: 0 for success, negative error code for failure.602 */603static int cros_ec_ishtp_probe(struct ishtp_cl_device *cl_device)604{605 int rv;606 struct ishtp_cl *cros_ish_cl;607 struct ishtp_cl_data *client_data =608 devm_kzalloc(ishtp_device(cl_device),609 sizeof(*client_data), GFP_KERNEL);610 if (!client_data)611 return -ENOMEM;612 613 /* Lock for initialization to complete */614 down_write(&init_lock);615 616 cros_ish_cl = ishtp_cl_allocate(cl_device);617 if (!cros_ish_cl) {618 rv = -ENOMEM;619 goto end_ishtp_cl_alloc_error;620 }621 622 ishtp_set_drvdata(cl_device, cros_ish_cl);623 ishtp_set_client_data(cros_ish_cl, client_data);624 client_data->cros_ish_cl = cros_ish_cl;625 client_data->cl_device = cl_device;626 627 init_waitqueue_head(&client_data->response.wait_queue);628 629 INIT_WORK(&client_data->work_ishtp_reset,630 reset_handler);631 INIT_WORK(&client_data->work_ec_evt,632 ish_evt_handler);633 634 rv = cros_ish_init(cros_ish_cl, false);635 if (rv)636 goto end_ishtp_cl_init_error;637 638 ishtp_get_device(cl_device);639 640 up_write(&init_lock);641 642 /* Register croc_ec_dev mfd */643 rv = cros_ec_dev_init(client_data);644 if (rv) {645 down_write(&init_lock);646 goto end_cros_ec_dev_init_error;647 }648 649 return 0;650 651end_cros_ec_dev_init_error:652 ishtp_cl_destroy_connection(cros_ish_cl, false);653 ishtp_put_device(cl_device);654end_ishtp_cl_init_error:655 ishtp_cl_free(cros_ish_cl);656end_ishtp_cl_alloc_error:657 up_write(&init_lock);658 return rv;659}660 661/**662 * cros_ec_ishtp_remove() - ISHTP client driver remove callback663 * @cl_device: ISHTP client device instance664 *665 * Return: 0666 */667static void cros_ec_ishtp_remove(struct ishtp_cl_device *cl_device)668{669 struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device);670 struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl);671 672 cancel_work_sync(&client_data->work_ishtp_reset);673 cancel_work_sync(&client_data->work_ec_evt);674 cros_ish_deinit(cros_ish_cl);675 ishtp_put_device(cl_device);676}677 678/**679 * cros_ec_ishtp_reset() - ISHTP client driver reset callback680 * @cl_device: ISHTP client device instance681 *682 * Return: 0683 */684static int cros_ec_ishtp_reset(struct ishtp_cl_device *cl_device)685{686 struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device);687 struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl);688 689 schedule_work(&client_data->work_ishtp_reset);690 691 return 0;692}693 694/**695 * cros_ec_ishtp_suspend() - ISHTP client driver suspend callback696 * @device: device instance697 *698 * Return: 0 for success, negative error code for failure.699 */700static int __maybe_unused cros_ec_ishtp_suspend(struct device *device)701{702 struct ishtp_cl_device *cl_device = ishtp_dev_to_cl_device(device);703 struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device);704 struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl);705 706 return cros_ec_suspend(client_data->ec_dev);707}708 709/**710 * cros_ec_ishtp_resume() - ISHTP client driver resume callback711 * @device: device instance712 *713 * Return: 0 for success, negative error code for failure.714 */715static int __maybe_unused cros_ec_ishtp_resume(struct device *device)716{717 struct ishtp_cl_device *cl_device = ishtp_dev_to_cl_device(device);718 struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device);719 struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl);720 721 return cros_ec_resume(client_data->ec_dev);722}723 724static SIMPLE_DEV_PM_OPS(cros_ec_ishtp_pm_ops, cros_ec_ishtp_suspend,725 cros_ec_ishtp_resume);726 727static struct ishtp_cl_driver cros_ec_ishtp_driver = {728 .name = "cros_ec_ishtp",729 .id = cros_ec_ishtp_id_table,730 .probe = cros_ec_ishtp_probe,731 .remove = cros_ec_ishtp_remove,732 .reset = cros_ec_ishtp_reset,733 .driver = {734 .pm = &cros_ec_ishtp_pm_ops,735 },736};737 738static int __init cros_ec_ishtp_mod_init(void)739{740 return ishtp_cl_driver_register(&cros_ec_ishtp_driver, THIS_MODULE);741}742 743static void __exit cros_ec_ishtp_mod_exit(void)744{745 ishtp_cl_driver_unregister(&cros_ec_ishtp_driver);746}747 748module_init(cros_ec_ishtp_mod_init);749module_exit(cros_ec_ishtp_mod_exit);750 751MODULE_DESCRIPTION("ChromeOS EC ISHTP Client Driver");752MODULE_AUTHOR("Rushikesh S Kadam <rushikesh.s.kadam@intel.com>");753 754MODULE_LICENSE("GPL v2");755