// 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 2#define SMD_ALLOC_TBL_SIZE 64 /* * This lists the various smem heap items relevant for the allocation table and * smd channel entries. */static const struct { unsigned alloc_tbl_id; unsigned info_base_id; unsigned fifo_base_id;} smem_items[SMD_ALLOC_TBL_COUNT] = { { .alloc_tbl_id = 13, .info_base_id = 14, .fifo_base_id = 338 }, { .alloc_tbl_id = 266, .info_base_id = 138, .fifo_base_id = 202, },}; /** * 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 { struct device dev; const char *name; struct device_node *of_node; unsigned edge_id; unsigned remote_pid; int irq; struct regmap *ipc_regmap; int ipc_offset; int ipc_bit; struct mbox_client mbox_client; struct mbox_chan *mbox_chan; struct list_head channels; spinlock_t channels_lock; DECLARE_BITMAP(allocated[SMD_ALLOC_TBL_COUNT], SMD_ALLOC_TBL_SIZE); unsigned smem_available; wait_queue_head_t new_channel_event; struct work_struct scan_work; struct work_struct state_work;}; /* * SMD channel states. */enum smd_channel_state { SMD_CHANNEL_CLOSED, SMD_CHANNEL_OPENING, SMD_CHANNEL_OPENED, SMD_CHANNEL_FLUSHING, SMD_CHANNEL_CLOSING, SMD_CHANNEL_RESET, SMD_CHANNEL_RESET_OPENING}; struct qcom_smd_device { struct rpmsg_device rpdev; struct qcom_smd_edge *edge;}; struct qcom_smd_endpoint { struct rpmsg_endpoint ept; struct qcom_smd_channel *qsch;}; #define to_smd_device(r) container_of(r, struct qcom_smd_device, rpdev)#define to_smd_edge(d) container_of(d, struct qcom_smd_edge, dev)#define to_smd_endpoint(e) container_of(e, struct qcom_smd_endpoint, ept) /** * 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 { struct qcom_smd_edge *edge; struct qcom_smd_endpoint *qsept; bool registered; char *name; enum smd_channel_state state; enum smd_channel_state remote_state; wait_queue_head_t state_change_event; struct smd_channel_info_pair *info; struct smd_channel_info_word_pair *info_word; spinlock_t tx_lock; wait_queue_head_t fblockread_event; void *tx_fifo; void *rx_fifo; int fifo_size; void *bounce_buffer; spinlock_t recv_lock; int pkt_size; void *drvdata; struct list_head list;}; /* * Format of the smd_info smem items, for byte aligned channels. */struct smd_channel_info { __le32 state; u8 fDSR; u8 fCTS; u8 fCD; u8 fRI; u8 fHEAD; u8 fTAIL; u8 fSTATE; u8 fBLOCKREADINTR; __le32 tail; __le32 head;}; struct smd_channel_info_pair { struct smd_channel_info tx; struct smd_channel_info rx;}; /* * Format of the smd_info smem items, for word aligned channels. */struct smd_channel_info_word { __le32 state; __le32 fDSR; __le32 fCTS; __le32 fCD; __le32 fRI; __le32 fHEAD; __le32 fTAIL; __le32 fSTATE; __le32 fBLOCKREADINTR; __le32 tail; __le32 head;}; struct smd_channel_info_word_pair { struct smd_channel_info_word tx; struct smd_channel_info_word rx;}; #define GET_RX_CHANNEL_FLAG(channel, param) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u8)); \ channel->info_word ? \ le32_to_cpu(channel->info_word->rx.param) : \ channel->info->rx.param; \ }) #define GET_RX_CHANNEL_INFO(channel, param) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u32)); \ le32_to_cpu(channel->info_word ? \ channel->info_word->rx.param : \ channel->info->rx.param); \ }) #define SET_RX_CHANNEL_FLAG(channel, param, value) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u8)); \ if (channel->info_word) \ channel->info_word->rx.param = cpu_to_le32(value); \ else \ channel->info->rx.param = value; \ }) #define SET_RX_CHANNEL_INFO(channel, param, value) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u32)); \ if (channel->info_word) \ channel->info_word->rx.param = cpu_to_le32(value); \ else \ channel->info->rx.param = cpu_to_le32(value); \ }) #define GET_TX_CHANNEL_FLAG(channel, param) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u8)); \ channel->info_word ? \ le32_to_cpu(channel->info_word->tx.param) : \ channel->info->tx.param; \ }) #define GET_TX_CHANNEL_INFO(channel, param) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u32)); \ le32_to_cpu(channel->info_word ? \ channel->info_word->tx.param : \ channel->info->tx.param); \ }) #define SET_TX_CHANNEL_FLAG(channel, param, value) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u8)); \ if (channel->info_word) \ channel->info_word->tx.param = cpu_to_le32(value); \ else \ channel->info->tx.param = value; \ }) #define SET_TX_CHANNEL_INFO(channel, param, value) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u32)); \ if (channel->info_word) \ channel->info_word->tx.param = cpu_to_le32(value); \ else \ channel->info->tx.param = cpu_to_le32(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 { u8 name[20]; __le32 cid; __le32 flags; __le32 ref_count;} __packed; #define SMD_CHANNEL_FLAGS_EDGE_MASK 0xff#define SMD_CHANNEL_FLAGS_STREAM BIT(8)#define SMD_CHANNEL_FLAGS_PACKET BIT(9) /* * Each smd packet contains a 20 byte header, with the first 4 being the length * of the packet. */#define SMD_PACKET_HEADER_LEN 20 /* * Signal the remote processor associated with 'channel'. */static void qcom_smd_signal_channel(struct qcom_smd_channel *channel){ struct qcom_smd_edge *edge = channel->edge; if (edge->mbox_chan) { /* * We can ignore a failing mbox_send_message() as the only * possible cause is that the FIFO in the framework is full of * other writes to the same bit. */ mbox_send_message(edge->mbox_chan, NULL); mbox_client_txdone(edge->mbox_chan, 0); } else { regmap_write(edge->ipc_regmap, edge->ipc_offset, BIT(edge->ipc_bit)); }} /* * Initialize the tx channel info */static void qcom_smd_channel_reset(struct qcom_smd_channel *channel){ SET_TX_CHANNEL_INFO(channel, state, SMD_CHANNEL_CLOSED); SET_TX_CHANNEL_FLAG(channel, fDSR, 0); SET_TX_CHANNEL_FLAG(channel, fCTS, 0); SET_TX_CHANNEL_FLAG(channel, fCD, 0); SET_TX_CHANNEL_FLAG(channel, fRI, 0); SET_TX_CHANNEL_FLAG(channel, fHEAD, 0); SET_TX_CHANNEL_FLAG(channel, fTAIL, 0); SET_TX_CHANNEL_FLAG(channel, fSTATE, 1); SET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR, 1); SET_TX_CHANNEL_INFO(channel, head, 0); SET_RX_CHANNEL_INFO(channel, tail, 0); qcom_smd_signal_channel(channel); channel->state = SMD_CHANNEL_CLOSED; channel->pkt_size = 0;} /* * 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){ unsigned head; unsigned tail; head = GET_RX_CHANNEL_INFO(channel, head); tail = GET_RX_CHANNEL_INFO(channel, tail); return (head - tail) & (channel->fifo_size - 1);} /* * Set tx channel state and inform the remote processor */static void qcom_smd_channel_set_state(struct qcom_smd_channel *channel, int state){ struct qcom_smd_edge *edge = channel->edge; bool is_open = state == SMD_CHANNEL_OPENED; if (channel->state == state) return; dev_dbg(&edge->dev, "set_state(%s, %d)\n", channel->name, state); SET_TX_CHANNEL_FLAG(channel, fDSR, is_open); SET_TX_CHANNEL_FLAG(channel, fCTS, is_open); SET_TX_CHANNEL_FLAG(channel, fCD, is_open); SET_TX_CHANNEL_INFO(channel, state, state); SET_TX_CHANNEL_FLAG(channel, fSTATE, 1); channel->state = state; qcom_smd_signal_channel(channel);} /* * 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){ if (word_aligned) { __iowrite32_copy(dst, src, count / sizeof(u32)); } else { memcpy_toio(dst, src, count); }} /* * 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){ if (word_aligned) { __ioread32_copy(dst, src, count / sizeof(u32)); } else { memcpy_fromio(dst, src, count); }} /* * 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){ bool word_aligned;Lines 1–500 of 1622. Load more
// 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 2 #define SMD_ALLOC_TBL_SIZE 64 /* * This lists the various smem heap items relevant for the allocation table and * smd channel entries. */ static const struct { unsigned alloc_tbl_id; unsigned info_base_id; unsigned fifo_base_id; } smem_items[SMD_ALLOC_TBL_COUNT] = { { .alloc_tbl_id = 13, .info_base_id = 14, .fifo_base_id = 338 }, { .alloc_tbl_id = 266, .info_base_id = 138, .fifo_base_id = 202, }, }; /** * 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 { struct device dev; const char *name; struct device_node *of_node; unsigned edge_id; unsigned remote_pid; int irq; struct regmap *ipc_regmap; int ipc_offset; int ipc_bit; struct mbox_client mbox_client; struct mbox_chan *mbox_chan; struct list_head channels; spinlock_t channels_lock; DECLARE_BITMAP(allocated[SMD_ALLOC_TBL_COUNT], SMD_ALLOC_TBL_SIZE); unsigned smem_available; wait_queue_head_t new_channel_event; struct work_struct scan_work; struct work_struct state_work; }; /* * SMD channel states. */ enum smd_channel_state { SMD_CHANNEL_CLOSED, SMD_CHANNEL_OPENING, SMD_CHANNEL_OPENED, SMD_CHANNEL_FLUSHING, SMD_CHANNEL_CLOSING, SMD_CHANNEL_RESET, SMD_CHANNEL_RESET_OPENING }; struct qcom_smd_device { struct rpmsg_device rpdev; struct qcom_smd_edge *edge; }; struct qcom_smd_endpoint { struct rpmsg_endpoint ept; struct qcom_smd_channel *qsch; }; #define to_smd_device(r) container_of(r, struct qcom_smd_device, rpdev) #define to_smd_edge(d) container_of(d, struct qcom_smd_edge, dev) #define to_smd_endpoint(e) container_of(e, struct qcom_smd_endpoint, ept) /** * 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 { struct qcom_smd_edge *edge; struct qcom_smd_endpoint *qsept; bool registered; char *name; enum smd_channel_state state; enum smd_channel_state remote_state; wait_queue_head_t state_change_event; struct smd_channel_info_pair *info; struct smd_channel_info_word_pair *info_word; spinlock_t tx_lock; wait_queue_head_t fblockread_event; void *tx_fifo; void *rx_fifo; int fifo_size; void *bounce_buffer; spinlock_t recv_lock; int pkt_size; void *drvdata; struct list_head list; }; /* * Format of the smd_info smem items, for byte aligned channels. */ struct smd_channel_info { __le32 state; u8 fDSR; u8 fCTS; u8 fCD; u8 fRI; u8 fHEAD; u8 fTAIL; u8 fSTATE; u8 fBLOCKREADINTR; __le32 tail; __le32 head; }; struct smd_channel_info_pair { struct smd_channel_info tx; struct smd_channel_info rx; }; /* * Format of the smd_info smem items, for word aligned channels. */ struct smd_channel_info_word { __le32 state; __le32 fDSR; __le32 fCTS; __le32 fCD; __le32 fRI; __le32 fHEAD; __le32 fTAIL; __le32 fSTATE; __le32 fBLOCKREADINTR; __le32 tail; __le32 head; }; struct smd_channel_info_word_pair { struct smd_channel_info_word tx; struct smd_channel_info_word rx; }; #define GET_RX_CHANNEL_FLAG(channel, param) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u8)); \ channel->info_word ? \ le32_to_cpu(channel->info_word->rx.param) : \ channel->info->rx.param; \ }) #define GET_RX_CHANNEL_INFO(channel, param) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u32)); \ le32_to_cpu(channel->info_word ? \ channel->info_word->rx.param : \ channel->info->rx.param); \ }) #define SET_RX_CHANNEL_FLAG(channel, param, value) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u8)); \ if (channel->info_word) \ channel->info_word->rx.param = cpu_to_le32(value); \ else \ channel->info->rx.param = value; \ }) #define SET_RX_CHANNEL_INFO(channel, param, value) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u32)); \ if (channel->info_word) \ channel->info_word->rx.param = cpu_to_le32(value); \ else \ channel->info->rx.param = cpu_to_le32(value); \ }) #define GET_TX_CHANNEL_FLAG(channel, param) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u8)); \ channel->info_word ? \ le32_to_cpu(channel->info_word->tx.param) : \ channel->info->tx.param; \ }) #define GET_TX_CHANNEL_INFO(channel, param) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u32)); \ le32_to_cpu(channel->info_word ? \ channel->info_word->tx.param : \ channel->info->tx.param); \ }) #define SET_TX_CHANNEL_FLAG(channel, param, value) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u8)); \ if (channel->info_word) \ channel->info_word->tx.param = cpu_to_le32(value); \ else \ channel->info->tx.param = value; \ }) #define SET_TX_CHANNEL_INFO(channel, param, value) \ ({ \ BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u32)); \ if (channel->info_word) \ channel->info_word->tx.param = cpu_to_le32(value); \ else \ channel->info->tx.param = cpu_to_le32(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 { u8 name[20]; __le32 cid; __le32 flags; __le32 ref_count; } __packed; #define SMD_CHANNEL_FLAGS_EDGE_MASK 0xff #define SMD_CHANNEL_FLAGS_STREAM BIT(8) #define SMD_CHANNEL_FLAGS_PACKET BIT(9) /* * Each smd packet contains a 20 byte header, with the first 4 being the length * of the packet. */ #define SMD_PACKET_HEADER_LEN 20 /* * Signal the remote processor associated with 'channel'. */ static void qcom_smd_signal_channel(struct qcom_smd_channel *channel) { struct qcom_smd_edge *edge = channel->edge; if (edge->mbox_chan) { /* * We can ignore a failing mbox_send_message() as the only * possible cause is that the FIFO in the framework is full of * other writes to the same bit. */ mbox_send_message(edge->mbox_chan, NULL); mbox_client_txdone(edge->mbox_chan, 0); } else { regmap_write(edge->ipc_regmap, edge->ipc_offset, BIT(edge->ipc_bit)); } } /* * Initialize the tx channel info */ static void qcom_smd_channel_reset(struct qcom_smd_channel *channel) { SET_TX_CHANNEL_INFO(channel, state, SMD_CHANNEL_CLOSED); SET_TX_CHANNEL_FLAG(channel, fDSR, 0); SET_TX_CHANNEL_FLAG(channel, fCTS, 0); SET_TX_CHANNEL_FLAG(channel, fCD, 0); SET_TX_CHANNEL_FLAG(channel, fRI, 0); SET_TX_CHANNEL_FLAG(channel, fHEAD, 0); SET_TX_CHANNEL_FLAG(channel, fTAIL, 0); SET_TX_CHANNEL_FLAG(channel, fSTATE, 1); SET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR, 1); SET_TX_CHANNEL_INFO(channel, head, 0); SET_RX_CHANNEL_INFO(channel, tail, 0); qcom_smd_signal_channel(channel); channel->state = SMD_CHANNEL_CLOSED; channel->pkt_size = 0; } /* * 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) { unsigned head; unsigned tail; head = GET_RX_CHANNEL_INFO(channel, head); tail = GET_RX_CHANNEL_INFO(channel, tail); return (head - tail) & (channel->fifo_size - 1); } /* * Set tx channel state and inform the remote processor */ static void qcom_smd_channel_set_state(struct qcom_smd_channel *channel, int state) { struct qcom_smd_edge *edge = channel->edge; bool is_open = state == SMD_CHANNEL_OPENED; if (channel->state == state) return; dev_dbg(&edge->dev, "set_state(%s, %d)\n", channel->name, state); SET_TX_CHANNEL_FLAG(channel, fDSR, is_open); SET_TX_CHANNEL_FLAG(channel, fCTS, is_open); SET_TX_CHANNEL_FLAG(channel, fCD, is_open); SET_TX_CHANNEL_INFO(channel, state, state); SET_TX_CHANNEL_FLAG(channel, fSTATE, 1); channel->state = state; qcom_smd_signal_channel(channel); } /* * 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) { if (word_aligned) { __iowrite32_copy(dst, src, count / sizeof(u32)); } else { memcpy_toio(dst, src, count); } } /* * 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) { if (word_aligned) { __ioread32_copy(dst, src, count / sizeof(u32)); } else { memcpy_fromio(dst, src, count); } } /* * 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) { bool word_aligned;