brintos

brintos / linux-shallow public Read only

0
0
Text · 20.3 KiB · 5ac37bd Raw
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