0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/delay.h>
0011 #include <linux/module.h>
0012 #include <linux/pci.h>
0013 #include <linux/platform_data/cros_ec_commands.h>
0014 #include <linux/platform_data/cros_ec_proto.h>
0015 #include <linux/intel-ish-client-if.h>
0016
0017 #include "cros_ec.h"
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028 #define CROS_ISH_CL_TX_RING_SIZE 8
0029 #define CROS_ISH_CL_RX_RING_SIZE 8
0030
0031
0032 enum cros_ec_ish_channel {
0033 CROS_EC_COMMAND = 1,
0034 CROS_MKBP_EVENT = 2,
0035 };
0036
0037
0038
0039
0040
0041 #define ISHTP_SEND_TIMEOUT (3 * HZ)
0042
0043
0044 static const struct ishtp_device_id cros_ec_ishtp_id_table[] = {
0045 { .guid = GUID_INIT(0x7b7154d0, 0x56f4, 0x4bdc,
0046 0xb0, 0xd8, 0x9e, 0x7c, 0xda, 0xe0, 0xd6, 0xa0), },
0047 { }
0048 };
0049 MODULE_DEVICE_TABLE(ishtp, cros_ec_ishtp_id_table);
0050
0051 struct header {
0052 u8 channel;
0053 u8 status;
0054 u8 token;
0055 u8 reserved;
0056 } __packed;
0057
0058 struct cros_ish_out_msg {
0059 struct header hdr;
0060 struct ec_host_request ec_request;
0061 } __packed;
0062
0063 struct cros_ish_in_msg {
0064 struct header hdr;
0065 struct ec_host_response ec_response;
0066 } __packed;
0067
0068 #define IN_MSG_EC_RESPONSE_PREAMBLE \
0069 offsetof(struct cros_ish_in_msg, ec_response)
0070
0071 #define OUT_MSG_EC_REQUEST_PREAMBLE \
0072 offsetof(struct cros_ish_out_msg, ec_request)
0073
0074 #define cl_data_to_dev(client_data) ishtp_device((client_data)->cl_device)
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085 static DECLARE_RWSEM(init_lock);
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101 struct response_info {
0102 void *data;
0103 size_t max_size;
0104 size_t size;
0105 int error;
0106 u8 token;
0107 bool received;
0108 wait_queue_head_t wait_queue;
0109 };
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123 struct ishtp_cl_data {
0124 struct ishtp_cl *cros_ish_cl;
0125 struct ishtp_cl_device *cl_device;
0126
0127
0128
0129
0130
0131 struct response_info response;
0132
0133 struct work_struct work_ishtp_reset;
0134 struct work_struct work_ec_evt;
0135 struct cros_ec_device *ec_dev;
0136 };
0137
0138
0139
0140
0141
0142 static void ish_evt_handler(struct work_struct *work)
0143 {
0144 struct ishtp_cl_data *client_data =
0145 container_of(work, struct ishtp_cl_data, work_ec_evt);
0146
0147 cros_ec_irq_thread(0, client_data->ec_dev);
0148 }
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163 static int ish_send(struct ishtp_cl_data *client_data,
0164 u8 *out_msg, size_t out_size,
0165 u8 *in_msg, size_t in_size)
0166 {
0167 static u8 next_token;
0168 int rv;
0169 struct header *out_hdr = (struct header *)out_msg;
0170 struct ishtp_cl *cros_ish_cl = client_data->cros_ish_cl;
0171
0172 dev_dbg(cl_data_to_dev(client_data),
0173 "%s: channel=%02u status=%02u\n",
0174 __func__, out_hdr->channel, out_hdr->status);
0175
0176
0177 client_data->response.data = in_msg;
0178 client_data->response.max_size = in_size;
0179 client_data->response.error = 0;
0180 client_data->response.token = next_token++;
0181 client_data->response.received = false;
0182
0183 out_hdr->token = client_data->response.token;
0184
0185 rv = ishtp_cl_send(cros_ish_cl, out_msg, out_size);
0186 if (rv) {
0187 dev_err(cl_data_to_dev(client_data),
0188 "ishtp_cl_send error %d\n", rv);
0189 return rv;
0190 }
0191
0192 wait_event_interruptible_timeout(client_data->response.wait_queue,
0193 client_data->response.received,
0194 ISHTP_SEND_TIMEOUT);
0195 if (!client_data->response.received) {
0196 dev_err(cl_data_to_dev(client_data),
0197 "Timed out for response to host message\n");
0198 return -ETIMEDOUT;
0199 }
0200
0201 if (client_data->response.error < 0)
0202 return client_data->response.error;
0203
0204 return client_data->response.size;
0205 }
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217 static void process_recv(struct ishtp_cl *cros_ish_cl,
0218 struct ishtp_cl_rb *rb_in_proc, ktime_t timestamp)
0219 {
0220 size_t data_len = rb_in_proc->buf_idx;
0221 struct ishtp_cl_data *client_data =
0222 ishtp_get_client_data(cros_ish_cl);
0223 struct device *dev = cl_data_to_dev(client_data);
0224 struct cros_ish_in_msg *in_msg =
0225 (struct cros_ish_in_msg *)rb_in_proc->buffer.data;
0226
0227
0228 if (!down_read_trylock(&init_lock)) {
0229
0230 ishtp_cl_io_rb_recycle(rb_in_proc);
0231 dev_warn(dev,
0232 "Host is not ready to receive incoming messages\n");
0233 return;
0234 }
0235
0236
0237
0238
0239
0240 if (!rb_in_proc->buffer.data) {
0241 dev_warn(dev, "rb_in_proc->buffer.data returned null");
0242 client_data->response.error = -EBADMSG;
0243 goto end_error;
0244 }
0245
0246 if (data_len < sizeof(struct header)) {
0247 dev_err(dev, "data size %zu is less than header %zu\n",
0248 data_len, sizeof(struct header));
0249 client_data->response.error = -EMSGSIZE;
0250 goto end_error;
0251 }
0252
0253 dev_dbg(dev, "channel=%02u status=%02u\n",
0254 in_msg->hdr.channel, in_msg->hdr.status);
0255
0256 switch (in_msg->hdr.channel) {
0257 case CROS_EC_COMMAND:
0258 if (client_data->response.received) {
0259 dev_err(dev,
0260 "Previous firmware message not yet processed\n");
0261 goto end_error;
0262 }
0263
0264 if (client_data->response.token != in_msg->hdr.token) {
0265 dev_err_ratelimited(dev,
0266 "Dropping old response token %d\n",
0267 in_msg->hdr.token);
0268 goto end_error;
0269 }
0270
0271
0272 if (!client_data->response.data) {
0273 dev_err(dev,
0274 "Receiving buffer is null. Should be allocated by calling function\n");
0275 client_data->response.error = -EINVAL;
0276 goto error_wake_up;
0277 }
0278
0279 if (data_len > client_data->response.max_size) {
0280 dev_err(dev,
0281 "Received buffer size %zu is larger than allocated buffer %zu\n",
0282 data_len, client_data->response.max_size);
0283 client_data->response.error = -EMSGSIZE;
0284 goto error_wake_up;
0285 }
0286
0287 if (in_msg->hdr.status) {
0288 dev_err(dev, "firmware returned status %d\n",
0289 in_msg->hdr.status);
0290 client_data->response.error = -EIO;
0291 goto error_wake_up;
0292 }
0293
0294
0295 client_data->response.size = data_len;
0296
0297
0298
0299
0300
0301 memcpy(client_data->response.data,
0302 rb_in_proc->buffer.data, data_len);
0303
0304 error_wake_up:
0305
0306 ishtp_cl_io_rb_recycle(rb_in_proc);
0307 rb_in_proc = NULL;
0308
0309
0310 client_data->response.received = true;
0311
0312
0313 wake_up_interruptible(&client_data->response.wait_queue);
0314
0315 break;
0316
0317 case CROS_MKBP_EVENT:
0318
0319 ishtp_cl_io_rb_recycle(rb_in_proc);
0320 rb_in_proc = NULL;
0321
0322
0323
0324
0325 client_data->ec_dev->last_event_time = timestamp;
0326 schedule_work(&client_data->work_ec_evt);
0327
0328 break;
0329
0330 default:
0331 dev_err(dev, "Invalid channel=%02d\n", in_msg->hdr.channel);
0332 }
0333
0334 end_error:
0335
0336 if (rb_in_proc)
0337 ishtp_cl_io_rb_recycle(rb_in_proc);
0338
0339 up_read(&init_lock);
0340 }
0341
0342
0343
0344
0345
0346
0347
0348
0349 static void ish_event_cb(struct ishtp_cl_device *cl_device)
0350 {
0351 struct ishtp_cl_rb *rb_in_proc;
0352 struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device);
0353 ktime_t timestamp;
0354
0355
0356
0357
0358
0359 timestamp = cros_ec_get_time_ns();
0360
0361 while ((rb_in_proc = ishtp_cl_rx_get_rb(cros_ish_cl)) != NULL) {
0362
0363 process_recv(cros_ish_cl, rb_in_proc, timestamp);
0364 }
0365 }
0366
0367
0368
0369
0370
0371
0372
0373
0374
0375 static int cros_ish_init(struct ishtp_cl *cros_ish_cl)
0376 {
0377 int rv;
0378 struct ishtp_device *dev;
0379 struct ishtp_fw_client *fw_client;
0380 struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl);
0381
0382 rv = ishtp_cl_link(cros_ish_cl);
0383 if (rv) {
0384 dev_err(cl_data_to_dev(client_data),
0385 "ishtp_cl_link failed\n");
0386 return rv;
0387 }
0388
0389 dev = ishtp_get_ishtp_device(cros_ish_cl);
0390
0391
0392 ishtp_set_tx_ring_size(cros_ish_cl, CROS_ISH_CL_TX_RING_SIZE);
0393 ishtp_set_rx_ring_size(cros_ish_cl, CROS_ISH_CL_RX_RING_SIZE);
0394
0395 fw_client = ishtp_fw_cl_get_client(dev, &cros_ec_ishtp_id_table[0].guid);
0396 if (!fw_client) {
0397 dev_err(cl_data_to_dev(client_data),
0398 "ish client uuid not found\n");
0399 rv = -ENOENT;
0400 goto err_cl_unlink;
0401 }
0402
0403 ishtp_cl_set_fw_client_id(cros_ish_cl,
0404 ishtp_get_fw_client_id(fw_client));
0405 ishtp_set_connection_state(cros_ish_cl, ISHTP_CL_CONNECTING);
0406
0407 rv = ishtp_cl_connect(cros_ish_cl);
0408 if (rv) {
0409 dev_err(cl_data_to_dev(client_data),
0410 "client connect fail\n");
0411 goto err_cl_unlink;
0412 }
0413
0414 ishtp_register_event_cb(client_data->cl_device, ish_event_cb);
0415 return 0;
0416
0417 err_cl_unlink:
0418 ishtp_cl_unlink(cros_ish_cl);
0419 return rv;
0420 }
0421
0422
0423
0424
0425
0426
0427
0428 static void cros_ish_deinit(struct ishtp_cl *cros_ish_cl)
0429 {
0430 ishtp_set_connection_state(cros_ish_cl, ISHTP_CL_DISCONNECTING);
0431 ishtp_cl_disconnect(cros_ish_cl);
0432 ishtp_cl_unlink(cros_ish_cl);
0433 ishtp_cl_flush_queues(cros_ish_cl);
0434
0435
0436 ishtp_cl_free(cros_ish_cl);
0437 }
0438
0439
0440
0441
0442
0443
0444
0445
0446
0447
0448
0449 static int prepare_cros_ec_rx(struct cros_ec_device *ec_dev,
0450 const struct cros_ish_in_msg *in_msg,
0451 struct cros_ec_command *msg)
0452 {
0453 u8 sum = 0;
0454 int i, rv, offset;
0455
0456
0457 msg->result = in_msg->ec_response.result;
0458 rv = cros_ec_check_result(ec_dev, msg);
0459 if (rv < 0)
0460 return rv;
0461
0462 if (in_msg->ec_response.data_len > msg->insize) {
0463 dev_err(ec_dev->dev, "Packet too long (%d bytes, expected %d)",
0464 in_msg->ec_response.data_len, msg->insize);
0465 return -ENOSPC;
0466 }
0467
0468
0469 for (i = 0; i < sizeof(struct ec_host_response); i++)
0470 sum += ((u8 *)in_msg)[IN_MSG_EC_RESPONSE_PREAMBLE + i];
0471
0472 offset = sizeof(struct cros_ish_in_msg);
0473 for (i = 0; i < in_msg->ec_response.data_len; i++)
0474 sum += msg->data[i] = ((u8 *)in_msg)[offset + i];
0475
0476 if (sum) {
0477 dev_dbg(ec_dev->dev, "Bad received packet checksum %d\n", sum);
0478 return -EBADMSG;
0479 }
0480
0481 return 0;
0482 }
0483
0484 static int cros_ec_pkt_xfer_ish(struct cros_ec_device *ec_dev,
0485 struct cros_ec_command *msg)
0486 {
0487 int rv;
0488 struct ishtp_cl *cros_ish_cl = ec_dev->priv;
0489 struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl);
0490 struct device *dev = cl_data_to_dev(client_data);
0491 struct cros_ish_in_msg *in_msg = (struct cros_ish_in_msg *)ec_dev->din;
0492 struct cros_ish_out_msg *out_msg =
0493 (struct cros_ish_out_msg *)ec_dev->dout;
0494 size_t in_size = sizeof(struct cros_ish_in_msg) + msg->insize;
0495 size_t out_size = sizeof(struct cros_ish_out_msg) + msg->outsize;
0496
0497
0498 if (in_size > ec_dev->din_size) {
0499 dev_err(dev,
0500 "Incoming payload size %zu is too large for ec_dev->din_size %d\n",
0501 in_size, ec_dev->din_size);
0502 return -EMSGSIZE;
0503 }
0504
0505 if (out_size > ec_dev->dout_size) {
0506 dev_err(dev,
0507 "Outgoing payload size %zu is too large for ec_dev->dout_size %d\n",
0508 out_size, ec_dev->dout_size);
0509 return -EMSGSIZE;
0510 }
0511
0512
0513 if (!down_read_trylock(&init_lock)) {
0514 dev_warn(dev,
0515 "Host is not ready to send messages to ISH. Try again\n");
0516 return -EAGAIN;
0517 }
0518
0519
0520 out_msg->hdr.channel = CROS_EC_COMMAND;
0521 out_msg->hdr.status = 0;
0522
0523 ec_dev->dout += OUT_MSG_EC_REQUEST_PREAMBLE;
0524 rv = cros_ec_prepare_tx(ec_dev, msg);
0525 if (rv < 0)
0526 goto end_error;
0527 ec_dev->dout -= OUT_MSG_EC_REQUEST_PREAMBLE;
0528
0529 dev_dbg(dev,
0530 "out_msg: struct_ver=0x%x checksum=0x%x command=0x%x command_ver=0x%x data_len=0x%x\n",
0531 out_msg->ec_request.struct_version,
0532 out_msg->ec_request.checksum,
0533 out_msg->ec_request.command,
0534 out_msg->ec_request.command_version,
0535 out_msg->ec_request.data_len);
0536
0537
0538 rv = ish_send(client_data,
0539 (u8 *)out_msg, out_size,
0540 (u8 *)in_msg, in_size);
0541 if (rv < 0)
0542 goto end_error;
0543
0544 rv = prepare_cros_ec_rx(ec_dev, in_msg, msg);
0545 if (rv)
0546 goto end_error;
0547
0548 rv = in_msg->ec_response.data_len;
0549
0550 dev_dbg(dev,
0551 "in_msg: struct_ver=0x%x checksum=0x%x result=0x%x data_len=0x%x\n",
0552 in_msg->ec_response.struct_version,
0553 in_msg->ec_response.checksum,
0554 in_msg->ec_response.result,
0555 in_msg->ec_response.data_len);
0556
0557 end_error:
0558 if (msg->command == EC_CMD_REBOOT_EC)
0559 msleep(EC_REBOOT_DELAY_MS);
0560
0561 up_read(&init_lock);
0562
0563 return rv;
0564 }
0565
0566 static int cros_ec_dev_init(struct ishtp_cl_data *client_data)
0567 {
0568 struct cros_ec_device *ec_dev;
0569 struct device *dev = cl_data_to_dev(client_data);
0570
0571 ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
0572 if (!ec_dev)
0573 return -ENOMEM;
0574
0575 client_data->ec_dev = ec_dev;
0576 dev->driver_data = ec_dev;
0577
0578 ec_dev->dev = dev;
0579 ec_dev->priv = client_data->cros_ish_cl;
0580 ec_dev->cmd_xfer = NULL;
0581 ec_dev->pkt_xfer = cros_ec_pkt_xfer_ish;
0582 ec_dev->phys_name = dev_name(dev);
0583 ec_dev->din_size = sizeof(struct cros_ish_in_msg) +
0584 sizeof(struct ec_response_get_protocol_info);
0585 ec_dev->dout_size = sizeof(struct cros_ish_out_msg);
0586
0587 return cros_ec_register(ec_dev);
0588 }
0589
0590 static void reset_handler(struct work_struct *work)
0591 {
0592 int rv;
0593 struct device *dev;
0594 struct ishtp_cl *cros_ish_cl;
0595 struct ishtp_cl_device *cl_device;
0596 struct ishtp_cl_data *client_data =
0597 container_of(work, struct ishtp_cl_data, work_ishtp_reset);
0598
0599
0600 down_write(&init_lock);
0601
0602 cros_ish_cl = client_data->cros_ish_cl;
0603 cl_device = client_data->cl_device;
0604
0605
0606 ishtp_cl_unlink(cros_ish_cl);
0607 ishtp_cl_flush_queues(cros_ish_cl);
0608 ishtp_cl_free(cros_ish_cl);
0609
0610 cros_ish_cl = ishtp_cl_allocate(cl_device);
0611 if (!cros_ish_cl) {
0612 up_write(&init_lock);
0613 return;
0614 }
0615
0616 ishtp_set_drvdata(cl_device, cros_ish_cl);
0617 ishtp_set_client_data(cros_ish_cl, client_data);
0618 client_data->cros_ish_cl = cros_ish_cl;
0619
0620 rv = cros_ish_init(cros_ish_cl);
0621 if (rv) {
0622 ishtp_cl_free(cros_ish_cl);
0623 dev_err(cl_data_to_dev(client_data), "Reset Failed\n");
0624 up_write(&init_lock);
0625 return;
0626 }
0627
0628
0629 client_data->ec_dev->priv = client_data->cros_ish_cl;
0630 dev = cl_data_to_dev(client_data);
0631 dev->driver_data = client_data->ec_dev;
0632
0633 dev_info(cl_data_to_dev(client_data), "Chrome EC ISH reset done\n");
0634
0635 up_write(&init_lock);
0636 }
0637
0638
0639
0640
0641
0642
0643
0644 static int cros_ec_ishtp_probe(struct ishtp_cl_device *cl_device)
0645 {
0646 int rv;
0647 struct ishtp_cl *cros_ish_cl;
0648 struct ishtp_cl_data *client_data =
0649 devm_kzalloc(ishtp_device(cl_device),
0650 sizeof(*client_data), GFP_KERNEL);
0651 if (!client_data)
0652 return -ENOMEM;
0653
0654
0655 down_write(&init_lock);
0656
0657 cros_ish_cl = ishtp_cl_allocate(cl_device);
0658 if (!cros_ish_cl) {
0659 rv = -ENOMEM;
0660 goto end_ishtp_cl_alloc_error;
0661 }
0662
0663 ishtp_set_drvdata(cl_device, cros_ish_cl);
0664 ishtp_set_client_data(cros_ish_cl, client_data);
0665 client_data->cros_ish_cl = cros_ish_cl;
0666 client_data->cl_device = cl_device;
0667
0668 init_waitqueue_head(&client_data->response.wait_queue);
0669
0670 INIT_WORK(&client_data->work_ishtp_reset,
0671 reset_handler);
0672 INIT_WORK(&client_data->work_ec_evt,
0673 ish_evt_handler);
0674
0675 rv = cros_ish_init(cros_ish_cl);
0676 if (rv)
0677 goto end_ishtp_cl_init_error;
0678
0679 ishtp_get_device(cl_device);
0680
0681 up_write(&init_lock);
0682
0683
0684 rv = cros_ec_dev_init(client_data);
0685 if (rv) {
0686 down_write(&init_lock);
0687 goto end_cros_ec_dev_init_error;
0688 }
0689
0690 return 0;
0691
0692 end_cros_ec_dev_init_error:
0693 ishtp_set_connection_state(cros_ish_cl, ISHTP_CL_DISCONNECTING);
0694 ishtp_cl_disconnect(cros_ish_cl);
0695 ishtp_cl_unlink(cros_ish_cl);
0696 ishtp_cl_flush_queues(cros_ish_cl);
0697 ishtp_put_device(cl_device);
0698 end_ishtp_cl_init_error:
0699 ishtp_cl_free(cros_ish_cl);
0700 end_ishtp_cl_alloc_error:
0701 up_write(&init_lock);
0702 return rv;
0703 }
0704
0705
0706
0707
0708
0709
0710
0711 static void cros_ec_ishtp_remove(struct ishtp_cl_device *cl_device)
0712 {
0713 struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device);
0714 struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl);
0715
0716 cancel_work_sync(&client_data->work_ishtp_reset);
0717 cancel_work_sync(&client_data->work_ec_evt);
0718 cros_ish_deinit(cros_ish_cl);
0719 ishtp_put_device(cl_device);
0720 }
0721
0722
0723
0724
0725
0726
0727
0728 static int cros_ec_ishtp_reset(struct ishtp_cl_device *cl_device)
0729 {
0730 struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device);
0731 struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl);
0732
0733 schedule_work(&client_data->work_ishtp_reset);
0734
0735 return 0;
0736 }
0737
0738
0739
0740
0741
0742
0743
0744 static int __maybe_unused cros_ec_ishtp_suspend(struct device *device)
0745 {
0746 struct ishtp_cl_device *cl_device = ishtp_dev_to_cl_device(device);
0747 struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device);
0748 struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl);
0749
0750 return cros_ec_suspend(client_data->ec_dev);
0751 }
0752
0753
0754
0755
0756
0757
0758
0759 static int __maybe_unused cros_ec_ishtp_resume(struct device *device)
0760 {
0761 struct ishtp_cl_device *cl_device = ishtp_dev_to_cl_device(device);
0762 struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device);
0763 struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl);
0764
0765 return cros_ec_resume(client_data->ec_dev);
0766 }
0767
0768 static SIMPLE_DEV_PM_OPS(cros_ec_ishtp_pm_ops, cros_ec_ishtp_suspend,
0769 cros_ec_ishtp_resume);
0770
0771 static struct ishtp_cl_driver cros_ec_ishtp_driver = {
0772 .name = "cros_ec_ishtp",
0773 .id = cros_ec_ishtp_id_table,
0774 .probe = cros_ec_ishtp_probe,
0775 .remove = cros_ec_ishtp_remove,
0776 .reset = cros_ec_ishtp_reset,
0777 .driver = {
0778 .pm = &cros_ec_ishtp_pm_ops,
0779 },
0780 };
0781
0782 static int __init cros_ec_ishtp_mod_init(void)
0783 {
0784 return ishtp_cl_driver_register(&cros_ec_ishtp_driver, THIS_MODULE);
0785 }
0786
0787 static void __exit cros_ec_ishtp_mod_exit(void)
0788 {
0789 ishtp_cl_driver_unregister(&cros_ec_ishtp_driver);
0790 }
0791
0792 module_init(cros_ec_ishtp_mod_init);
0793 module_exit(cros_ec_ishtp_mod_exit);
0794
0795 MODULE_DESCRIPTION("ChromeOS EC ISHTP Client Driver");
0796 MODULE_AUTHOR("Rushikesh S Kadam <rushikesh.s.kadam@intel.com>");
0797
0798 MODULE_LICENSE("GPL v2");