// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2015, Sony Mobile Communications AB. * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. */ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/mailbox_client.h> #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/sched.h> #include <linux/sizes.h> #include <linux/slab.h> #include <linux/soc/qcom/smem.h> #include <linux/wait.h> #include <linux/rpmsg.h> #include <linux/rpmsg/qcom_smd.h> #include "rpmsg_internal.h" /* * The Qualcomm Shared Memory communication solution provides point-to-point * channels for clients to send and receive streaming or packet based data. * * Each channel consists of a control item (channel info) and a ring buffer * pair. The channel info carry information related to channel state, flow * control and the offsets within the ring buffer. * * All allocated channels are listed in an allocation table, identifying the * pair of items by name, type and remote processor. * * Upon creating a new channel the remote processor allocates channel info and * ring buffer items from the smem heap and populate the allocation table. An * interrupt is sent to the other end of the channel and a scan for new * channels should be done. A channel never goes away, it will only change * state. * * The remote processor signals it intent for bring up the communication * channel by setting the state of its end of the channel to "opening" and * sends out an interrupt. We detect this change and register a smd device to * consume the channel. Upon finding a consumer we finish the handshake and the * channel is up. * * Upon closing a channel, the remote processor will update the state of its * end of the channel and signal us, we will then unregister any attached * device and close our end of the channel. * * Devices attached to a channel can use the qcom_smd_send function to push * data to the channel, this is done by copying the data into the tx ring * buffer, updating the pointers in the channel info and signaling the remote * processor. * * The remote processor does the equivalent when it transfer data and upon * receiving the interrupt we check the channel info for new data and delivers * this to the attached device. If the device is not ready to receive the data * we leave it in the ring buffer for now. */ struct smd_channel_info; struct smd_channel_info_pair; struct smd_channel_info_word; struct smd_channel_info_word_pair; static const struct rpmsg_endpoint_ops qcom_smd_endpoint_ops; #define SMD_ALLOC_TBL_COUNT … #define SMD_ALLOC_TBL_SIZE … /* * This lists the various smem heap items relevant for the allocation table and * smd channel entries. */ static const struct { … } smem_items[SMD_ALLOC_TBL_COUNT] = …; /** * struct qcom_smd_edge - representing a remote processor * @dev: device associated with this edge * @name: name of this edge * @of_node: of_node handle for information related to this edge * @edge_id: identifier of this edge * @remote_pid: identifier of remote processor * @irq: interrupt for signals on this edge * @ipc_regmap: regmap handle holding the outgoing ipc register * @ipc_offset: offset within @ipc_regmap of the register for ipc * @ipc_bit: bit in the register at @ipc_offset of @ipc_regmap * @mbox_client: mailbox client handle * @mbox_chan: apcs ipc mailbox channel handle * @channels: list of all channels detected on this edge * @channels_lock: guard for modifications of @channels * @allocated: array of bitmaps representing already allocated channels * @smem_available: last available amount of smem triggering a channel scan * @new_channel_event: wait queue for new channel events * @scan_work: work item for discovering new channels * @state_work: work item for edge state changes */ struct qcom_smd_edge { … }; /* * SMD channel states. */ enum smd_channel_state { … }; struct qcom_smd_device { … }; struct qcom_smd_endpoint { … }; #define to_smd_device(r) … #define to_smd_edge(d) … #define to_smd_endpoint(e) … /** * struct qcom_smd_channel - smd channel struct * @edge: qcom_smd_edge this channel is living on * @qsept: reference to a associated smd endpoint * @registered: flag to indicate if the channel is registered * @name: name of the channel * @state: local state of the channel * @remote_state: remote state of the channel * @state_change_event: state change event * @info: byte aligned outgoing/incoming channel info * @info_word: word aligned outgoing/incoming channel info * @tx_lock: lock to make writes to the channel mutually exclusive * @fblockread_event: wakeup event tied to tx fBLOCKREADINTR * @tx_fifo: pointer to the outgoing ring buffer * @rx_fifo: pointer to the incoming ring buffer * @fifo_size: size of each ring buffer * @bounce_buffer: bounce buffer for reading wrapped packets * @cb: callback function registered for this channel * @recv_lock: guard for rx info modifications and cb pointer * @pkt_size: size of the currently handled packet * @drvdata: driver private data * @list: lite entry for @channels in qcom_smd_edge */ struct qcom_smd_channel { … }; /* * Format of the smd_info smem items, for byte aligned channels. */ struct smd_channel_info { … }; struct smd_channel_info_pair { … }; /* * Format of the smd_info smem items, for word aligned channels. */ struct smd_channel_info_word { … }; struct smd_channel_info_word_pair { … }; #define GET_RX_CHANNEL_FLAG(channel, param) … #define GET_RX_CHANNEL_INFO(channel, param) … #define SET_RX_CHANNEL_FLAG(channel, param, value) … #define SET_RX_CHANNEL_INFO(channel, param, value) … #define GET_TX_CHANNEL_FLAG(channel, param) … #define GET_TX_CHANNEL_INFO(channel, param) … #define SET_TX_CHANNEL_FLAG(channel, param, value) … #define SET_TX_CHANNEL_INFO(channel, param, value) … /** * struct qcom_smd_alloc_entry - channel allocation entry * @name: channel name * @cid: channel index * @flags: channel flags and edge id * @ref_count: reference count of the channel */ struct qcom_smd_alloc_entry { … } __packed; #define SMD_CHANNEL_FLAGS_EDGE_MASK … #define SMD_CHANNEL_FLAGS_STREAM … #define SMD_CHANNEL_FLAGS_PACKET … /* * Each smd packet contains a 20 byte header, with the first 4 being the length * of the packet. */ #define SMD_PACKET_HEADER_LEN … /* * Signal the remote processor associated with 'channel'. */ static void qcom_smd_signal_channel(struct qcom_smd_channel *channel) { … } /* * Initialize the tx channel info */ static void qcom_smd_channel_reset(struct qcom_smd_channel *channel) { … } /* * Set the callback for a channel, with appropriate locking */ static void qcom_smd_channel_set_callback(struct qcom_smd_channel *channel, rpmsg_rx_cb_t cb) { struct rpmsg_endpoint *ept = &channel->qsept->ept; unsigned long flags; spin_lock_irqsave(&channel->recv_lock, flags); ept->cb = cb; spin_unlock_irqrestore(&channel->recv_lock, flags); }; /* * Calculate the amount of data available in the rx fifo */ static size_t qcom_smd_channel_get_rx_avail(struct qcom_smd_channel *channel) { … } /* * Set tx channel state and inform the remote processor */ static void qcom_smd_channel_set_state(struct qcom_smd_channel *channel, int state) { … } /* * Copy count bytes of data using 32bit accesses, if that's required. */ static void smd_copy_to_fifo(void __iomem *dst, const void *src, size_t count, bool word_aligned) { … } /* * Copy count bytes of data using 32bit accesses, if that is required. */ static void smd_copy_from_fifo(void *dst, const void __iomem *src, size_t count, bool word_aligned) { … } /* * Read count bytes of data from the rx fifo into buf, but don't advance the * tail. */ static size_t qcom_smd_channel_peek(struct qcom_smd_channel *channel, void *buf, size_t count) { … } /* * Advance the rx tail by count bytes. */ static void qcom_smd_channel_advance(struct qcom_smd_channel *channel, size_t count) { … } /* * Read out a single packet from the rx fifo and deliver it to the device */ static int qcom_smd_channel_recv_single(struct qcom_smd_channel *channel) { … } /* * Per channel interrupt handling */ static bool qcom_smd_channel_intr(struct qcom_smd_channel *channel) { … } /* * The edge interrupts are triggered by the remote processor on state changes, * channel info updates or when new channels are created. */ static irqreturn_t qcom_smd_edge_intr(int irq, void *data) { … } /* * Calculate how much space is available in the tx fifo. */ static size_t qcom_smd_get_tx_avail(struct qcom_smd_channel *channel) { … } /* * Write count bytes of data into channel, possibly wrapping in the ring buffer */ static int qcom_smd_write_fifo(struct qcom_smd_channel *channel, const void *data, size_t count) { … } /** * __qcom_smd_send - write data to smd channel * @channel: channel handle * @data: buffer of data to write * @len: number of bytes to write * @wait: flag to indicate if write can wait * * This is a blocking write of len bytes into the channel's tx ring buffer and * signal the remote end. It will sleep until there is enough space available * in the tx buffer, utilizing the fBLOCKREADINTR signaling mechanism to avoid * polling. */ static int __qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len, bool wait) { … } /* * Helper for opening a channel */ static int qcom_smd_channel_open(struct qcom_smd_channel *channel, rpmsg_rx_cb_t cb) { … } /* * Helper for closing and resetting a channel */ static void qcom_smd_channel_close(struct qcom_smd_channel *channel) { … } static struct qcom_smd_channel * qcom_smd_find_channel(struct qcom_smd_edge *edge, const char *name) { … } static void __ept_release(struct kref *kref) { … } static struct rpmsg_endpoint *qcom_smd_create_ept(struct rpmsg_device *rpdev, rpmsg_rx_cb_t cb, void *priv, struct rpmsg_channel_info chinfo) { … } static void qcom_smd_destroy_ept(struct rpmsg_endpoint *ept) { … } static int qcom_smd_send(struct rpmsg_endpoint *ept, void *data, int len) { … } static int qcom_smd_trysend(struct rpmsg_endpoint *ept, void *data, int len) { … } static int qcom_smd_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) { … } static int qcom_smd_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) { … } static __poll_t qcom_smd_poll(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait) { … } /* * Finds the device_node for the smd child interested in this channel. */ static struct device_node *qcom_smd_match_channel(struct device_node *edge_node, const char *channel) { … } static int qcom_smd_announce_create(struct rpmsg_device *rpdev) { … } static const struct rpmsg_device_ops qcom_smd_device_ops = …; static const struct rpmsg_endpoint_ops qcom_smd_endpoint_ops = …; static void qcom_smd_release_device(struct device *dev) { … } /* * Create a smd client device for channel that is being opened. */ static int qcom_smd_create_device(struct qcom_smd_channel *channel) { … } static int qcom_smd_create_chrdev(struct qcom_smd_edge *edge) { … } /* * Allocate the qcom_smd_channel object for a newly found smd channel, * retrieving and validating the smem items involved. */ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *edge, unsigned smem_info_item, unsigned smem_fifo_item, char *name) { … } /* * Scans the allocation table for any newly allocated channels, calls * qcom_smd_create_channel() to create representations of these and add * them to the edge's list of channels. */ static void qcom_channel_scan_worker(struct work_struct *work) { … } /* * This per edge worker scans smem for any new channels and register these. It * then scans all registered channels for state changes that should be handled * by creating or destroying smd client devices for the registered channels. * * LOCKING: edge->channels_lock only needs to cover the list operations, as the * worker is killed before any channels are deallocated */ static void qcom_channel_state_worker(struct work_struct *work) { … } /* * Parses an of_node describing an edge. */ static int qcom_smd_parse_edge(struct device *dev, struct device_node *node, struct qcom_smd_edge *edge) { … } /* * Release function for an edge. * Reset the state of each associated channel and free the edge context. */ static void qcom_smd_edge_release(struct device *dev) { … } static ssize_t rpmsg_name_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(rpmsg_name); static struct attribute *qcom_smd_edge_attrs[] = …; ATTRIBUTE_GROUPS(…); /** * qcom_smd_register_edge() - register an edge based on an device_node * @parent: parent device for the edge * @node: device_node describing the edge * * Return: an edge reference, or negative ERR_PTR() on failure. */ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, struct device_node *node) { … } EXPORT_SYMBOL(…); static int qcom_smd_remove_device(struct device *dev, void *data) { … } /** * qcom_smd_unregister_edge() - release an edge and its children * @edge: edge reference acquired from qcom_smd_register_edge */ void qcom_smd_unregister_edge(struct qcom_smd_edge *edge) { … } EXPORT_SYMBOL(…); static int qcom_smd_probe(struct platform_device *pdev) { … } static int qcom_smd_remove_edge(struct device *dev, void *data) { … } /* * Shut down all smd clients by making sure that each edge stops processing * events and scanning for new channels, then call destroy on the devices. */ static void qcom_smd_remove(struct platform_device *pdev) { … } static const struct of_device_id qcom_smd_of_match[] = …; MODULE_DEVICE_TABLE(of, qcom_smd_of_match); static struct platform_driver qcom_smd_driver = …; static int __init qcom_smd_init(void) { … } arch_initcall(qcom_smd_init); static void __exit qcom_smd_exit(void) { … } module_exit(qcom_smd_exit); MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;