xref: /openbmc/linux/drivers/hsi/clients/cmt_speech.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27f62fe8aSKai Vehmanen /*
37f62fe8aSKai Vehmanen  * cmt_speech.c - HSI CMT speech driver
47f62fe8aSKai Vehmanen  *
57f62fe8aSKai Vehmanen  * Copyright (C) 2008,2009,2010 Nokia Corporation. All rights reserved.
67f62fe8aSKai Vehmanen  *
77f62fe8aSKai Vehmanen  * Contact: Kai Vehmanen <kai.vehmanen@nokia.com>
87f62fe8aSKai Vehmanen  * Original author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
97f62fe8aSKai Vehmanen  */
107f62fe8aSKai Vehmanen 
117f62fe8aSKai Vehmanen #include <linux/errno.h>
127f62fe8aSKai Vehmanen #include <linux/module.h>
137f62fe8aSKai Vehmanen #include <linux/types.h>
147f62fe8aSKai Vehmanen #include <linux/init.h>
157f62fe8aSKai Vehmanen #include <linux/device.h>
167f62fe8aSKai Vehmanen #include <linux/miscdevice.h>
177f62fe8aSKai Vehmanen #include <linux/mm.h>
187f62fe8aSKai Vehmanen #include <linux/slab.h>
197f62fe8aSKai Vehmanen #include <linux/fs.h>
207f62fe8aSKai Vehmanen #include <linux/poll.h>
21174cd4b1SIngo Molnar #include <linux/sched/signal.h>
227f62fe8aSKai Vehmanen #include <linux/ioctl.h>
237f62fe8aSKai Vehmanen #include <linux/uaccess.h>
247f62fe8aSKai Vehmanen #include <linux/pm_qos.h>
257f62fe8aSKai Vehmanen #include <linux/hsi/hsi.h>
267f62fe8aSKai Vehmanen #include <linux/hsi/ssi_protocol.h>
277f62fe8aSKai Vehmanen #include <linux/hsi/cs-protocol.h>
287f62fe8aSKai Vehmanen 
297f62fe8aSKai Vehmanen #define CS_MMAP_SIZE	PAGE_SIZE
307f62fe8aSKai Vehmanen 
317f62fe8aSKai Vehmanen struct char_queue {
327f62fe8aSKai Vehmanen 	struct list_head	list;
337f62fe8aSKai Vehmanen 	u32			msg;
347f62fe8aSKai Vehmanen };
357f62fe8aSKai Vehmanen 
367f62fe8aSKai Vehmanen struct cs_char {
377f62fe8aSKai Vehmanen 	unsigned int		opened;
387f62fe8aSKai Vehmanen 	struct hsi_client	*cl;
397f62fe8aSKai Vehmanen 	struct cs_hsi_iface	*hi;
407f62fe8aSKai Vehmanen 	struct list_head	chardev_queue;
417f62fe8aSKai Vehmanen 	struct list_head	dataind_queue;
427f62fe8aSKai Vehmanen 	int			dataind_pending;
437f62fe8aSKai Vehmanen 	/* mmap things */
447f62fe8aSKai Vehmanen 	unsigned long		mmap_base;
457f62fe8aSKai Vehmanen 	unsigned long		mmap_size;
467f62fe8aSKai Vehmanen 	spinlock_t		lock;
477f62fe8aSKai Vehmanen 	struct fasync_struct	*async_queue;
487f62fe8aSKai Vehmanen 	wait_queue_head_t	wait;
497f62fe8aSKai Vehmanen 	/* hsi channel ids */
507f62fe8aSKai Vehmanen 	int                     channel_id_cmd;
517f62fe8aSKai Vehmanen 	int                     channel_id_data;
527f62fe8aSKai Vehmanen };
537f62fe8aSKai Vehmanen 
547f62fe8aSKai Vehmanen #define SSI_CHANNEL_STATE_READING	1
557f62fe8aSKai Vehmanen #define SSI_CHANNEL_STATE_WRITING	(1 << 1)
567f62fe8aSKai Vehmanen #define SSI_CHANNEL_STATE_POLL		(1 << 2)
577f62fe8aSKai Vehmanen #define SSI_CHANNEL_STATE_ERROR		(1 << 3)
587f62fe8aSKai Vehmanen 
597f62fe8aSKai Vehmanen #define TARGET_MASK			0xf000000
607f62fe8aSKai Vehmanen #define TARGET_REMOTE			(1 << CS_DOMAIN_SHIFT)
617f62fe8aSKai Vehmanen #define TARGET_LOCAL			0
627f62fe8aSKai Vehmanen 
637f62fe8aSKai Vehmanen /* Number of pre-allocated commands buffers */
647f62fe8aSKai Vehmanen #define CS_MAX_CMDS		        4
657f62fe8aSKai Vehmanen 
667f62fe8aSKai Vehmanen /*
677f62fe8aSKai Vehmanen  * During data transfers, transactions must be handled
687f62fe8aSKai Vehmanen  * within 20ms (fixed value in cmtspeech HSI protocol)
697f62fe8aSKai Vehmanen  */
707f62fe8aSKai Vehmanen #define CS_QOS_LATENCY_FOR_DATA_USEC	20000
717f62fe8aSKai Vehmanen 
727f62fe8aSKai Vehmanen /* Timeout to wait for pending HSI transfers to complete */
737f62fe8aSKai Vehmanen #define CS_HSI_TRANSFER_TIMEOUT_MS      500
747f62fe8aSKai Vehmanen 
757f62fe8aSKai Vehmanen 
767f62fe8aSKai Vehmanen #define RX_PTR_BOUNDARY_SHIFT		8
777f62fe8aSKai Vehmanen #define RX_PTR_MAX_SHIFT		(RX_PTR_BOUNDARY_SHIFT + \
787f62fe8aSKai Vehmanen 						CS_MAX_BUFFERS_SHIFT)
797f62fe8aSKai Vehmanen struct cs_hsi_iface {
807f62fe8aSKai Vehmanen 	struct hsi_client		*cl;
817f62fe8aSKai Vehmanen 	struct hsi_client		*master;
827f62fe8aSKai Vehmanen 
837f62fe8aSKai Vehmanen 	unsigned int			iface_state;
847f62fe8aSKai Vehmanen 	unsigned int			wakeline_state;
857f62fe8aSKai Vehmanen 	unsigned int			control_state;
867f62fe8aSKai Vehmanen 	unsigned int			data_state;
877f62fe8aSKai Vehmanen 
887f62fe8aSKai Vehmanen 	/* state exposed to application */
897f62fe8aSKai Vehmanen 	struct cs_mmap_config_block	*mmap_cfg;
907f62fe8aSKai Vehmanen 
917f62fe8aSKai Vehmanen 	unsigned long			mmap_base;
927f62fe8aSKai Vehmanen 	unsigned long			mmap_size;
937f62fe8aSKai Vehmanen 
947f62fe8aSKai Vehmanen 	unsigned int			rx_slot;
957f62fe8aSKai Vehmanen 	unsigned int			tx_slot;
967f62fe8aSKai Vehmanen 
977f62fe8aSKai Vehmanen 	/* note: for security reasons, we do not trust the contents of
987f62fe8aSKai Vehmanen 	 * mmap_cfg, but instead duplicate the variables here */
997f62fe8aSKai Vehmanen 	unsigned int			buf_size;
1007f62fe8aSKai Vehmanen 	unsigned int			rx_bufs;
1017f62fe8aSKai Vehmanen 	unsigned int			tx_bufs;
1027f62fe8aSKai Vehmanen 	unsigned int			rx_ptr_boundary;
1037f62fe8aSKai Vehmanen 	unsigned int			rx_offsets[CS_MAX_BUFFERS];
1047f62fe8aSKai Vehmanen 	unsigned int			tx_offsets[CS_MAX_BUFFERS];
1057f62fe8aSKai Vehmanen 
1067f62fe8aSKai Vehmanen 	/* size of aligned memory blocks */
1077f62fe8aSKai Vehmanen 	unsigned int			slot_size;
1087f62fe8aSKai Vehmanen 	unsigned int			flags;
1097f62fe8aSKai Vehmanen 
1107f62fe8aSKai Vehmanen 	struct list_head		cmdqueue;
1117f62fe8aSKai Vehmanen 
1127f62fe8aSKai Vehmanen 	struct hsi_msg			*data_rx_msg;
1137f62fe8aSKai Vehmanen 	struct hsi_msg			*data_tx_msg;
1147f62fe8aSKai Vehmanen 	wait_queue_head_t		datawait;
1157f62fe8aSKai Vehmanen 
1167f62fe8aSKai Vehmanen 	struct pm_qos_request           pm_qos_req;
1177f62fe8aSKai Vehmanen 
1187f62fe8aSKai Vehmanen 	spinlock_t			lock;
1197f62fe8aSKai Vehmanen };
1207f62fe8aSKai Vehmanen 
1217f62fe8aSKai Vehmanen static struct cs_char cs_char_data;
1227f62fe8aSKai Vehmanen 
1237f62fe8aSKai Vehmanen static void cs_hsi_read_on_control(struct cs_hsi_iface *hi);
1247f62fe8aSKai Vehmanen static void cs_hsi_read_on_data(struct cs_hsi_iface *hi);
1257f62fe8aSKai Vehmanen 
rx_ptr_shift_too_big(void)1267f62fe8aSKai Vehmanen static inline void rx_ptr_shift_too_big(void)
1277f62fe8aSKai Vehmanen {
1287f62fe8aSKai Vehmanen 	BUILD_BUG_ON((1LLU << RX_PTR_MAX_SHIFT) > UINT_MAX);
1297f62fe8aSKai Vehmanen }
1307f62fe8aSKai Vehmanen 
cs_notify(u32 message,struct list_head * head)1317f62fe8aSKai Vehmanen static void cs_notify(u32 message, struct list_head *head)
1327f62fe8aSKai Vehmanen {
1337f62fe8aSKai Vehmanen 	struct char_queue *entry;
1347f62fe8aSKai Vehmanen 
1357f62fe8aSKai Vehmanen 	spin_lock(&cs_char_data.lock);
1367f62fe8aSKai Vehmanen 
1377f62fe8aSKai Vehmanen 	if (!cs_char_data.opened) {
1387f62fe8aSKai Vehmanen 		spin_unlock(&cs_char_data.lock);
1397f62fe8aSKai Vehmanen 		goto out;
1407f62fe8aSKai Vehmanen 	}
1417f62fe8aSKai Vehmanen 
1427f62fe8aSKai Vehmanen 	entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
1437f62fe8aSKai Vehmanen 	if (!entry) {
1447f62fe8aSKai Vehmanen 		dev_err(&cs_char_data.cl->device,
1457f62fe8aSKai Vehmanen 			"Can't allocate new entry for the queue.\n");
1467f62fe8aSKai Vehmanen 		spin_unlock(&cs_char_data.lock);
1477f62fe8aSKai Vehmanen 		goto out;
1487f62fe8aSKai Vehmanen 	}
1497f62fe8aSKai Vehmanen 
1507f62fe8aSKai Vehmanen 	entry->msg = message;
1517f62fe8aSKai Vehmanen 	list_add_tail(&entry->list, head);
1527f62fe8aSKai Vehmanen 
1537f62fe8aSKai Vehmanen 	spin_unlock(&cs_char_data.lock);
1547f62fe8aSKai Vehmanen 
1557f62fe8aSKai Vehmanen 	wake_up_interruptible(&cs_char_data.wait);
1567f62fe8aSKai Vehmanen 	kill_fasync(&cs_char_data.async_queue, SIGIO, POLL_IN);
1577f62fe8aSKai Vehmanen 
1587f62fe8aSKai Vehmanen out:
1597f62fe8aSKai Vehmanen 	return;
1607f62fe8aSKai Vehmanen }
1617f62fe8aSKai Vehmanen 
cs_pop_entry(struct list_head * head)1627f62fe8aSKai Vehmanen static u32 cs_pop_entry(struct list_head *head)
1637f62fe8aSKai Vehmanen {
1647f62fe8aSKai Vehmanen 	struct char_queue *entry;
1657f62fe8aSKai Vehmanen 	u32 data;
1667f62fe8aSKai Vehmanen 
1677f62fe8aSKai Vehmanen 	entry = list_entry(head->next, struct char_queue, list);
1687f62fe8aSKai Vehmanen 	data = entry->msg;
1697f62fe8aSKai Vehmanen 	list_del(&entry->list);
1707f62fe8aSKai Vehmanen 	kfree(entry);
1717f62fe8aSKai Vehmanen 
1727f62fe8aSKai Vehmanen 	return data;
1737f62fe8aSKai Vehmanen }
1747f62fe8aSKai Vehmanen 
cs_notify_control(u32 message)1757f62fe8aSKai Vehmanen static void cs_notify_control(u32 message)
1767f62fe8aSKai Vehmanen {
1777f62fe8aSKai Vehmanen 	cs_notify(message, &cs_char_data.chardev_queue);
1787f62fe8aSKai Vehmanen }
1797f62fe8aSKai Vehmanen 
cs_notify_data(u32 message,int maxlength)1807f62fe8aSKai Vehmanen static void cs_notify_data(u32 message, int maxlength)
1817f62fe8aSKai Vehmanen {
1827f62fe8aSKai Vehmanen 	cs_notify(message, &cs_char_data.dataind_queue);
1837f62fe8aSKai Vehmanen 
1847f62fe8aSKai Vehmanen 	spin_lock(&cs_char_data.lock);
1857f62fe8aSKai Vehmanen 	cs_char_data.dataind_pending++;
1867f62fe8aSKai Vehmanen 	while (cs_char_data.dataind_pending > maxlength &&
1877f62fe8aSKai Vehmanen 				!list_empty(&cs_char_data.dataind_queue)) {
1887f62fe8aSKai Vehmanen 		dev_dbg(&cs_char_data.cl->device, "data notification "
1897f62fe8aSKai Vehmanen 		"queue overrun (%u entries)\n", cs_char_data.dataind_pending);
1907f62fe8aSKai Vehmanen 
1917f62fe8aSKai Vehmanen 		cs_pop_entry(&cs_char_data.dataind_queue);
1927f62fe8aSKai Vehmanen 		cs_char_data.dataind_pending--;
1937f62fe8aSKai Vehmanen 	}
1947f62fe8aSKai Vehmanen 	spin_unlock(&cs_char_data.lock);
1957f62fe8aSKai Vehmanen }
1967f62fe8aSKai Vehmanen 
cs_set_cmd(struct hsi_msg * msg,u32 cmd)1977f62fe8aSKai Vehmanen static inline void cs_set_cmd(struct hsi_msg *msg, u32 cmd)
1987f62fe8aSKai Vehmanen {
1997f62fe8aSKai Vehmanen 	u32 *data = sg_virt(msg->sgt.sgl);
2007f62fe8aSKai Vehmanen 	*data = cmd;
2017f62fe8aSKai Vehmanen }
2027f62fe8aSKai Vehmanen 
cs_get_cmd(struct hsi_msg * msg)2037f62fe8aSKai Vehmanen static inline u32 cs_get_cmd(struct hsi_msg *msg)
2047f62fe8aSKai Vehmanen {
2057f62fe8aSKai Vehmanen 	u32 *data = sg_virt(msg->sgt.sgl);
2067f62fe8aSKai Vehmanen 	return *data;
2077f62fe8aSKai Vehmanen }
2087f62fe8aSKai Vehmanen 
cs_release_cmd(struct hsi_msg * msg)2097f62fe8aSKai Vehmanen static void cs_release_cmd(struct hsi_msg *msg)
2107f62fe8aSKai Vehmanen {
2117f62fe8aSKai Vehmanen 	struct cs_hsi_iface *hi = msg->context;
2127f62fe8aSKai Vehmanen 
2137f62fe8aSKai Vehmanen 	list_add_tail(&msg->link, &hi->cmdqueue);
2147f62fe8aSKai Vehmanen }
2157f62fe8aSKai Vehmanen 
cs_cmd_destructor(struct hsi_msg * msg)2167f62fe8aSKai Vehmanen static void cs_cmd_destructor(struct hsi_msg *msg)
2177f62fe8aSKai Vehmanen {
2187f62fe8aSKai Vehmanen 	struct cs_hsi_iface *hi = msg->context;
2197f62fe8aSKai Vehmanen 
2207f62fe8aSKai Vehmanen 	spin_lock(&hi->lock);
2217f62fe8aSKai Vehmanen 
2227f62fe8aSKai Vehmanen 	dev_dbg(&cs_char_data.cl->device, "control cmd destructor\n");
2237f62fe8aSKai Vehmanen 
2247f62fe8aSKai Vehmanen 	if (hi->iface_state != CS_STATE_CLOSED)
2257f62fe8aSKai Vehmanen 		dev_err(&hi->cl->device, "Cmd flushed while driver active\n");
2267f62fe8aSKai Vehmanen 
2277f62fe8aSKai Vehmanen 	if (msg->ttype == HSI_MSG_READ)
2287f62fe8aSKai Vehmanen 		hi->control_state &=
2297f62fe8aSKai Vehmanen 			~(SSI_CHANNEL_STATE_POLL | SSI_CHANNEL_STATE_READING);
2307f62fe8aSKai Vehmanen 	else if (msg->ttype == HSI_MSG_WRITE &&
2317f62fe8aSKai Vehmanen 			hi->control_state & SSI_CHANNEL_STATE_WRITING)
2327f62fe8aSKai Vehmanen 		hi->control_state &= ~SSI_CHANNEL_STATE_WRITING;
2337f62fe8aSKai Vehmanen 
2347f62fe8aSKai Vehmanen 	cs_release_cmd(msg);
2357f62fe8aSKai Vehmanen 
2367f62fe8aSKai Vehmanen 	spin_unlock(&hi->lock);
2377f62fe8aSKai Vehmanen }
2387f62fe8aSKai Vehmanen 
cs_claim_cmd(struct cs_hsi_iface * ssi)2397f62fe8aSKai Vehmanen static struct hsi_msg *cs_claim_cmd(struct cs_hsi_iface* ssi)
2407f62fe8aSKai Vehmanen {
2417f62fe8aSKai Vehmanen 	struct hsi_msg *msg;
2427f62fe8aSKai Vehmanen 
2437f62fe8aSKai Vehmanen 	BUG_ON(list_empty(&ssi->cmdqueue));
2447f62fe8aSKai Vehmanen 
2457f62fe8aSKai Vehmanen 	msg = list_first_entry(&ssi->cmdqueue, struct hsi_msg, link);
2467f62fe8aSKai Vehmanen 	list_del(&msg->link);
2477f62fe8aSKai Vehmanen 	msg->destructor = cs_cmd_destructor;
2487f62fe8aSKai Vehmanen 
2497f62fe8aSKai Vehmanen 	return msg;
2507f62fe8aSKai Vehmanen }
2517f62fe8aSKai Vehmanen 
cs_free_cmds(struct cs_hsi_iface * ssi)2527f62fe8aSKai Vehmanen static void cs_free_cmds(struct cs_hsi_iface *ssi)
2537f62fe8aSKai Vehmanen {
2547f62fe8aSKai Vehmanen 	struct hsi_msg *msg, *tmp;
2557f62fe8aSKai Vehmanen 
2567f62fe8aSKai Vehmanen 	list_for_each_entry_safe(msg, tmp, &ssi->cmdqueue, link) {
2577f62fe8aSKai Vehmanen 		list_del(&msg->link);
2587f62fe8aSKai Vehmanen 		msg->destructor = NULL;
2597f62fe8aSKai Vehmanen 		kfree(sg_virt(msg->sgt.sgl));
2607f62fe8aSKai Vehmanen 		hsi_free_msg(msg);
2617f62fe8aSKai Vehmanen 	}
2627f62fe8aSKai Vehmanen }
2637f62fe8aSKai Vehmanen 
cs_alloc_cmds(struct cs_hsi_iface * hi)2647f62fe8aSKai Vehmanen static int cs_alloc_cmds(struct cs_hsi_iface *hi)
2657f62fe8aSKai Vehmanen {
2667f62fe8aSKai Vehmanen 	struct hsi_msg *msg;
2677f62fe8aSKai Vehmanen 	u32 *buf;
2687f62fe8aSKai Vehmanen 	unsigned int i;
2697f62fe8aSKai Vehmanen 
2707f62fe8aSKai Vehmanen 	INIT_LIST_HEAD(&hi->cmdqueue);
2717f62fe8aSKai Vehmanen 
2727f62fe8aSKai Vehmanen 	for (i = 0; i < CS_MAX_CMDS; i++) {
2737f62fe8aSKai Vehmanen 		msg = hsi_alloc_msg(1, GFP_KERNEL);
2747f62fe8aSKai Vehmanen 		if (!msg)
2757f62fe8aSKai Vehmanen 			goto out;
2767f62fe8aSKai Vehmanen 		buf = kmalloc(sizeof(*buf), GFP_KERNEL);
2777f62fe8aSKai Vehmanen 		if (!buf) {
2787f62fe8aSKai Vehmanen 			hsi_free_msg(msg);
2797f62fe8aSKai Vehmanen 			goto out;
2807f62fe8aSKai Vehmanen 		}
2817f62fe8aSKai Vehmanen 		sg_init_one(msg->sgt.sgl, buf, sizeof(*buf));
2827f62fe8aSKai Vehmanen 		msg->channel = cs_char_data.channel_id_cmd;
2837f62fe8aSKai Vehmanen 		msg->context = hi;
2847f62fe8aSKai Vehmanen 		list_add_tail(&msg->link, &hi->cmdqueue);
2857f62fe8aSKai Vehmanen 	}
2867f62fe8aSKai Vehmanen 
2877f62fe8aSKai Vehmanen 	return 0;
2887f62fe8aSKai Vehmanen 
2897f62fe8aSKai Vehmanen out:
2907f62fe8aSKai Vehmanen 	cs_free_cmds(hi);
2917f62fe8aSKai Vehmanen 	return -ENOMEM;
2927f62fe8aSKai Vehmanen }
2937f62fe8aSKai Vehmanen 
cs_hsi_data_destructor(struct hsi_msg * msg)2947f62fe8aSKai Vehmanen static void cs_hsi_data_destructor(struct hsi_msg *msg)
2957f62fe8aSKai Vehmanen {
2967f62fe8aSKai Vehmanen 	struct cs_hsi_iface *hi = msg->context;
2977f62fe8aSKai Vehmanen 	const char *dir = (msg->ttype == HSI_MSG_READ) ? "TX" : "RX";
2987f62fe8aSKai Vehmanen 
2997f62fe8aSKai Vehmanen 	dev_dbg(&cs_char_data.cl->device, "Freeing data %s message\n", dir);
3007f62fe8aSKai Vehmanen 
3017f62fe8aSKai Vehmanen 	spin_lock(&hi->lock);
3027f62fe8aSKai Vehmanen 	if (hi->iface_state != CS_STATE_CLOSED)
3037f62fe8aSKai Vehmanen 		dev_err(&cs_char_data.cl->device,
3047f62fe8aSKai Vehmanen 				"Data %s flush while device active\n", dir);
3057f62fe8aSKai Vehmanen 	if (msg->ttype == HSI_MSG_READ)
3067f62fe8aSKai Vehmanen 		hi->data_state &=
3077f62fe8aSKai Vehmanen 			~(SSI_CHANNEL_STATE_POLL | SSI_CHANNEL_STATE_READING);
3087f62fe8aSKai Vehmanen 	else
3097f62fe8aSKai Vehmanen 		hi->data_state &= ~SSI_CHANNEL_STATE_WRITING;
3107f62fe8aSKai Vehmanen 
3117f62fe8aSKai Vehmanen 	msg->status = HSI_STATUS_COMPLETED;
3127f62fe8aSKai Vehmanen 	if (unlikely(waitqueue_active(&hi->datawait)))
3137f62fe8aSKai Vehmanen 		wake_up_interruptible(&hi->datawait);
3147f62fe8aSKai Vehmanen 
3157f62fe8aSKai Vehmanen 	spin_unlock(&hi->lock);
3167f62fe8aSKai Vehmanen }
3177f62fe8aSKai Vehmanen 
cs_hsi_alloc_data(struct cs_hsi_iface * hi)3187f62fe8aSKai Vehmanen static int cs_hsi_alloc_data(struct cs_hsi_iface *hi)
3197f62fe8aSKai Vehmanen {
3207f62fe8aSKai Vehmanen 	struct hsi_msg *txmsg, *rxmsg;
3217f62fe8aSKai Vehmanen 	int res = 0;
3227f62fe8aSKai Vehmanen 
3237f62fe8aSKai Vehmanen 	rxmsg = hsi_alloc_msg(1, GFP_KERNEL);
3247f62fe8aSKai Vehmanen 	if (!rxmsg) {
3257f62fe8aSKai Vehmanen 		res = -ENOMEM;
3267f62fe8aSKai Vehmanen 		goto out1;
3277f62fe8aSKai Vehmanen 	}
3287f62fe8aSKai Vehmanen 	rxmsg->channel = cs_char_data.channel_id_data;
3297f62fe8aSKai Vehmanen 	rxmsg->destructor = cs_hsi_data_destructor;
3307f62fe8aSKai Vehmanen 	rxmsg->context = hi;
3317f62fe8aSKai Vehmanen 
3327f62fe8aSKai Vehmanen 	txmsg = hsi_alloc_msg(1, GFP_KERNEL);
3337f62fe8aSKai Vehmanen 	if (!txmsg) {
3347f62fe8aSKai Vehmanen 		res = -ENOMEM;
3357f62fe8aSKai Vehmanen 		goto out2;
3367f62fe8aSKai Vehmanen 	}
3377f62fe8aSKai Vehmanen 	txmsg->channel = cs_char_data.channel_id_data;
3387f62fe8aSKai Vehmanen 	txmsg->destructor = cs_hsi_data_destructor;
3397f62fe8aSKai Vehmanen 	txmsg->context = hi;
3407f62fe8aSKai Vehmanen 
3417f62fe8aSKai Vehmanen 	hi->data_rx_msg = rxmsg;
3427f62fe8aSKai Vehmanen 	hi->data_tx_msg = txmsg;
3437f62fe8aSKai Vehmanen 
3447f62fe8aSKai Vehmanen 	return 0;
3457f62fe8aSKai Vehmanen 
3467f62fe8aSKai Vehmanen out2:
3477f62fe8aSKai Vehmanen 	hsi_free_msg(rxmsg);
3487f62fe8aSKai Vehmanen out1:
3497f62fe8aSKai Vehmanen 	return res;
3507f62fe8aSKai Vehmanen }
3517f62fe8aSKai Vehmanen 
cs_hsi_free_data_msg(struct hsi_msg * msg)3527f62fe8aSKai Vehmanen static void cs_hsi_free_data_msg(struct hsi_msg *msg)
3537f62fe8aSKai Vehmanen {
3547f62fe8aSKai Vehmanen 	WARN_ON(msg->status != HSI_STATUS_COMPLETED &&
3557f62fe8aSKai Vehmanen 					msg->status != HSI_STATUS_ERROR);
3567f62fe8aSKai Vehmanen 	hsi_free_msg(msg);
3577f62fe8aSKai Vehmanen }
3587f62fe8aSKai Vehmanen 
cs_hsi_free_data(struct cs_hsi_iface * hi)3597f62fe8aSKai Vehmanen static void cs_hsi_free_data(struct cs_hsi_iface *hi)
3607f62fe8aSKai Vehmanen {
3617f62fe8aSKai Vehmanen 	cs_hsi_free_data_msg(hi->data_rx_msg);
3627f62fe8aSKai Vehmanen 	cs_hsi_free_data_msg(hi->data_tx_msg);
3637f62fe8aSKai Vehmanen }
3647f62fe8aSKai Vehmanen 
__cs_hsi_error_pre(struct cs_hsi_iface * hi,struct hsi_msg * msg,const char * info,unsigned int * state)3657f62fe8aSKai Vehmanen static inline void __cs_hsi_error_pre(struct cs_hsi_iface *hi,
3667f62fe8aSKai Vehmanen 					struct hsi_msg *msg, const char *info,
3677f62fe8aSKai Vehmanen 					unsigned int *state)
3687f62fe8aSKai Vehmanen {
3697f62fe8aSKai Vehmanen 	spin_lock(&hi->lock);
3707f62fe8aSKai Vehmanen 	dev_err(&hi->cl->device, "HSI %s error, msg %d, state %u\n",
3717f62fe8aSKai Vehmanen 		info, msg->status, *state);
3727f62fe8aSKai Vehmanen }
3737f62fe8aSKai Vehmanen 
__cs_hsi_error_post(struct cs_hsi_iface * hi)3747f62fe8aSKai Vehmanen static inline void __cs_hsi_error_post(struct cs_hsi_iface *hi)
3757f62fe8aSKai Vehmanen {
3767f62fe8aSKai Vehmanen 	spin_unlock(&hi->lock);
3777f62fe8aSKai Vehmanen }
3787f62fe8aSKai Vehmanen 
__cs_hsi_error_read_bits(unsigned int * state)3797f62fe8aSKai Vehmanen static inline void __cs_hsi_error_read_bits(unsigned int *state)
3807f62fe8aSKai Vehmanen {
3817f62fe8aSKai Vehmanen 	*state |= SSI_CHANNEL_STATE_ERROR;
3827f62fe8aSKai Vehmanen 	*state &= ~(SSI_CHANNEL_STATE_READING | SSI_CHANNEL_STATE_POLL);
3837f62fe8aSKai Vehmanen }
3847f62fe8aSKai Vehmanen 
__cs_hsi_error_write_bits(unsigned int * state)3857f62fe8aSKai Vehmanen static inline void __cs_hsi_error_write_bits(unsigned int *state)
3867f62fe8aSKai Vehmanen {
3877f62fe8aSKai Vehmanen 	*state |= SSI_CHANNEL_STATE_ERROR;
3887f62fe8aSKai Vehmanen 	*state &= ~SSI_CHANNEL_STATE_WRITING;
3897f62fe8aSKai Vehmanen }
3907f62fe8aSKai Vehmanen 
cs_hsi_control_read_error(struct cs_hsi_iface * hi,struct hsi_msg * msg)3917f62fe8aSKai Vehmanen static void cs_hsi_control_read_error(struct cs_hsi_iface *hi,
3927f62fe8aSKai Vehmanen 							struct hsi_msg *msg)
3937f62fe8aSKai Vehmanen {
3947f62fe8aSKai Vehmanen 	__cs_hsi_error_pre(hi, msg, "control read", &hi->control_state);
3957f62fe8aSKai Vehmanen 	cs_release_cmd(msg);
3967f62fe8aSKai Vehmanen 	__cs_hsi_error_read_bits(&hi->control_state);
3977f62fe8aSKai Vehmanen 	__cs_hsi_error_post(hi);
3987f62fe8aSKai Vehmanen }
3997f62fe8aSKai Vehmanen 
cs_hsi_control_write_error(struct cs_hsi_iface * hi,struct hsi_msg * msg)4007f62fe8aSKai Vehmanen static void cs_hsi_control_write_error(struct cs_hsi_iface *hi,
4017f62fe8aSKai Vehmanen 							struct hsi_msg *msg)
4027f62fe8aSKai Vehmanen {
4037f62fe8aSKai Vehmanen 	__cs_hsi_error_pre(hi, msg, "control write", &hi->control_state);
4047f62fe8aSKai Vehmanen 	cs_release_cmd(msg);
4057f62fe8aSKai Vehmanen 	__cs_hsi_error_write_bits(&hi->control_state);
4067f62fe8aSKai Vehmanen 	__cs_hsi_error_post(hi);
4077f62fe8aSKai Vehmanen 
4087f62fe8aSKai Vehmanen }
4097f62fe8aSKai Vehmanen 
cs_hsi_data_read_error(struct cs_hsi_iface * hi,struct hsi_msg * msg)4107f62fe8aSKai Vehmanen static void cs_hsi_data_read_error(struct cs_hsi_iface *hi, struct hsi_msg *msg)
4117f62fe8aSKai Vehmanen {
4127f62fe8aSKai Vehmanen 	__cs_hsi_error_pre(hi, msg, "data read", &hi->data_state);
4137f62fe8aSKai Vehmanen 	__cs_hsi_error_read_bits(&hi->data_state);
4147f62fe8aSKai Vehmanen 	__cs_hsi_error_post(hi);
4157f62fe8aSKai Vehmanen }
4167f62fe8aSKai Vehmanen 
cs_hsi_data_write_error(struct cs_hsi_iface * hi,struct hsi_msg * msg)4177f62fe8aSKai Vehmanen static void cs_hsi_data_write_error(struct cs_hsi_iface *hi,
4187f62fe8aSKai Vehmanen 							struct hsi_msg *msg)
4197f62fe8aSKai Vehmanen {
4207f62fe8aSKai Vehmanen 	__cs_hsi_error_pre(hi, msg, "data write", &hi->data_state);
4217f62fe8aSKai Vehmanen 	__cs_hsi_error_write_bits(&hi->data_state);
4227f62fe8aSKai Vehmanen 	__cs_hsi_error_post(hi);
4237f62fe8aSKai Vehmanen }
4247f62fe8aSKai Vehmanen 
cs_hsi_read_on_control_complete(struct hsi_msg * msg)4257f62fe8aSKai Vehmanen static void cs_hsi_read_on_control_complete(struct hsi_msg *msg)
4267f62fe8aSKai Vehmanen {
4277f62fe8aSKai Vehmanen 	u32 cmd = cs_get_cmd(msg);
4287f62fe8aSKai Vehmanen 	struct cs_hsi_iface *hi = msg->context;
4297f62fe8aSKai Vehmanen 
4307f62fe8aSKai Vehmanen 	spin_lock(&hi->lock);
4317f62fe8aSKai Vehmanen 	hi->control_state &= ~SSI_CHANNEL_STATE_READING;
4327f62fe8aSKai Vehmanen 	if (msg->status == HSI_STATUS_ERROR) {
4337f62fe8aSKai Vehmanen 		dev_err(&hi->cl->device, "Control RX error detected\n");
4347f62fe8aSKai Vehmanen 		spin_unlock(&hi->lock);
4353c13ab1dSIago Abal 		cs_hsi_control_read_error(hi, msg);
4367f62fe8aSKai Vehmanen 		goto out;
4377f62fe8aSKai Vehmanen 	}
4387f62fe8aSKai Vehmanen 	dev_dbg(&hi->cl->device, "Read on control: %08X\n", cmd);
4397f62fe8aSKai Vehmanen 	cs_release_cmd(msg);
4407f62fe8aSKai Vehmanen 	if (hi->flags & CS_FEAT_TSTAMP_RX_CTRL) {
441b6dc80dbSArnd Bergmann 		struct timespec64 tspec;
4425023a5caSSebastian Reichel 		struct cs_timestamp *tstamp =
4437f62fe8aSKai Vehmanen 			&hi->mmap_cfg->tstamp_rx_ctrl;
4445023a5caSSebastian Reichel 
445b6dc80dbSArnd Bergmann 		ktime_get_ts64(&tspec);
4465023a5caSSebastian Reichel 
4475023a5caSSebastian Reichel 		tstamp->tv_sec = (__u32) tspec.tv_sec;
4485023a5caSSebastian Reichel 		tstamp->tv_nsec = (__u32) tspec.tv_nsec;
4497f62fe8aSKai Vehmanen 	}
4507f62fe8aSKai Vehmanen 	spin_unlock(&hi->lock);
4517f62fe8aSKai Vehmanen 
4527f62fe8aSKai Vehmanen 	cs_notify_control(cmd);
4537f62fe8aSKai Vehmanen 
4547f62fe8aSKai Vehmanen out:
4557f62fe8aSKai Vehmanen 	cs_hsi_read_on_control(hi);
4567f62fe8aSKai Vehmanen }
4577f62fe8aSKai Vehmanen 
cs_hsi_peek_on_control_complete(struct hsi_msg * msg)4587f62fe8aSKai Vehmanen static void cs_hsi_peek_on_control_complete(struct hsi_msg *msg)
4597f62fe8aSKai Vehmanen {
4607f62fe8aSKai Vehmanen 	struct cs_hsi_iface *hi = msg->context;
4617f62fe8aSKai Vehmanen 	int ret;
4627f62fe8aSKai Vehmanen 
4637f62fe8aSKai Vehmanen 	if (msg->status == HSI_STATUS_ERROR) {
4647f62fe8aSKai Vehmanen 		dev_err(&hi->cl->device, "Control peek RX error detected\n");
4657f62fe8aSKai Vehmanen 		cs_hsi_control_read_error(hi, msg);
4667f62fe8aSKai Vehmanen 		return;
4677f62fe8aSKai Vehmanen 	}
4687f62fe8aSKai Vehmanen 
4697f62fe8aSKai Vehmanen 	WARN_ON(!(hi->control_state & SSI_CHANNEL_STATE_READING));
4707f62fe8aSKai Vehmanen 
4717f62fe8aSKai Vehmanen 	dev_dbg(&hi->cl->device, "Peek on control complete, reading\n");
4727f62fe8aSKai Vehmanen 	msg->sgt.nents = 1;
4737f62fe8aSKai Vehmanen 	msg->complete = cs_hsi_read_on_control_complete;
4747f62fe8aSKai Vehmanen 	ret = hsi_async_read(hi->cl, msg);
4757f62fe8aSKai Vehmanen 	if (ret)
4767f62fe8aSKai Vehmanen 		cs_hsi_control_read_error(hi, msg);
4777f62fe8aSKai Vehmanen }
4787f62fe8aSKai Vehmanen 
cs_hsi_read_on_control(struct cs_hsi_iface * hi)4797f62fe8aSKai Vehmanen static void cs_hsi_read_on_control(struct cs_hsi_iface *hi)
4807f62fe8aSKai Vehmanen {
4817f62fe8aSKai Vehmanen 	struct hsi_msg *msg;
4827f62fe8aSKai Vehmanen 	int ret;
4837f62fe8aSKai Vehmanen 
4847f62fe8aSKai Vehmanen 	spin_lock(&hi->lock);
4857f62fe8aSKai Vehmanen 	if (hi->control_state & SSI_CHANNEL_STATE_READING) {
4867f62fe8aSKai Vehmanen 		dev_err(&hi->cl->device, "Control read already pending (%d)\n",
4877f62fe8aSKai Vehmanen 			hi->control_state);
4887f62fe8aSKai Vehmanen 		spin_unlock(&hi->lock);
4897f62fe8aSKai Vehmanen 		return;
4907f62fe8aSKai Vehmanen 	}
4917f62fe8aSKai Vehmanen 	if (hi->control_state & SSI_CHANNEL_STATE_ERROR) {
4927f62fe8aSKai Vehmanen 		dev_err(&hi->cl->device, "Control read error (%d)\n",
4937f62fe8aSKai Vehmanen 			hi->control_state);
4947f62fe8aSKai Vehmanen 		spin_unlock(&hi->lock);
4957f62fe8aSKai Vehmanen 		return;
4967f62fe8aSKai Vehmanen 	}
4977f62fe8aSKai Vehmanen 	hi->control_state |= SSI_CHANNEL_STATE_READING;
4987f62fe8aSKai Vehmanen 	dev_dbg(&hi->cl->device, "Issuing RX on control\n");
4997f62fe8aSKai Vehmanen 	msg = cs_claim_cmd(hi);
5007f62fe8aSKai Vehmanen 	spin_unlock(&hi->lock);
5017f62fe8aSKai Vehmanen 
5027f62fe8aSKai Vehmanen 	msg->sgt.nents = 0;
5037f62fe8aSKai Vehmanen 	msg->complete = cs_hsi_peek_on_control_complete;
5047f62fe8aSKai Vehmanen 	ret = hsi_async_read(hi->cl, msg);
5057f62fe8aSKai Vehmanen 	if (ret)
5067f62fe8aSKai Vehmanen 		cs_hsi_control_read_error(hi, msg);
5077f62fe8aSKai Vehmanen }
5087f62fe8aSKai Vehmanen 
cs_hsi_write_on_control_complete(struct hsi_msg * msg)5097f62fe8aSKai Vehmanen static void cs_hsi_write_on_control_complete(struct hsi_msg *msg)
5107f62fe8aSKai Vehmanen {
5117f62fe8aSKai Vehmanen 	struct cs_hsi_iface *hi = msg->context;
5127f62fe8aSKai Vehmanen 	if (msg->status == HSI_STATUS_COMPLETED) {
5137f62fe8aSKai Vehmanen 		spin_lock(&hi->lock);
5147f62fe8aSKai Vehmanen 		hi->control_state &= ~SSI_CHANNEL_STATE_WRITING;
5157f62fe8aSKai Vehmanen 		cs_release_cmd(msg);
5167f62fe8aSKai Vehmanen 		spin_unlock(&hi->lock);
5177f62fe8aSKai Vehmanen 	} else if (msg->status == HSI_STATUS_ERROR) {
5187f62fe8aSKai Vehmanen 		cs_hsi_control_write_error(hi, msg);
5197f62fe8aSKai Vehmanen 	} else {
5207f62fe8aSKai Vehmanen 		dev_err(&hi->cl->device,
5217f62fe8aSKai Vehmanen 			"unexpected status in control write callback %d\n",
5227f62fe8aSKai Vehmanen 			msg->status);
5237f62fe8aSKai Vehmanen 	}
5247f62fe8aSKai Vehmanen }
5257f62fe8aSKai Vehmanen 
cs_hsi_write_on_control(struct cs_hsi_iface * hi,u32 message)5267f62fe8aSKai Vehmanen static int cs_hsi_write_on_control(struct cs_hsi_iface *hi, u32 message)
5277f62fe8aSKai Vehmanen {
5287f62fe8aSKai Vehmanen 	struct hsi_msg *msg;
5297f62fe8aSKai Vehmanen 	int ret;
5307f62fe8aSKai Vehmanen 
5317f62fe8aSKai Vehmanen 	spin_lock(&hi->lock);
5327f62fe8aSKai Vehmanen 	if (hi->control_state & SSI_CHANNEL_STATE_ERROR) {
5337f62fe8aSKai Vehmanen 		spin_unlock(&hi->lock);
5347f62fe8aSKai Vehmanen 		return -EIO;
5357f62fe8aSKai Vehmanen 	}
5367f62fe8aSKai Vehmanen 	if (hi->control_state & SSI_CHANNEL_STATE_WRITING) {
5377f62fe8aSKai Vehmanen 		dev_err(&hi->cl->device,
5387f62fe8aSKai Vehmanen 			"Write still pending on control channel.\n");
5397f62fe8aSKai Vehmanen 		spin_unlock(&hi->lock);
5407f62fe8aSKai Vehmanen 		return -EBUSY;
5417f62fe8aSKai Vehmanen 	}
5427f62fe8aSKai Vehmanen 	hi->control_state |= SSI_CHANNEL_STATE_WRITING;
5437f62fe8aSKai Vehmanen 	msg = cs_claim_cmd(hi);
5447f62fe8aSKai Vehmanen 	spin_unlock(&hi->lock);
5457f62fe8aSKai Vehmanen 
5467f62fe8aSKai Vehmanen 	cs_set_cmd(msg, message);
5477f62fe8aSKai Vehmanen 	msg->sgt.nents = 1;
5487f62fe8aSKai Vehmanen 	msg->complete = cs_hsi_write_on_control_complete;
5497f62fe8aSKai Vehmanen 	dev_dbg(&hi->cl->device,
5507f62fe8aSKai Vehmanen 		"Sending control message %08X\n", message);
5517f62fe8aSKai Vehmanen 	ret = hsi_async_write(hi->cl, msg);
5527f62fe8aSKai Vehmanen 	if (ret) {
5537f62fe8aSKai Vehmanen 		dev_err(&hi->cl->device,
5547f62fe8aSKai Vehmanen 			"async_write failed with %d\n", ret);
5557f62fe8aSKai Vehmanen 		cs_hsi_control_write_error(hi, msg);
5567f62fe8aSKai Vehmanen 	}
5577f62fe8aSKai Vehmanen 
5587f62fe8aSKai Vehmanen 	/*
5597f62fe8aSKai Vehmanen 	 * Make sure control read is always pending when issuing
5607f62fe8aSKai Vehmanen 	 * new control writes. This is needed as the controller
5617f62fe8aSKai Vehmanen 	 * may flush our messages if e.g. the peer device reboots
5627f62fe8aSKai Vehmanen 	 * unexpectedly (and we cannot directly resubmit a new read from
5637f62fe8aSKai Vehmanen 	 * the message destructor; see cs_cmd_destructor()).
5647f62fe8aSKai Vehmanen 	 */
5657f62fe8aSKai Vehmanen 	if (!(hi->control_state & SSI_CHANNEL_STATE_READING)) {
5667f62fe8aSKai Vehmanen 		dev_err(&hi->cl->device, "Restarting control reads\n");
5677f62fe8aSKai Vehmanen 		cs_hsi_read_on_control(hi);
5687f62fe8aSKai Vehmanen 	}
5697f62fe8aSKai Vehmanen 
5707f62fe8aSKai Vehmanen 	return 0;
5717f62fe8aSKai Vehmanen }
5727f62fe8aSKai Vehmanen 
cs_hsi_read_on_data_complete(struct hsi_msg * msg)5737f62fe8aSKai Vehmanen static void cs_hsi_read_on_data_complete(struct hsi_msg *msg)
5747f62fe8aSKai Vehmanen {
5757f62fe8aSKai Vehmanen 	struct cs_hsi_iface *hi = msg->context;
5767f62fe8aSKai Vehmanen 	u32 payload;
5777f62fe8aSKai Vehmanen 
5787f62fe8aSKai Vehmanen 	if (unlikely(msg->status == HSI_STATUS_ERROR)) {
5797f62fe8aSKai Vehmanen 		cs_hsi_data_read_error(hi, msg);
5807f62fe8aSKai Vehmanen 		return;
5817f62fe8aSKai Vehmanen 	}
5827f62fe8aSKai Vehmanen 
5837f62fe8aSKai Vehmanen 	spin_lock(&hi->lock);
5847f62fe8aSKai Vehmanen 	WARN_ON(!(hi->data_state & SSI_CHANNEL_STATE_READING));
5857f62fe8aSKai Vehmanen 	hi->data_state &= ~SSI_CHANNEL_STATE_READING;
5867f62fe8aSKai Vehmanen 	payload = CS_RX_DATA_RECEIVED;
5877f62fe8aSKai Vehmanen 	payload |= hi->rx_slot;
5887f62fe8aSKai Vehmanen 	hi->rx_slot++;
5897f62fe8aSKai Vehmanen 	hi->rx_slot %= hi->rx_ptr_boundary;
5907f62fe8aSKai Vehmanen 	/* expose current rx ptr in mmap area */
5917f62fe8aSKai Vehmanen 	hi->mmap_cfg->rx_ptr = hi->rx_slot;
5927f62fe8aSKai Vehmanen 	if (unlikely(waitqueue_active(&hi->datawait)))
5937f62fe8aSKai Vehmanen 		wake_up_interruptible(&hi->datawait);
5947f62fe8aSKai Vehmanen 	spin_unlock(&hi->lock);
5957f62fe8aSKai Vehmanen 
5967f62fe8aSKai Vehmanen 	cs_notify_data(payload, hi->rx_bufs);
5977f62fe8aSKai Vehmanen 	cs_hsi_read_on_data(hi);
5987f62fe8aSKai Vehmanen }
5997f62fe8aSKai Vehmanen 
cs_hsi_peek_on_data_complete(struct hsi_msg * msg)6007f62fe8aSKai Vehmanen static void cs_hsi_peek_on_data_complete(struct hsi_msg *msg)
6017f62fe8aSKai Vehmanen {
6027f62fe8aSKai Vehmanen 	struct cs_hsi_iface *hi = msg->context;
6037f62fe8aSKai Vehmanen 	u32 *address;
6047f62fe8aSKai Vehmanen 	int ret;
6057f62fe8aSKai Vehmanen 
6067f62fe8aSKai Vehmanen 	if (unlikely(msg->status == HSI_STATUS_ERROR)) {
6077f62fe8aSKai Vehmanen 		cs_hsi_data_read_error(hi, msg);
6087f62fe8aSKai Vehmanen 		return;
6097f62fe8aSKai Vehmanen 	}
6107f62fe8aSKai Vehmanen 	if (unlikely(hi->iface_state != CS_STATE_CONFIGURED)) {
6117f62fe8aSKai Vehmanen 		dev_err(&hi->cl->device, "Data received in invalid state\n");
6127f62fe8aSKai Vehmanen 		cs_hsi_data_read_error(hi, msg);
6137f62fe8aSKai Vehmanen 		return;
6147f62fe8aSKai Vehmanen 	}
6157f62fe8aSKai Vehmanen 
6167f62fe8aSKai Vehmanen 	spin_lock(&hi->lock);
6177f62fe8aSKai Vehmanen 	WARN_ON(!(hi->data_state & SSI_CHANNEL_STATE_POLL));
6187f62fe8aSKai Vehmanen 	hi->data_state &= ~SSI_CHANNEL_STATE_POLL;
6197f62fe8aSKai Vehmanen 	hi->data_state |= SSI_CHANNEL_STATE_READING;
6207f62fe8aSKai Vehmanen 	spin_unlock(&hi->lock);
6217f62fe8aSKai Vehmanen 
6227f62fe8aSKai Vehmanen 	address = (u32 *)(hi->mmap_base +
6237f62fe8aSKai Vehmanen 				hi->rx_offsets[hi->rx_slot % hi->rx_bufs]);
6247f62fe8aSKai Vehmanen 	sg_init_one(msg->sgt.sgl, address, hi->buf_size);
6257f62fe8aSKai Vehmanen 	msg->sgt.nents = 1;
6267f62fe8aSKai Vehmanen 	msg->complete = cs_hsi_read_on_data_complete;
6277f62fe8aSKai Vehmanen 	ret = hsi_async_read(hi->cl, msg);
6287f62fe8aSKai Vehmanen 	if (ret)
6297f62fe8aSKai Vehmanen 		cs_hsi_data_read_error(hi, msg);
6307f62fe8aSKai Vehmanen }
6317f62fe8aSKai Vehmanen 
6327f62fe8aSKai Vehmanen /*
6337f62fe8aSKai Vehmanen  * Read/write transaction is ongoing. Returns false if in
6347f62fe8aSKai Vehmanen  * SSI_CHANNEL_STATE_POLL state.
6357f62fe8aSKai Vehmanen  */
cs_state_xfer_active(unsigned int state)6367f62fe8aSKai Vehmanen static inline int cs_state_xfer_active(unsigned int state)
6377f62fe8aSKai Vehmanen {
6387f62fe8aSKai Vehmanen 	return (state & SSI_CHANNEL_STATE_WRITING) ||
6397f62fe8aSKai Vehmanen 		(state & SSI_CHANNEL_STATE_READING);
6407f62fe8aSKai Vehmanen }
6417f62fe8aSKai Vehmanen 
6427f62fe8aSKai Vehmanen /*
6437f62fe8aSKai Vehmanen  * No pending read/writes
6447f62fe8aSKai Vehmanen  */
cs_state_idle(unsigned int state)6457f62fe8aSKai Vehmanen static inline int cs_state_idle(unsigned int state)
6467f62fe8aSKai Vehmanen {
6477f62fe8aSKai Vehmanen 	return !(state & ~SSI_CHANNEL_STATE_ERROR);
6487f62fe8aSKai Vehmanen }
6497f62fe8aSKai Vehmanen 
cs_hsi_read_on_data(struct cs_hsi_iface * hi)6507f62fe8aSKai Vehmanen static void cs_hsi_read_on_data(struct cs_hsi_iface *hi)
6517f62fe8aSKai Vehmanen {
6527f62fe8aSKai Vehmanen 	struct hsi_msg *rxmsg;
6537f62fe8aSKai Vehmanen 	int ret;
6547f62fe8aSKai Vehmanen 
6557f62fe8aSKai Vehmanen 	spin_lock(&hi->lock);
6567f62fe8aSKai Vehmanen 	if (hi->data_state &
6577f62fe8aSKai Vehmanen 		(SSI_CHANNEL_STATE_READING | SSI_CHANNEL_STATE_POLL)) {
6587f62fe8aSKai Vehmanen 		dev_dbg(&hi->cl->device, "Data read already pending (%u)\n",
6597f62fe8aSKai Vehmanen 			hi->data_state);
6607f62fe8aSKai Vehmanen 		spin_unlock(&hi->lock);
6617f62fe8aSKai Vehmanen 		return;
6627f62fe8aSKai Vehmanen 	}
6637f62fe8aSKai Vehmanen 	hi->data_state |= SSI_CHANNEL_STATE_POLL;
6647f62fe8aSKai Vehmanen 	spin_unlock(&hi->lock);
6657f62fe8aSKai Vehmanen 
6667f62fe8aSKai Vehmanen 	rxmsg = hi->data_rx_msg;
6677f62fe8aSKai Vehmanen 	sg_init_one(rxmsg->sgt.sgl, (void *)hi->mmap_base, 0);
6687f62fe8aSKai Vehmanen 	rxmsg->sgt.nents = 0;
6697f62fe8aSKai Vehmanen 	rxmsg->complete = cs_hsi_peek_on_data_complete;
6707f62fe8aSKai Vehmanen 
6717f62fe8aSKai Vehmanen 	ret = hsi_async_read(hi->cl, rxmsg);
6727f62fe8aSKai Vehmanen 	if (ret)
6737f62fe8aSKai Vehmanen 		cs_hsi_data_read_error(hi, rxmsg);
6747f62fe8aSKai Vehmanen }
6757f62fe8aSKai Vehmanen 
cs_hsi_write_on_data_complete(struct hsi_msg * msg)6767f62fe8aSKai Vehmanen static void cs_hsi_write_on_data_complete(struct hsi_msg *msg)
6777f62fe8aSKai Vehmanen {
6787f62fe8aSKai Vehmanen 	struct cs_hsi_iface *hi = msg->context;
6797f62fe8aSKai Vehmanen 
6807f62fe8aSKai Vehmanen 	if (msg->status == HSI_STATUS_COMPLETED) {
6817f62fe8aSKai Vehmanen 		spin_lock(&hi->lock);
6827f62fe8aSKai Vehmanen 		hi->data_state &= ~SSI_CHANNEL_STATE_WRITING;
6837f62fe8aSKai Vehmanen 		if (unlikely(waitqueue_active(&hi->datawait)))
6847f62fe8aSKai Vehmanen 			wake_up_interruptible(&hi->datawait);
6857f62fe8aSKai Vehmanen 		spin_unlock(&hi->lock);
6867f62fe8aSKai Vehmanen 	} else {
6877f62fe8aSKai Vehmanen 		cs_hsi_data_write_error(hi, msg);
6887f62fe8aSKai Vehmanen 	}
6897f62fe8aSKai Vehmanen }
6907f62fe8aSKai Vehmanen 
cs_hsi_write_on_data(struct cs_hsi_iface * hi,unsigned int slot)6917f62fe8aSKai Vehmanen static int cs_hsi_write_on_data(struct cs_hsi_iface *hi, unsigned int slot)
6927f62fe8aSKai Vehmanen {
6937f62fe8aSKai Vehmanen 	u32 *address;
6947f62fe8aSKai Vehmanen 	struct hsi_msg *txmsg;
6957f62fe8aSKai Vehmanen 	int ret;
6967f62fe8aSKai Vehmanen 
6977f62fe8aSKai Vehmanen 	spin_lock(&hi->lock);
6987f62fe8aSKai Vehmanen 	if (hi->iface_state != CS_STATE_CONFIGURED) {
6997f62fe8aSKai Vehmanen 		dev_err(&hi->cl->device, "Not configured, aborting\n");
7007f62fe8aSKai Vehmanen 		ret = -EINVAL;
7017f62fe8aSKai Vehmanen 		goto error;
7027f62fe8aSKai Vehmanen 	}
7037f62fe8aSKai Vehmanen 	if (hi->data_state & SSI_CHANNEL_STATE_ERROR) {
7047f62fe8aSKai Vehmanen 		dev_err(&hi->cl->device, "HSI error, aborting\n");
7057f62fe8aSKai Vehmanen 		ret = -EIO;
7067f62fe8aSKai Vehmanen 		goto error;
7077f62fe8aSKai Vehmanen 	}
7087f62fe8aSKai Vehmanen 	if (hi->data_state & SSI_CHANNEL_STATE_WRITING) {
7097f62fe8aSKai Vehmanen 		dev_err(&hi->cl->device, "Write pending on data channel.\n");
7107f62fe8aSKai Vehmanen 		ret = -EBUSY;
7117f62fe8aSKai Vehmanen 		goto error;
7127f62fe8aSKai Vehmanen 	}
7137f62fe8aSKai Vehmanen 	hi->data_state |= SSI_CHANNEL_STATE_WRITING;
7147f62fe8aSKai Vehmanen 	spin_unlock(&hi->lock);
7157f62fe8aSKai Vehmanen 
7167f62fe8aSKai Vehmanen 	hi->tx_slot = slot;
7177f62fe8aSKai Vehmanen 	address = (u32 *)(hi->mmap_base + hi->tx_offsets[hi->tx_slot]);
7187f62fe8aSKai Vehmanen 	txmsg = hi->data_tx_msg;
7197f62fe8aSKai Vehmanen 	sg_init_one(txmsg->sgt.sgl, address, hi->buf_size);
7207f62fe8aSKai Vehmanen 	txmsg->complete = cs_hsi_write_on_data_complete;
7217f62fe8aSKai Vehmanen 	ret = hsi_async_write(hi->cl, txmsg);
7227f62fe8aSKai Vehmanen 	if (ret)
7237f62fe8aSKai Vehmanen 		cs_hsi_data_write_error(hi, txmsg);
7247f62fe8aSKai Vehmanen 
7257f62fe8aSKai Vehmanen 	return ret;
7267f62fe8aSKai Vehmanen 
7277f62fe8aSKai Vehmanen error:
7287f62fe8aSKai Vehmanen 	spin_unlock(&hi->lock);
7297f62fe8aSKai Vehmanen 	if (ret == -EIO)
7307f62fe8aSKai Vehmanen 		cs_hsi_data_write_error(hi, hi->data_tx_msg);
7317f62fe8aSKai Vehmanen 
7327f62fe8aSKai Vehmanen 	return ret;
7337f62fe8aSKai Vehmanen }
7347f62fe8aSKai Vehmanen 
cs_hsi_get_state(struct cs_hsi_iface * hi)7357f62fe8aSKai Vehmanen static unsigned int cs_hsi_get_state(struct cs_hsi_iface *hi)
7367f62fe8aSKai Vehmanen {
7377f62fe8aSKai Vehmanen 	return hi->iface_state;
7387f62fe8aSKai Vehmanen }
7397f62fe8aSKai Vehmanen 
cs_hsi_command(struct cs_hsi_iface * hi,u32 cmd)7407f62fe8aSKai Vehmanen static int cs_hsi_command(struct cs_hsi_iface *hi, u32 cmd)
7417f62fe8aSKai Vehmanen {
7427f62fe8aSKai Vehmanen 	int ret = 0;
7437f62fe8aSKai Vehmanen 
7447f62fe8aSKai Vehmanen 	local_bh_disable();
7457f62fe8aSKai Vehmanen 	switch (cmd & TARGET_MASK) {
7467f62fe8aSKai Vehmanen 	case TARGET_REMOTE:
7477f62fe8aSKai Vehmanen 		ret = cs_hsi_write_on_control(hi, cmd);
7487f62fe8aSKai Vehmanen 		break;
7497f62fe8aSKai Vehmanen 	case TARGET_LOCAL:
7507f62fe8aSKai Vehmanen 		if ((cmd & CS_CMD_MASK) == CS_TX_DATA_READY)
7517f62fe8aSKai Vehmanen 			ret = cs_hsi_write_on_data(hi, cmd & CS_PARAM_MASK);
7527f62fe8aSKai Vehmanen 		else
7537f62fe8aSKai Vehmanen 			ret = -EINVAL;
7547f62fe8aSKai Vehmanen 		break;
7557f62fe8aSKai Vehmanen 	default:
7567f62fe8aSKai Vehmanen 		ret = -EINVAL;
7577f62fe8aSKai Vehmanen 		break;
7587f62fe8aSKai Vehmanen 	}
7597f62fe8aSKai Vehmanen 	local_bh_enable();
7607f62fe8aSKai Vehmanen 
7617f62fe8aSKai Vehmanen 	return ret;
7627f62fe8aSKai Vehmanen }
7637f62fe8aSKai Vehmanen 
cs_hsi_set_wakeline(struct cs_hsi_iface * hi,bool new_state)7647f62fe8aSKai Vehmanen static void cs_hsi_set_wakeline(struct cs_hsi_iface *hi, bool new_state)
7657f62fe8aSKai Vehmanen {
7667f62fe8aSKai Vehmanen 	int change = 0;
7677f62fe8aSKai Vehmanen 
7687f62fe8aSKai Vehmanen 	spin_lock_bh(&hi->lock);
7697f62fe8aSKai Vehmanen 	if (hi->wakeline_state != new_state) {
7707f62fe8aSKai Vehmanen 		hi->wakeline_state = new_state;
7717f62fe8aSKai Vehmanen 		change = 1;
7727f62fe8aSKai Vehmanen 		dev_dbg(&hi->cl->device, "setting wake line to %d (%p)\n",
7737f62fe8aSKai Vehmanen 			new_state, hi->cl);
7747f62fe8aSKai Vehmanen 	}
7757f62fe8aSKai Vehmanen 	spin_unlock_bh(&hi->lock);
7767f62fe8aSKai Vehmanen 
7777f62fe8aSKai Vehmanen 	if (change) {
7787f62fe8aSKai Vehmanen 		if (new_state)
7797f62fe8aSKai Vehmanen 			ssip_slave_start_tx(hi->master);
7807f62fe8aSKai Vehmanen 		else
7817f62fe8aSKai Vehmanen 			ssip_slave_stop_tx(hi->master);
7827f62fe8aSKai Vehmanen 	}
7837f62fe8aSKai Vehmanen 
7847f62fe8aSKai Vehmanen 	dev_dbg(&hi->cl->device, "wake line set to %d (%p)\n",
7857f62fe8aSKai Vehmanen 		new_state, hi->cl);
7867f62fe8aSKai Vehmanen }
7877f62fe8aSKai Vehmanen 
set_buffer_sizes(struct cs_hsi_iface * hi,int rx_bufs,int tx_bufs)7887f62fe8aSKai Vehmanen static void set_buffer_sizes(struct cs_hsi_iface *hi, int rx_bufs, int tx_bufs)
7897f62fe8aSKai Vehmanen {
7907f62fe8aSKai Vehmanen 	hi->rx_bufs = rx_bufs;
7917f62fe8aSKai Vehmanen 	hi->tx_bufs = tx_bufs;
7927f62fe8aSKai Vehmanen 	hi->mmap_cfg->rx_bufs = rx_bufs;
7937f62fe8aSKai Vehmanen 	hi->mmap_cfg->tx_bufs = tx_bufs;
7947f62fe8aSKai Vehmanen 
7957f62fe8aSKai Vehmanen 	if (hi->flags & CS_FEAT_ROLLING_RX_COUNTER) {
7967f62fe8aSKai Vehmanen 		/*
7977f62fe8aSKai Vehmanen 		 * For more robust overrun detection, let the rx
7987f62fe8aSKai Vehmanen 		 * pointer run in range 0..'boundary-1'. Boundary
7997f62fe8aSKai Vehmanen 		 * is a multiple of rx_bufs, and limited in max size
8007f62fe8aSKai Vehmanen 		 * by RX_PTR_MAX_SHIFT to allow for fast ptr-diff
8017f62fe8aSKai Vehmanen 		 * calculation.
8027f62fe8aSKai Vehmanen 		 */
8037f62fe8aSKai Vehmanen 		hi->rx_ptr_boundary = (rx_bufs << RX_PTR_BOUNDARY_SHIFT);
8047f62fe8aSKai Vehmanen 		hi->mmap_cfg->rx_ptr_boundary = hi->rx_ptr_boundary;
8057f62fe8aSKai Vehmanen 	} else {
8067f62fe8aSKai Vehmanen 		hi->rx_ptr_boundary = hi->rx_bufs;
8077f62fe8aSKai Vehmanen 	}
8087f62fe8aSKai Vehmanen }
8097f62fe8aSKai Vehmanen 
check_buf_params(struct cs_hsi_iface * hi,const struct cs_buffer_config * buf_cfg)8107f62fe8aSKai Vehmanen static int check_buf_params(struct cs_hsi_iface *hi,
8117f62fe8aSKai Vehmanen 					const struct cs_buffer_config *buf_cfg)
8127f62fe8aSKai Vehmanen {
8137f62fe8aSKai Vehmanen 	size_t buf_size_aligned = L1_CACHE_ALIGN(buf_cfg->buf_size) *
8147f62fe8aSKai Vehmanen 					(buf_cfg->rx_bufs + buf_cfg->tx_bufs);
8157f62fe8aSKai Vehmanen 	size_t ctrl_size_aligned = L1_CACHE_ALIGN(sizeof(*hi->mmap_cfg));
8167f62fe8aSKai Vehmanen 	int r = 0;
8177f62fe8aSKai Vehmanen 
8187f62fe8aSKai Vehmanen 	if (buf_cfg->rx_bufs > CS_MAX_BUFFERS ||
8197f62fe8aSKai Vehmanen 					buf_cfg->tx_bufs > CS_MAX_BUFFERS) {
8207f62fe8aSKai Vehmanen 		r = -EINVAL;
8217f62fe8aSKai Vehmanen 	} else if ((buf_size_aligned + ctrl_size_aligned) >= hi->mmap_size) {
8227f62fe8aSKai Vehmanen 		dev_err(&hi->cl->device, "No space for the requested buffer "
8237f62fe8aSKai Vehmanen 			"configuration\n");
8247f62fe8aSKai Vehmanen 		r = -ENOBUFS;
8257f62fe8aSKai Vehmanen 	}
8267f62fe8aSKai Vehmanen 
8277f62fe8aSKai Vehmanen 	return r;
8287f62fe8aSKai Vehmanen }
8297f62fe8aSKai Vehmanen 
8304ef69e17SRandy Dunlap /*
8317f62fe8aSKai Vehmanen  * Block until pending data transfers have completed.
8327f62fe8aSKai Vehmanen  */
cs_hsi_data_sync(struct cs_hsi_iface * hi)8337f62fe8aSKai Vehmanen static int cs_hsi_data_sync(struct cs_hsi_iface *hi)
8347f62fe8aSKai Vehmanen {
8357f62fe8aSKai Vehmanen 	int r = 0;
8367f62fe8aSKai Vehmanen 
8377f62fe8aSKai Vehmanen 	spin_lock_bh(&hi->lock);
8387f62fe8aSKai Vehmanen 
8397f62fe8aSKai Vehmanen 	if (!cs_state_xfer_active(hi->data_state)) {
8407f62fe8aSKai Vehmanen 		dev_dbg(&hi->cl->device, "hsi_data_sync break, idle\n");
8417f62fe8aSKai Vehmanen 		goto out;
8427f62fe8aSKai Vehmanen 	}
8437f62fe8aSKai Vehmanen 
8447f62fe8aSKai Vehmanen 	for (;;) {
8457f62fe8aSKai Vehmanen 		int s;
8467f62fe8aSKai Vehmanen 		DEFINE_WAIT(wait);
8477f62fe8aSKai Vehmanen 		if (!cs_state_xfer_active(hi->data_state))
8487f62fe8aSKai Vehmanen 			goto out;
8497f62fe8aSKai Vehmanen 		if (signal_pending(current)) {
8507f62fe8aSKai Vehmanen 			r = -ERESTARTSYS;
8517f62fe8aSKai Vehmanen 			goto out;
8527f62fe8aSKai Vehmanen 		}
8534ef69e17SRandy Dunlap 		/*
8547f62fe8aSKai Vehmanen 		 * prepare_to_wait must be called with hi->lock held
8557f62fe8aSKai Vehmanen 		 * so that callbacks can check for waitqueue_active()
8567f62fe8aSKai Vehmanen 		 */
8577f62fe8aSKai Vehmanen 		prepare_to_wait(&hi->datawait, &wait, TASK_INTERRUPTIBLE);
8587f62fe8aSKai Vehmanen 		spin_unlock_bh(&hi->lock);
8597f62fe8aSKai Vehmanen 		s = schedule_timeout(
8607f62fe8aSKai Vehmanen 			msecs_to_jiffies(CS_HSI_TRANSFER_TIMEOUT_MS));
8617f62fe8aSKai Vehmanen 		spin_lock_bh(&hi->lock);
8627f62fe8aSKai Vehmanen 		finish_wait(&hi->datawait, &wait);
8637f62fe8aSKai Vehmanen 		if (!s) {
8647f62fe8aSKai Vehmanen 			dev_dbg(&hi->cl->device,
8657f62fe8aSKai Vehmanen 				"hsi_data_sync timeout after %d ms\n",
8667f62fe8aSKai Vehmanen 				CS_HSI_TRANSFER_TIMEOUT_MS);
8677f62fe8aSKai Vehmanen 			r = -EIO;
8687f62fe8aSKai Vehmanen 			goto out;
8697f62fe8aSKai Vehmanen 		}
8707f62fe8aSKai Vehmanen 	}
8717f62fe8aSKai Vehmanen 
8727f62fe8aSKai Vehmanen out:
8737f62fe8aSKai Vehmanen 	spin_unlock_bh(&hi->lock);
8747f62fe8aSKai Vehmanen 	dev_dbg(&hi->cl->device, "hsi_data_sync done with res %d\n", r);
8757f62fe8aSKai Vehmanen 
8767f62fe8aSKai Vehmanen 	return r;
8777f62fe8aSKai Vehmanen }
8787f62fe8aSKai Vehmanen 
cs_hsi_data_enable(struct cs_hsi_iface * hi,struct cs_buffer_config * buf_cfg)8797f62fe8aSKai Vehmanen static void cs_hsi_data_enable(struct cs_hsi_iface *hi,
8807f62fe8aSKai Vehmanen 					struct cs_buffer_config *buf_cfg)
8817f62fe8aSKai Vehmanen {
8827f62fe8aSKai Vehmanen 	unsigned int data_start, i;
8837f62fe8aSKai Vehmanen 
8847f62fe8aSKai Vehmanen 	BUG_ON(hi->buf_size == 0);
8857f62fe8aSKai Vehmanen 
8867f62fe8aSKai Vehmanen 	set_buffer_sizes(hi, buf_cfg->rx_bufs, buf_cfg->tx_bufs);
8877f62fe8aSKai Vehmanen 
8887f62fe8aSKai Vehmanen 	hi->slot_size = L1_CACHE_ALIGN(hi->buf_size);
8897f62fe8aSKai Vehmanen 	dev_dbg(&hi->cl->device,
8907f62fe8aSKai Vehmanen 			"setting slot size to %u, buf size %u, align %u\n",
8917f62fe8aSKai Vehmanen 			hi->slot_size, hi->buf_size, L1_CACHE_BYTES);
8927f62fe8aSKai Vehmanen 
8937f62fe8aSKai Vehmanen 	data_start = L1_CACHE_ALIGN(sizeof(*hi->mmap_cfg));
8947f62fe8aSKai Vehmanen 	dev_dbg(&hi->cl->device,
8957f62fe8aSKai Vehmanen 			"setting data start at %u, cfg block %u, align %u\n",
8967f62fe8aSKai Vehmanen 			data_start, sizeof(*hi->mmap_cfg), L1_CACHE_BYTES);
8977f62fe8aSKai Vehmanen 
8987f62fe8aSKai Vehmanen 	for (i = 0; i < hi->mmap_cfg->rx_bufs; i++) {
8997f62fe8aSKai Vehmanen 		hi->rx_offsets[i] = data_start + i * hi->slot_size;
9007f62fe8aSKai Vehmanen 		hi->mmap_cfg->rx_offsets[i] = hi->rx_offsets[i];
9017f62fe8aSKai Vehmanen 		dev_dbg(&hi->cl->device, "DL buf #%u at %u\n",
9027f62fe8aSKai Vehmanen 					i, hi->rx_offsets[i]);
9037f62fe8aSKai Vehmanen 	}
9047f62fe8aSKai Vehmanen 	for (i = 0; i < hi->mmap_cfg->tx_bufs; i++) {
9057f62fe8aSKai Vehmanen 		hi->tx_offsets[i] = data_start +
9067f62fe8aSKai Vehmanen 			(i + hi->mmap_cfg->rx_bufs) * hi->slot_size;
9077f62fe8aSKai Vehmanen 		hi->mmap_cfg->tx_offsets[i] = hi->tx_offsets[i];
9087f62fe8aSKai Vehmanen 		dev_dbg(&hi->cl->device, "UL buf #%u at %u\n",
9097f62fe8aSKai Vehmanen 					i, hi->rx_offsets[i]);
9107f62fe8aSKai Vehmanen 	}
9117f62fe8aSKai Vehmanen 
9127f62fe8aSKai Vehmanen 	hi->iface_state = CS_STATE_CONFIGURED;
9137f62fe8aSKai Vehmanen }
9147f62fe8aSKai Vehmanen 
cs_hsi_data_disable(struct cs_hsi_iface * hi,int old_state)9157f62fe8aSKai Vehmanen static void cs_hsi_data_disable(struct cs_hsi_iface *hi, int old_state)
9167f62fe8aSKai Vehmanen {
9177f62fe8aSKai Vehmanen 	if (old_state == CS_STATE_CONFIGURED) {
9187f62fe8aSKai Vehmanen 		dev_dbg(&hi->cl->device,
9197f62fe8aSKai Vehmanen 			"closing data channel with slot size 0\n");
9207f62fe8aSKai Vehmanen 		hi->iface_state = CS_STATE_OPENED;
9217f62fe8aSKai Vehmanen 	}
9227f62fe8aSKai Vehmanen }
9237f62fe8aSKai Vehmanen 
cs_hsi_buf_config(struct cs_hsi_iface * hi,struct cs_buffer_config * buf_cfg)9247f62fe8aSKai Vehmanen static int cs_hsi_buf_config(struct cs_hsi_iface *hi,
9257f62fe8aSKai Vehmanen 					struct cs_buffer_config *buf_cfg)
9267f62fe8aSKai Vehmanen {
9277f62fe8aSKai Vehmanen 	int r = 0;
9287f62fe8aSKai Vehmanen 	unsigned int old_state = hi->iface_state;
9297f62fe8aSKai Vehmanen 
9307f62fe8aSKai Vehmanen 	spin_lock_bh(&hi->lock);
9317f62fe8aSKai Vehmanen 	/* Prevent new transactions during buffer reconfig */
9327f62fe8aSKai Vehmanen 	if (old_state == CS_STATE_CONFIGURED)
9337f62fe8aSKai Vehmanen 		hi->iface_state = CS_STATE_OPENED;
9347f62fe8aSKai Vehmanen 	spin_unlock_bh(&hi->lock);
9357f62fe8aSKai Vehmanen 
9367f62fe8aSKai Vehmanen 	/*
9377f62fe8aSKai Vehmanen 	 * make sure that no non-zero data reads are ongoing before
9387f62fe8aSKai Vehmanen 	 * proceeding to change the buffer layout
9397f62fe8aSKai Vehmanen 	 */
9407f62fe8aSKai Vehmanen 	r = cs_hsi_data_sync(hi);
9417f62fe8aSKai Vehmanen 	if (r < 0)
9427f62fe8aSKai Vehmanen 		return r;
9437f62fe8aSKai Vehmanen 
9447f62fe8aSKai Vehmanen 	WARN_ON(cs_state_xfer_active(hi->data_state));
9457f62fe8aSKai Vehmanen 
9467f62fe8aSKai Vehmanen 	spin_lock_bh(&hi->lock);
9477f62fe8aSKai Vehmanen 	r = check_buf_params(hi, buf_cfg);
9487f62fe8aSKai Vehmanen 	if (r < 0)
9497f62fe8aSKai Vehmanen 		goto error;
9507f62fe8aSKai Vehmanen 
9517f62fe8aSKai Vehmanen 	hi->buf_size = buf_cfg->buf_size;
9527f62fe8aSKai Vehmanen 	hi->mmap_cfg->buf_size = hi->buf_size;
9537f62fe8aSKai Vehmanen 	hi->flags = buf_cfg->flags;
9547f62fe8aSKai Vehmanen 
9557f62fe8aSKai Vehmanen 	hi->rx_slot = 0;
9567f62fe8aSKai Vehmanen 	hi->tx_slot = 0;
9577f62fe8aSKai Vehmanen 	hi->slot_size = 0;
9587f62fe8aSKai Vehmanen 
9597f62fe8aSKai Vehmanen 	if (hi->buf_size)
9607f62fe8aSKai Vehmanen 		cs_hsi_data_enable(hi, buf_cfg);
9617f62fe8aSKai Vehmanen 	else
9627f62fe8aSKai Vehmanen 		cs_hsi_data_disable(hi, old_state);
9637f62fe8aSKai Vehmanen 
9647f62fe8aSKai Vehmanen 	spin_unlock_bh(&hi->lock);
9657f62fe8aSKai Vehmanen 
9667f62fe8aSKai Vehmanen 	if (old_state != hi->iface_state) {
9677f62fe8aSKai Vehmanen 		if (hi->iface_state == CS_STATE_CONFIGURED) {
9686ca50a47SRafael J. Wysocki 			cpu_latency_qos_add_request(&hi->pm_qos_req,
9697f62fe8aSKai Vehmanen 				CS_QOS_LATENCY_FOR_DATA_USEC);
9707f62fe8aSKai Vehmanen 			local_bh_disable();
9717f62fe8aSKai Vehmanen 			cs_hsi_read_on_data(hi);
9727f62fe8aSKai Vehmanen 			local_bh_enable();
9737f62fe8aSKai Vehmanen 		} else if (old_state == CS_STATE_CONFIGURED) {
9746ca50a47SRafael J. Wysocki 			cpu_latency_qos_remove_request(&hi->pm_qos_req);
9757f62fe8aSKai Vehmanen 		}
9767f62fe8aSKai Vehmanen 	}
9777f62fe8aSKai Vehmanen 	return r;
9787f62fe8aSKai Vehmanen 
9797f62fe8aSKai Vehmanen error:
9807f62fe8aSKai Vehmanen 	spin_unlock_bh(&hi->lock);
9817f62fe8aSKai Vehmanen 	return r;
9827f62fe8aSKai Vehmanen }
9837f62fe8aSKai Vehmanen 
cs_hsi_start(struct cs_hsi_iface ** hi,struct hsi_client * cl,unsigned long mmap_base,unsigned long mmap_size)9847f62fe8aSKai Vehmanen static int cs_hsi_start(struct cs_hsi_iface **hi, struct hsi_client *cl,
9857f62fe8aSKai Vehmanen 			unsigned long mmap_base, unsigned long mmap_size)
9867f62fe8aSKai Vehmanen {
9877f62fe8aSKai Vehmanen 	int err = 0;
9887f62fe8aSKai Vehmanen 	struct cs_hsi_iface *hsi_if = kzalloc(sizeof(*hsi_if), GFP_KERNEL);
9897f62fe8aSKai Vehmanen 
9907f62fe8aSKai Vehmanen 	dev_dbg(&cl->device, "cs_hsi_start\n");
9917f62fe8aSKai Vehmanen 
9927f62fe8aSKai Vehmanen 	if (!hsi_if) {
9937f62fe8aSKai Vehmanen 		err = -ENOMEM;
9947f62fe8aSKai Vehmanen 		goto leave0;
9957f62fe8aSKai Vehmanen 	}
9967f62fe8aSKai Vehmanen 	spin_lock_init(&hsi_if->lock);
9977f62fe8aSKai Vehmanen 	hsi_if->cl = cl;
9987f62fe8aSKai Vehmanen 	hsi_if->iface_state = CS_STATE_CLOSED;
9997f62fe8aSKai Vehmanen 	hsi_if->mmap_cfg = (struct cs_mmap_config_block *)mmap_base;
10007f62fe8aSKai Vehmanen 	hsi_if->mmap_base = mmap_base;
10017f62fe8aSKai Vehmanen 	hsi_if->mmap_size = mmap_size;
10027f62fe8aSKai Vehmanen 	memset(hsi_if->mmap_cfg, 0, sizeof(*hsi_if->mmap_cfg));
10037f62fe8aSKai Vehmanen 	init_waitqueue_head(&hsi_if->datawait);
10047f62fe8aSKai Vehmanen 	err = cs_alloc_cmds(hsi_if);
10057f62fe8aSKai Vehmanen 	if (err < 0) {
10067f62fe8aSKai Vehmanen 		dev_err(&cl->device, "Unable to alloc HSI messages\n");
10077f62fe8aSKai Vehmanen 		goto leave1;
10087f62fe8aSKai Vehmanen 	}
10097f62fe8aSKai Vehmanen 	err = cs_hsi_alloc_data(hsi_if);
10107f62fe8aSKai Vehmanen 	if (err < 0) {
10117f62fe8aSKai Vehmanen 		dev_err(&cl->device, "Unable to alloc HSI messages for data\n");
10127f62fe8aSKai Vehmanen 		goto leave2;
10137f62fe8aSKai Vehmanen 	}
10147f62fe8aSKai Vehmanen 	err = hsi_claim_port(cl, 1);
10157f62fe8aSKai Vehmanen 	if (err < 0) {
10167f62fe8aSKai Vehmanen 		dev_err(&cl->device,
10177f62fe8aSKai Vehmanen 				"Could not open, HSI port already claimed\n");
10187f62fe8aSKai Vehmanen 		goto leave3;
10197f62fe8aSKai Vehmanen 	}
10207f62fe8aSKai Vehmanen 	hsi_if->master = ssip_slave_get_master(cl);
10217f62fe8aSKai Vehmanen 	if (IS_ERR(hsi_if->master)) {
1022265ef3eeSJulia Lawall 		err = PTR_ERR(hsi_if->master);
10237f62fe8aSKai Vehmanen 		dev_err(&cl->device, "Could not get HSI master client\n");
10247f62fe8aSKai Vehmanen 		goto leave4;
10257f62fe8aSKai Vehmanen 	}
10267f62fe8aSKai Vehmanen 	if (!ssip_slave_running(hsi_if->master)) {
10277f62fe8aSKai Vehmanen 		err = -ENODEV;
10287f62fe8aSKai Vehmanen 		dev_err(&cl->device,
10297f62fe8aSKai Vehmanen 				"HSI port not initialized\n");
10307f62fe8aSKai Vehmanen 		goto leave4;
10317f62fe8aSKai Vehmanen 	}
10327f62fe8aSKai Vehmanen 
10337f62fe8aSKai Vehmanen 	hsi_if->iface_state = CS_STATE_OPENED;
10347f62fe8aSKai Vehmanen 	local_bh_disable();
10357f62fe8aSKai Vehmanen 	cs_hsi_read_on_control(hsi_if);
10367f62fe8aSKai Vehmanen 	local_bh_enable();
10377f62fe8aSKai Vehmanen 
10387f62fe8aSKai Vehmanen 	dev_dbg(&cl->device, "cs_hsi_start...done\n");
10397f62fe8aSKai Vehmanen 
10407f62fe8aSKai Vehmanen 	BUG_ON(!hi);
10417f62fe8aSKai Vehmanen 	*hi = hsi_if;
10427f62fe8aSKai Vehmanen 
10437f62fe8aSKai Vehmanen 	return 0;
10447f62fe8aSKai Vehmanen 
10457f62fe8aSKai Vehmanen leave4:
10467f62fe8aSKai Vehmanen 	hsi_release_port(cl);
10477f62fe8aSKai Vehmanen leave3:
10487f62fe8aSKai Vehmanen 	cs_hsi_free_data(hsi_if);
10497f62fe8aSKai Vehmanen leave2:
10507f62fe8aSKai Vehmanen 	cs_free_cmds(hsi_if);
10517f62fe8aSKai Vehmanen leave1:
10527f62fe8aSKai Vehmanen 	kfree(hsi_if);
10537f62fe8aSKai Vehmanen leave0:
10547f62fe8aSKai Vehmanen 	dev_dbg(&cl->device, "cs_hsi_start...done/error\n\n");
10557f62fe8aSKai Vehmanen 
10567f62fe8aSKai Vehmanen 	return err;
10577f62fe8aSKai Vehmanen }
10587f62fe8aSKai Vehmanen 
cs_hsi_stop(struct cs_hsi_iface * hi)10597f62fe8aSKai Vehmanen static void cs_hsi_stop(struct cs_hsi_iface *hi)
10607f62fe8aSKai Vehmanen {
10617f62fe8aSKai Vehmanen 	dev_dbg(&hi->cl->device, "cs_hsi_stop\n");
10627f62fe8aSKai Vehmanen 	cs_hsi_set_wakeline(hi, 0);
10637f62fe8aSKai Vehmanen 	ssip_slave_put_master(hi->master);
10647f62fe8aSKai Vehmanen 
10657f62fe8aSKai Vehmanen 	/* hsi_release_port() needs to be called with CS_STATE_CLOSED */
10667f62fe8aSKai Vehmanen 	hi->iface_state = CS_STATE_CLOSED;
10677f62fe8aSKai Vehmanen 	hsi_release_port(hi->cl);
10687f62fe8aSKai Vehmanen 
10697f62fe8aSKai Vehmanen 	/*
10707f62fe8aSKai Vehmanen 	 * hsi_release_port() should flush out all the pending
10717f62fe8aSKai Vehmanen 	 * messages, so cs_state_idle() should be true for both
10727f62fe8aSKai Vehmanen 	 * control and data channels.
10737f62fe8aSKai Vehmanen 	 */
10747f62fe8aSKai Vehmanen 	WARN_ON(!cs_state_idle(hi->control_state));
10757f62fe8aSKai Vehmanen 	WARN_ON(!cs_state_idle(hi->data_state));
10767f62fe8aSKai Vehmanen 
10776ca50a47SRafael J. Wysocki 	if (cpu_latency_qos_request_active(&hi->pm_qos_req))
10786ca50a47SRafael J. Wysocki 		cpu_latency_qos_remove_request(&hi->pm_qos_req);
10797f62fe8aSKai Vehmanen 
10807f62fe8aSKai Vehmanen 	spin_lock_bh(&hi->lock);
10817f62fe8aSKai Vehmanen 	cs_hsi_free_data(hi);
10827f62fe8aSKai Vehmanen 	cs_free_cmds(hi);
10837f62fe8aSKai Vehmanen 	spin_unlock_bh(&hi->lock);
10847f62fe8aSKai Vehmanen 	kfree(hi);
10857f62fe8aSKai Vehmanen }
10867f62fe8aSKai Vehmanen 
cs_char_vma_fault(struct vm_fault * vmf)108770d52ba9SSouptick Joarder static vm_fault_t cs_char_vma_fault(struct vm_fault *vmf)
10887f62fe8aSKai Vehmanen {
108911bac800SDave Jiang 	struct cs_char *csdata = vmf->vma->vm_private_data;
10907f62fe8aSKai Vehmanen 	struct page *page;
10917f62fe8aSKai Vehmanen 
10920f1a3e5fSLinus Walleij 	page = virt_to_page((void *)csdata->mmap_base);
10937f62fe8aSKai Vehmanen 	get_page(page);
10947f62fe8aSKai Vehmanen 	vmf->page = page;
10957f62fe8aSKai Vehmanen 
10967f62fe8aSKai Vehmanen 	return 0;
10977f62fe8aSKai Vehmanen }
10987f62fe8aSKai Vehmanen 
10997cbea8dcSKirill A. Shutemov static const struct vm_operations_struct cs_char_vm_ops = {
11007f62fe8aSKai Vehmanen 	.fault	= cs_char_vma_fault,
11017f62fe8aSKai Vehmanen };
11027f62fe8aSKai Vehmanen 
cs_char_fasync(int fd,struct file * file,int on)11037f62fe8aSKai Vehmanen static int cs_char_fasync(int fd, struct file *file, int on)
11047f62fe8aSKai Vehmanen {
11057f62fe8aSKai Vehmanen 	struct cs_char *csdata = file->private_data;
11067f62fe8aSKai Vehmanen 
11077f62fe8aSKai Vehmanen 	if (fasync_helper(fd, file, on, &csdata->async_queue) < 0)
11087f62fe8aSKai Vehmanen 		return -EIO;
11097f62fe8aSKai Vehmanen 
11107f62fe8aSKai Vehmanen 	return 0;
11117f62fe8aSKai Vehmanen }
11127f62fe8aSKai Vehmanen 
cs_char_poll(struct file * file,poll_table * wait)1113afc9a42bSAl Viro static __poll_t cs_char_poll(struct file *file, poll_table *wait)
11147f62fe8aSKai Vehmanen {
11157f62fe8aSKai Vehmanen 	struct cs_char *csdata = file->private_data;
1116afc9a42bSAl Viro 	__poll_t ret = 0;
11177f62fe8aSKai Vehmanen 
11187f62fe8aSKai Vehmanen 	poll_wait(file, &cs_char_data.wait, wait);
11197f62fe8aSKai Vehmanen 	spin_lock_bh(&csdata->lock);
11207f62fe8aSKai Vehmanen 	if (!list_empty(&csdata->chardev_queue))
1121a9a08845SLinus Torvalds 		ret = EPOLLIN | EPOLLRDNORM;
11227f62fe8aSKai Vehmanen 	else if (!list_empty(&csdata->dataind_queue))
1123a9a08845SLinus Torvalds 		ret = EPOLLIN | EPOLLRDNORM;
11247f62fe8aSKai Vehmanen 	spin_unlock_bh(&csdata->lock);
11257f62fe8aSKai Vehmanen 
11267f62fe8aSKai Vehmanen 	return ret;
11277f62fe8aSKai Vehmanen }
11287f62fe8aSKai Vehmanen 
cs_char_read(struct file * file,char __user * buf,size_t count,loff_t * unused)11297f62fe8aSKai Vehmanen static ssize_t cs_char_read(struct file *file, char __user *buf, size_t count,
11307f62fe8aSKai Vehmanen 								loff_t *unused)
11317f62fe8aSKai Vehmanen {
11327f62fe8aSKai Vehmanen 	struct cs_char *csdata = file->private_data;
11337f62fe8aSKai Vehmanen 	u32 data;
11347f62fe8aSKai Vehmanen 	ssize_t retval;
11357f62fe8aSKai Vehmanen 
11367f62fe8aSKai Vehmanen 	if (count < sizeof(data))
11377f62fe8aSKai Vehmanen 		return -EINVAL;
11387f62fe8aSKai Vehmanen 
11397f62fe8aSKai Vehmanen 	for (;;) {
11407f62fe8aSKai Vehmanen 		DEFINE_WAIT(wait);
11417f62fe8aSKai Vehmanen 
11427f62fe8aSKai Vehmanen 		spin_lock_bh(&csdata->lock);
11437f62fe8aSKai Vehmanen 		if (!list_empty(&csdata->chardev_queue)) {
11447f62fe8aSKai Vehmanen 			data = cs_pop_entry(&csdata->chardev_queue);
11457f62fe8aSKai Vehmanen 		} else if (!list_empty(&csdata->dataind_queue)) {
11467f62fe8aSKai Vehmanen 			data = cs_pop_entry(&csdata->dataind_queue);
11477f62fe8aSKai Vehmanen 			csdata->dataind_pending--;
11487f62fe8aSKai Vehmanen 		} else {
11497f62fe8aSKai Vehmanen 			data = 0;
11507f62fe8aSKai Vehmanen 		}
11517f62fe8aSKai Vehmanen 		spin_unlock_bh(&csdata->lock);
11527f62fe8aSKai Vehmanen 
11537f62fe8aSKai Vehmanen 		if (data)
11547f62fe8aSKai Vehmanen 			break;
11557f62fe8aSKai Vehmanen 		if (file->f_flags & O_NONBLOCK) {
11567f62fe8aSKai Vehmanen 			retval = -EAGAIN;
11577f62fe8aSKai Vehmanen 			goto out;
11587f62fe8aSKai Vehmanen 		} else if (signal_pending(current)) {
11597f62fe8aSKai Vehmanen 			retval = -ERESTARTSYS;
11607f62fe8aSKai Vehmanen 			goto out;
11617f62fe8aSKai Vehmanen 		}
11627f62fe8aSKai Vehmanen 		prepare_to_wait_exclusive(&csdata->wait, &wait,
11637f62fe8aSKai Vehmanen 						TASK_INTERRUPTIBLE);
11647f62fe8aSKai Vehmanen 		schedule();
11657f62fe8aSKai Vehmanen 		finish_wait(&csdata->wait, &wait);
11667f62fe8aSKai Vehmanen 	}
11677f62fe8aSKai Vehmanen 
11687f62fe8aSKai Vehmanen 	retval = put_user(data, (u32 __user *)buf);
11697f62fe8aSKai Vehmanen 	if (!retval)
11707f62fe8aSKai Vehmanen 		retval = sizeof(data);
11717f62fe8aSKai Vehmanen 
11727f62fe8aSKai Vehmanen out:
11737f62fe8aSKai Vehmanen 	return retval;
11747f62fe8aSKai Vehmanen }
11757f62fe8aSKai Vehmanen 
cs_char_write(struct file * file,const char __user * buf,size_t count,loff_t * unused)11767f62fe8aSKai Vehmanen static ssize_t cs_char_write(struct file *file, const char __user *buf,
11777f62fe8aSKai Vehmanen 						size_t count, loff_t *unused)
11787f62fe8aSKai Vehmanen {
11797f62fe8aSKai Vehmanen 	struct cs_char *csdata = file->private_data;
11807f62fe8aSKai Vehmanen 	u32 data;
11817f62fe8aSKai Vehmanen 	int err;
11827f62fe8aSKai Vehmanen 	ssize_t	retval;
11837f62fe8aSKai Vehmanen 
11847f62fe8aSKai Vehmanen 	if (count < sizeof(data))
11857f62fe8aSKai Vehmanen 		return -EINVAL;
11867f62fe8aSKai Vehmanen 
11877f62fe8aSKai Vehmanen 	if (get_user(data, (u32 __user *)buf))
11887f62fe8aSKai Vehmanen 		retval = -EFAULT;
11897f62fe8aSKai Vehmanen 	else
11907f62fe8aSKai Vehmanen 		retval = count;
11917f62fe8aSKai Vehmanen 
11927f62fe8aSKai Vehmanen 	err = cs_hsi_command(csdata->hi, data);
11937f62fe8aSKai Vehmanen 	if (err < 0)
11947f62fe8aSKai Vehmanen 		retval = err;
11957f62fe8aSKai Vehmanen 
11967f62fe8aSKai Vehmanen 	return retval;
11977f62fe8aSKai Vehmanen }
11987f62fe8aSKai Vehmanen 
cs_char_ioctl(struct file * file,unsigned int cmd,unsigned long arg)11997f62fe8aSKai Vehmanen static long cs_char_ioctl(struct file *file, unsigned int cmd,
12007f62fe8aSKai Vehmanen 				unsigned long arg)
12017f62fe8aSKai Vehmanen {
12027f62fe8aSKai Vehmanen 	struct cs_char *csdata = file->private_data;
12037f62fe8aSKai Vehmanen 	int r = 0;
12047f62fe8aSKai Vehmanen 
12057f62fe8aSKai Vehmanen 	switch (cmd) {
12067f62fe8aSKai Vehmanen 	case CS_GET_STATE: {
12077f62fe8aSKai Vehmanen 		unsigned int state;
12087f62fe8aSKai Vehmanen 
12097f62fe8aSKai Vehmanen 		state = cs_hsi_get_state(csdata->hi);
12107f62fe8aSKai Vehmanen 		if (copy_to_user((void __user *)arg, &state, sizeof(state)))
12117f62fe8aSKai Vehmanen 			r = -EFAULT;
12127f62fe8aSKai Vehmanen 
12137f62fe8aSKai Vehmanen 		break;
12147f62fe8aSKai Vehmanen 	}
12157f62fe8aSKai Vehmanen 	case CS_SET_WAKELINE: {
12167f62fe8aSKai Vehmanen 		unsigned int state;
12177f62fe8aSKai Vehmanen 
12187f62fe8aSKai Vehmanen 		if (copy_from_user(&state, (void __user *)arg, sizeof(state))) {
12197f62fe8aSKai Vehmanen 			r = -EFAULT;
12207f62fe8aSKai Vehmanen 			break;
12217f62fe8aSKai Vehmanen 		}
12227f62fe8aSKai Vehmanen 
12237f62fe8aSKai Vehmanen 		if (state > 1) {
12247f62fe8aSKai Vehmanen 			r = -EINVAL;
12257f62fe8aSKai Vehmanen 			break;
12267f62fe8aSKai Vehmanen 		}
12277f62fe8aSKai Vehmanen 
12287f62fe8aSKai Vehmanen 		cs_hsi_set_wakeline(csdata->hi, !!state);
12297f62fe8aSKai Vehmanen 
12307f62fe8aSKai Vehmanen 		break;
12317f62fe8aSKai Vehmanen 	}
12327f62fe8aSKai Vehmanen 	case CS_GET_IF_VERSION: {
12337f62fe8aSKai Vehmanen 		unsigned int ifver = CS_IF_VERSION;
12347f62fe8aSKai Vehmanen 
12357f62fe8aSKai Vehmanen 		if (copy_to_user((void __user *)arg, &ifver, sizeof(ifver)))
12367f62fe8aSKai Vehmanen 			r = -EFAULT;
12377f62fe8aSKai Vehmanen 
12387f62fe8aSKai Vehmanen 		break;
12397f62fe8aSKai Vehmanen 	}
12407f62fe8aSKai Vehmanen 	case CS_CONFIG_BUFS: {
12417f62fe8aSKai Vehmanen 		struct cs_buffer_config buf_cfg;
12427f62fe8aSKai Vehmanen 
12437f62fe8aSKai Vehmanen 		if (copy_from_user(&buf_cfg, (void __user *)arg,
12447f62fe8aSKai Vehmanen 							sizeof(buf_cfg)))
12457f62fe8aSKai Vehmanen 			r = -EFAULT;
12467f62fe8aSKai Vehmanen 		else
12477f62fe8aSKai Vehmanen 			r = cs_hsi_buf_config(csdata->hi, &buf_cfg);
12487f62fe8aSKai Vehmanen 
12497f62fe8aSKai Vehmanen 		break;
12507f62fe8aSKai Vehmanen 	}
12517f62fe8aSKai Vehmanen 	default:
12527f62fe8aSKai Vehmanen 		r = -ENOTTY;
12537f62fe8aSKai Vehmanen 		break;
12547f62fe8aSKai Vehmanen 	}
12557f62fe8aSKai Vehmanen 
12567f62fe8aSKai Vehmanen 	return r;
12577f62fe8aSKai Vehmanen }
12587f62fe8aSKai Vehmanen 
cs_char_mmap(struct file * file,struct vm_area_struct * vma)12597f62fe8aSKai Vehmanen static int cs_char_mmap(struct file *file, struct vm_area_struct *vma)
12607f62fe8aSKai Vehmanen {
12617f62fe8aSKai Vehmanen 	if (vma->vm_end < vma->vm_start)
12627f62fe8aSKai Vehmanen 		return -EINVAL;
12637f62fe8aSKai Vehmanen 
1264f6004b7bSMuhammad Falak R Wani 	if (vma_pages(vma) != 1)
12657f62fe8aSKai Vehmanen 		return -EINVAL;
12667f62fe8aSKai Vehmanen 
1267*1c71222eSSuren Baghdasaryan 	vm_flags_set(vma, VM_IO | VM_DONTDUMP | VM_DONTEXPAND);
12687f62fe8aSKai Vehmanen 	vma->vm_ops = &cs_char_vm_ops;
12697f62fe8aSKai Vehmanen 	vma->vm_private_data = file->private_data;
12707f62fe8aSKai Vehmanen 
12717f62fe8aSKai Vehmanen 	return 0;
12727f62fe8aSKai Vehmanen }
12737f62fe8aSKai Vehmanen 
cs_char_open(struct inode * unused,struct file * file)12747f62fe8aSKai Vehmanen static int cs_char_open(struct inode *unused, struct file *file)
12757f62fe8aSKai Vehmanen {
12767f62fe8aSKai Vehmanen 	int ret = 0;
12777f62fe8aSKai Vehmanen 	unsigned long p;
12787f62fe8aSKai Vehmanen 
12797f62fe8aSKai Vehmanen 	spin_lock_bh(&cs_char_data.lock);
12807f62fe8aSKai Vehmanen 	if (cs_char_data.opened) {
12817f62fe8aSKai Vehmanen 		ret = -EBUSY;
12827f62fe8aSKai Vehmanen 		spin_unlock_bh(&cs_char_data.lock);
12837f62fe8aSKai Vehmanen 		goto out1;
12847f62fe8aSKai Vehmanen 	}
12857f62fe8aSKai Vehmanen 	cs_char_data.opened = 1;
12867f62fe8aSKai Vehmanen 	cs_char_data.dataind_pending = 0;
12877f62fe8aSKai Vehmanen 	spin_unlock_bh(&cs_char_data.lock);
12887f62fe8aSKai Vehmanen 
12897f62fe8aSKai Vehmanen 	p = get_zeroed_page(GFP_KERNEL);
12907f62fe8aSKai Vehmanen 	if (!p) {
12917f62fe8aSKai Vehmanen 		ret = -ENOMEM;
12927f62fe8aSKai Vehmanen 		goto out2;
12937f62fe8aSKai Vehmanen 	}
12947f62fe8aSKai Vehmanen 
12957f62fe8aSKai Vehmanen 	ret = cs_hsi_start(&cs_char_data.hi, cs_char_data.cl, p, CS_MMAP_SIZE);
12967f62fe8aSKai Vehmanen 	if (ret) {
12977f62fe8aSKai Vehmanen 		dev_err(&cs_char_data.cl->device, "Unable to initialize HSI\n");
12987f62fe8aSKai Vehmanen 		goto out3;
12997f62fe8aSKai Vehmanen 	}
13007f62fe8aSKai Vehmanen 
13017f62fe8aSKai Vehmanen 	/* these are only used in release so lock not needed */
13027f62fe8aSKai Vehmanen 	cs_char_data.mmap_base = p;
13037f62fe8aSKai Vehmanen 	cs_char_data.mmap_size = CS_MMAP_SIZE;
13047f62fe8aSKai Vehmanen 
13057f62fe8aSKai Vehmanen 	file->private_data = &cs_char_data;
13067f62fe8aSKai Vehmanen 
13077f62fe8aSKai Vehmanen 	return 0;
13087f62fe8aSKai Vehmanen 
13097f62fe8aSKai Vehmanen out3:
13107f62fe8aSKai Vehmanen 	free_page(p);
13117f62fe8aSKai Vehmanen out2:
13127f62fe8aSKai Vehmanen 	spin_lock_bh(&cs_char_data.lock);
13137f62fe8aSKai Vehmanen 	cs_char_data.opened = 0;
13147f62fe8aSKai Vehmanen 	spin_unlock_bh(&cs_char_data.lock);
13157f62fe8aSKai Vehmanen out1:
13167f62fe8aSKai Vehmanen 	return ret;
13177f62fe8aSKai Vehmanen }
13187f62fe8aSKai Vehmanen 
cs_free_char_queue(struct list_head * head)13197f62fe8aSKai Vehmanen static void cs_free_char_queue(struct list_head *head)
13207f62fe8aSKai Vehmanen {
13217f62fe8aSKai Vehmanen 	struct char_queue *entry;
13227f62fe8aSKai Vehmanen 	struct list_head *cursor, *next;
13237f62fe8aSKai Vehmanen 
13247f62fe8aSKai Vehmanen 	if (!list_empty(head)) {
13257f62fe8aSKai Vehmanen 		list_for_each_safe(cursor, next, head) {
13267f62fe8aSKai Vehmanen 			entry = list_entry(cursor, struct char_queue, list);
13277f62fe8aSKai Vehmanen 			list_del(&entry->list);
13287f62fe8aSKai Vehmanen 			kfree(entry);
13297f62fe8aSKai Vehmanen 		}
13307f62fe8aSKai Vehmanen 	}
13317f62fe8aSKai Vehmanen 
13327f62fe8aSKai Vehmanen }
13337f62fe8aSKai Vehmanen 
cs_char_release(struct inode * unused,struct file * file)13347f62fe8aSKai Vehmanen static int cs_char_release(struct inode *unused, struct file *file)
13357f62fe8aSKai Vehmanen {
13367f62fe8aSKai Vehmanen 	struct cs_char *csdata = file->private_data;
13377f62fe8aSKai Vehmanen 
13387f62fe8aSKai Vehmanen 	cs_hsi_stop(csdata->hi);
13397f62fe8aSKai Vehmanen 	spin_lock_bh(&csdata->lock);
13407f62fe8aSKai Vehmanen 	csdata->hi = NULL;
13417f62fe8aSKai Vehmanen 	free_page(csdata->mmap_base);
13427f62fe8aSKai Vehmanen 	cs_free_char_queue(&csdata->chardev_queue);
13437f62fe8aSKai Vehmanen 	cs_free_char_queue(&csdata->dataind_queue);
13447f62fe8aSKai Vehmanen 	csdata->opened = 0;
13457f62fe8aSKai Vehmanen 	spin_unlock_bh(&csdata->lock);
13467f62fe8aSKai Vehmanen 
13477f62fe8aSKai Vehmanen 	return 0;
13487f62fe8aSKai Vehmanen }
13497f62fe8aSKai Vehmanen 
13507f62fe8aSKai Vehmanen static const struct file_operations cs_char_fops = {
13517f62fe8aSKai Vehmanen 	.owner		= THIS_MODULE,
13527f62fe8aSKai Vehmanen 	.read		= cs_char_read,
13537f62fe8aSKai Vehmanen 	.write		= cs_char_write,
13547f62fe8aSKai Vehmanen 	.poll		= cs_char_poll,
13557f62fe8aSKai Vehmanen 	.unlocked_ioctl	= cs_char_ioctl,
13567f62fe8aSKai Vehmanen 	.mmap		= cs_char_mmap,
13577f62fe8aSKai Vehmanen 	.open		= cs_char_open,
13587f62fe8aSKai Vehmanen 	.release	= cs_char_release,
13597f62fe8aSKai Vehmanen 	.fasync		= cs_char_fasync,
13607f62fe8aSKai Vehmanen };
13617f62fe8aSKai Vehmanen 
13627f62fe8aSKai Vehmanen static struct miscdevice cs_char_miscdev = {
13637f62fe8aSKai Vehmanen 	.minor	= MISC_DYNAMIC_MINOR,
13647f62fe8aSKai Vehmanen 	.name	= "cmt_speech",
13657f62fe8aSKai Vehmanen 	.fops	= &cs_char_fops
13667f62fe8aSKai Vehmanen };
13677f62fe8aSKai Vehmanen 
cs_hsi_client_probe(struct device * dev)13687f62fe8aSKai Vehmanen static int cs_hsi_client_probe(struct device *dev)
13697f62fe8aSKai Vehmanen {
13707f62fe8aSKai Vehmanen 	int err = 0;
13717f62fe8aSKai Vehmanen 	struct hsi_client *cl = to_hsi_client(dev);
13727f62fe8aSKai Vehmanen 
13737f62fe8aSKai Vehmanen 	dev_dbg(dev, "hsi_client_probe\n");
13747f62fe8aSKai Vehmanen 	init_waitqueue_head(&cs_char_data.wait);
13757f62fe8aSKai Vehmanen 	spin_lock_init(&cs_char_data.lock);
13767f62fe8aSKai Vehmanen 	cs_char_data.opened = 0;
13777f62fe8aSKai Vehmanen 	cs_char_data.cl = cl;
13787f62fe8aSKai Vehmanen 	cs_char_data.hi = NULL;
13797f62fe8aSKai Vehmanen 	INIT_LIST_HEAD(&cs_char_data.chardev_queue);
13807f62fe8aSKai Vehmanen 	INIT_LIST_HEAD(&cs_char_data.dataind_queue);
13817f62fe8aSKai Vehmanen 
13827f62fe8aSKai Vehmanen 	cs_char_data.channel_id_cmd = hsi_get_channel_id_by_name(cl,
13837f62fe8aSKai Vehmanen 		"speech-control");
13847f62fe8aSKai Vehmanen 	if (cs_char_data.channel_id_cmd < 0) {
13857f62fe8aSKai Vehmanen 		err = cs_char_data.channel_id_cmd;
13867f62fe8aSKai Vehmanen 		dev_err(dev, "Could not get cmd channel (%d)\n", err);
13877f62fe8aSKai Vehmanen 		return err;
13887f62fe8aSKai Vehmanen 	}
13897f62fe8aSKai Vehmanen 
13907f62fe8aSKai Vehmanen 	cs_char_data.channel_id_data = hsi_get_channel_id_by_name(cl,
13917f62fe8aSKai Vehmanen 		"speech-data");
13927f62fe8aSKai Vehmanen 	if (cs_char_data.channel_id_data < 0) {
13937f62fe8aSKai Vehmanen 		err = cs_char_data.channel_id_data;
13947f62fe8aSKai Vehmanen 		dev_err(dev, "Could not get data channel (%d)\n", err);
13957f62fe8aSKai Vehmanen 		return err;
13967f62fe8aSKai Vehmanen 	}
13977f62fe8aSKai Vehmanen 
13987f62fe8aSKai Vehmanen 	err = misc_register(&cs_char_miscdev);
13997f62fe8aSKai Vehmanen 	if (err)
14007f62fe8aSKai Vehmanen 		dev_err(dev, "Failed to register: %d\n", err);
14017f62fe8aSKai Vehmanen 
14027f62fe8aSKai Vehmanen 	return err;
14037f62fe8aSKai Vehmanen }
14047f62fe8aSKai Vehmanen 
cs_hsi_client_remove(struct device * dev)14057f62fe8aSKai Vehmanen static int cs_hsi_client_remove(struct device *dev)
14067f62fe8aSKai Vehmanen {
14077f62fe8aSKai Vehmanen 	struct cs_hsi_iface *hi;
14087f62fe8aSKai Vehmanen 
14097f62fe8aSKai Vehmanen 	dev_dbg(dev, "hsi_client_remove\n");
14107f62fe8aSKai Vehmanen 	misc_deregister(&cs_char_miscdev);
14117f62fe8aSKai Vehmanen 	spin_lock_bh(&cs_char_data.lock);
14127f62fe8aSKai Vehmanen 	hi = cs_char_data.hi;
14137f62fe8aSKai Vehmanen 	cs_char_data.hi = NULL;
14147f62fe8aSKai Vehmanen 	spin_unlock_bh(&cs_char_data.lock);
14157f62fe8aSKai Vehmanen 	if (hi)
14167f62fe8aSKai Vehmanen 		cs_hsi_stop(hi);
14177f62fe8aSKai Vehmanen 
14187f62fe8aSKai Vehmanen 	return 0;
14197f62fe8aSKai Vehmanen }
14207f62fe8aSKai Vehmanen 
14217f62fe8aSKai Vehmanen static struct hsi_client_driver cs_hsi_driver = {
14227f62fe8aSKai Vehmanen 	.driver = {
14237f62fe8aSKai Vehmanen 		.name	= "cmt-speech",
14247f62fe8aSKai Vehmanen 		.owner	= THIS_MODULE,
14257f62fe8aSKai Vehmanen 		.probe	= cs_hsi_client_probe,
14267f62fe8aSKai Vehmanen 		.remove	= cs_hsi_client_remove,
14277f62fe8aSKai Vehmanen 	},
14287f62fe8aSKai Vehmanen };
14297f62fe8aSKai Vehmanen 
cs_char_init(void)14307f62fe8aSKai Vehmanen static int __init cs_char_init(void)
14317f62fe8aSKai Vehmanen {
14327f62fe8aSKai Vehmanen 	pr_info("CMT speech driver added\n");
14337f62fe8aSKai Vehmanen 	return hsi_register_client_driver(&cs_hsi_driver);
14347f62fe8aSKai Vehmanen }
14357f62fe8aSKai Vehmanen module_init(cs_char_init);
14367f62fe8aSKai Vehmanen 
cs_char_exit(void)14377f62fe8aSKai Vehmanen static void __exit cs_char_exit(void)
14387f62fe8aSKai Vehmanen {
14397f62fe8aSKai Vehmanen 	hsi_unregister_client_driver(&cs_hsi_driver);
14407f62fe8aSKai Vehmanen 	pr_info("CMT speech driver removed\n");
14417f62fe8aSKai Vehmanen }
14427f62fe8aSKai Vehmanen module_exit(cs_char_exit);
14437f62fe8aSKai Vehmanen 
14447f62fe8aSKai Vehmanen MODULE_ALIAS("hsi:cmt-speech");
14457f62fe8aSKai Vehmanen MODULE_AUTHOR("Kai Vehmanen <kai.vehmanen@nokia.com>");
14467f62fe8aSKai Vehmanen MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
14477f62fe8aSKai Vehmanen MODULE_DESCRIPTION("CMT speech driver");
14487f62fe8aSKai Vehmanen MODULE_LICENSE("GPL v2");
1449