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