1622 lines · 41.0 KB · C
RawDownload
// 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.