// SPDX-License-Identifier: GPL-2.0 // ISHTP interface for ChromeOS Embedded Controller // // Copyright (c) 2019, Intel Corporation. // // ISHTP client driver for talking to the Chrome OS EC firmware running // on Intel Integrated Sensor Hub (ISH) using the ISH Transport protocol // (ISH-TP). #include <linux/delay.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/platform_data/cros_ec_commands.h> #include <linux/platform_data/cros_ec_proto.h> #include <linux/intel-ish-client-if.h> #include "cros_ec.h" /* * ISH TX/RX ring buffer pool size * * The AP->ISH messages and corresponding ISH->AP responses are * serialized. We need 1 TX and 1 RX buffer for these. * * The MKBP ISH->AP events are serialized. We need one additional RX * buffer for them. */ #define CROS_ISH_CL_TX_RING_SIZE … #define CROS_ISH_CL_RX_RING_SIZE … /* ISH CrOS EC Host Commands */ enum cros_ec_ish_channel { … }; /* * ISH firmware timeout for 1 message send failure is 1Hz, and the * firmware will retry 2 times, so 3Hz is used for timeout. */ #define ISHTP_SEND_TIMEOUT … /* ISH Transport CrOS EC ISH client unique GUID */ static const struct ishtp_device_id cros_ec_ishtp_id_table[] = …; MODULE_DEVICE_TABLE(ishtp, cros_ec_ishtp_id_table); struct header { … } __packed; struct cros_ish_out_msg { … } __packed; struct cros_ish_in_msg { … } __packed; #define IN_MSG_EC_RESPONSE_PREAMBLE … #define OUT_MSG_EC_REQUEST_PREAMBLE … #define cl_data_to_dev(client_data) … /* * The Read-Write Semaphore is used to prevent message TX or RX while * the ishtp client is being initialized or undergoing reset. * * The readers are the kernel function calls responsible for IA->ISH * and ISH->AP messaging. * * The writers are .reset() and .probe() function. */ static DECLARE_RWSEM(init_lock); /** * struct response_info - Encapsulate firmware response related * information for passing between function ish_send() and * process_recv() callback. * * @data: Copy the data received from firmware here. * @max_size: Max size allocated for the @data buffer. If the received * data exceeds this value, we log an error. * @size: Actual size of data received from firmware. * @error: 0 for success, negative error code for a failure in process_recv(). * @token: Expected token for response that we are waiting on. * @received: Set to true on receiving a valid firmware response to host command * @wait_queue: Wait queue for host to wait for firmware response. */ struct response_info { … }; /** * struct ishtp_cl_data - Encapsulate per ISH TP Client. * * @cros_ish_cl: ISHTP firmware client instance. * @cl_device: ISHTP client device instance. * @response: Response info passing between ish_send() and process_recv(). * @work_ishtp_reset: Work queue reset handling. * @work_ec_evt: Work queue for EC events. * @ec_dev: CrOS EC MFD device. * * This structure is used to store per client data. */ struct ishtp_cl_data { … }; /** * ish_evt_handler - ISH to AP event handler * @work: Work struct */ static void ish_evt_handler(struct work_struct *work) { … } /** * ish_send() - Send message from host to firmware * * @client_data: Client data instance * @out_msg: Message buffer to be sent to firmware * @out_size: Size of out going message * @in_msg: Message buffer where the incoming data is copied. This buffer * is allocated by calling * @in_size: Max size of incoming message * * Return: Number of bytes copied in the in_msg on success, negative * error code on failure. */ static int ish_send(struct ishtp_cl_data *client_data, u8 *out_msg, size_t out_size, u8 *in_msg, size_t in_size) { … } /** * process_recv() - Received and parse incoming packet * @cros_ish_cl: Client instance to get stats * @rb_in_proc: Host interface message buffer * @timestamp: Timestamp of when parent callback started * * Parse the incoming packet. If it is a response packet then it will * update per instance flags and wake up the caller waiting to for the * response. If it is an event packet then it will schedule event work. */ static void process_recv(struct ishtp_cl *cros_ish_cl, struct ishtp_cl_rb *rb_in_proc, ktime_t timestamp) { … } /** * ish_event_cb() - bus driver callback for incoming message * @cl_device: ISHTP client device for which this message is targeted. * * Remove the packet from the list and process the message by calling * process_recv. */ static void ish_event_cb(struct ishtp_cl_device *cl_device) { … } /** * cros_ish_init() - Init function for ISHTP client * @cros_ish_cl: ISHTP client instance * @reset: true if called from reset handler * * This function complete the initializtion of the client. * * Return: 0 for success, negative error code for failure. */ static int cros_ish_init(struct ishtp_cl *cros_ish_cl, bool reset) { … } /** * cros_ish_deinit() - Deinit function for ISHTP client * @cros_ish_cl: ISHTP client instance * * Unlink and free cros_ec client */ static void cros_ish_deinit(struct ishtp_cl *cros_ish_cl) { … } /** * prepare_cros_ec_rx() - Check & prepare receive buffer * @ec_dev: CrOS EC MFD device. * @in_msg: Incoming message buffer * @msg: cros_ec command used to send & receive data * * Return: 0 for success, negative error code for failure. * * Check the received buffer. Convert to cros_ec_command format. */ static int prepare_cros_ec_rx(struct cros_ec_device *ec_dev, const struct cros_ish_in_msg *in_msg, struct cros_ec_command *msg) { … } static int cros_ec_pkt_xfer_ish(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) { … } static int cros_ec_dev_init(struct ishtp_cl_data *client_data) { … } static void reset_handler(struct work_struct *work) { … } /** * cros_ec_ishtp_probe() - ISHTP client driver probe callback * @cl_device: ISHTP client device instance * * Return: 0 for success, negative error code for failure. */ static int cros_ec_ishtp_probe(struct ishtp_cl_device *cl_device) { … } /** * cros_ec_ishtp_remove() - ISHTP client driver remove callback * @cl_device: ISHTP client device instance * * Return: 0 */ static void cros_ec_ishtp_remove(struct ishtp_cl_device *cl_device) { … } /** * cros_ec_ishtp_reset() - ISHTP client driver reset callback * @cl_device: ISHTP client device instance * * Return: 0 */ static int cros_ec_ishtp_reset(struct ishtp_cl_device *cl_device) { … } /** * cros_ec_ishtp_suspend() - ISHTP client driver suspend callback * @device: device instance * * Return: 0 for success, negative error code for failure. */ static int __maybe_unused cros_ec_ishtp_suspend(struct device *device) { … } /** * cros_ec_ishtp_resume() - ISHTP client driver resume callback * @device: device instance * * Return: 0 for success, negative error code for failure. */ static int __maybe_unused cros_ec_ishtp_resume(struct device *device) { … } static SIMPLE_DEV_PM_OPS(cros_ec_ishtp_pm_ops, cros_ec_ishtp_suspend, cros_ec_ishtp_resume); static struct ishtp_cl_driver cros_ec_ishtp_driver = …; static int __init cros_ec_ishtp_mod_init(void) { … } static void __exit cros_ec_ishtp_mod_exit(void) { … } module_init(…) …; module_exit(cros_ec_ishtp_mod_exit); MODULE_DESCRIPTION(…) …; MODULE_AUTHOR(…) …; MODULE_LICENSE(…) …;