162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * cmt_speech.c - HSI CMT speech driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008,2009,2010 Nokia Corporation. All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Contact: Kai Vehmanen <kai.vehmanen@nokia.com> 862306a36Sopenharmony_ci * Original author: Peter Ujfalusi <peter.ujfalusi@nokia.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/types.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/device.h> 1662306a36Sopenharmony_ci#include <linux/miscdevice.h> 1762306a36Sopenharmony_ci#include <linux/mm.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/fs.h> 2062306a36Sopenharmony_ci#include <linux/poll.h> 2162306a36Sopenharmony_ci#include <linux/sched/signal.h> 2262306a36Sopenharmony_ci#include <linux/ioctl.h> 2362306a36Sopenharmony_ci#include <linux/uaccess.h> 2462306a36Sopenharmony_ci#include <linux/pm_qos.h> 2562306a36Sopenharmony_ci#include <linux/hsi/hsi.h> 2662306a36Sopenharmony_ci#include <linux/hsi/ssi_protocol.h> 2762306a36Sopenharmony_ci#include <linux/hsi/cs-protocol.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define CS_MMAP_SIZE PAGE_SIZE 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct char_queue { 3262306a36Sopenharmony_ci struct list_head list; 3362306a36Sopenharmony_ci u32 msg; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct cs_char { 3762306a36Sopenharmony_ci unsigned int opened; 3862306a36Sopenharmony_ci struct hsi_client *cl; 3962306a36Sopenharmony_ci struct cs_hsi_iface *hi; 4062306a36Sopenharmony_ci struct list_head chardev_queue; 4162306a36Sopenharmony_ci struct list_head dataind_queue; 4262306a36Sopenharmony_ci int dataind_pending; 4362306a36Sopenharmony_ci /* mmap things */ 4462306a36Sopenharmony_ci unsigned long mmap_base; 4562306a36Sopenharmony_ci unsigned long mmap_size; 4662306a36Sopenharmony_ci spinlock_t lock; 4762306a36Sopenharmony_ci struct fasync_struct *async_queue; 4862306a36Sopenharmony_ci wait_queue_head_t wait; 4962306a36Sopenharmony_ci /* hsi channel ids */ 5062306a36Sopenharmony_ci int channel_id_cmd; 5162306a36Sopenharmony_ci int channel_id_data; 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define SSI_CHANNEL_STATE_READING 1 5562306a36Sopenharmony_ci#define SSI_CHANNEL_STATE_WRITING (1 << 1) 5662306a36Sopenharmony_ci#define SSI_CHANNEL_STATE_POLL (1 << 2) 5762306a36Sopenharmony_ci#define SSI_CHANNEL_STATE_ERROR (1 << 3) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define TARGET_MASK 0xf000000 6062306a36Sopenharmony_ci#define TARGET_REMOTE (1 << CS_DOMAIN_SHIFT) 6162306a36Sopenharmony_ci#define TARGET_LOCAL 0 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* Number of pre-allocated commands buffers */ 6462306a36Sopenharmony_ci#define CS_MAX_CMDS 4 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* 6762306a36Sopenharmony_ci * During data transfers, transactions must be handled 6862306a36Sopenharmony_ci * within 20ms (fixed value in cmtspeech HSI protocol) 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci#define CS_QOS_LATENCY_FOR_DATA_USEC 20000 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Timeout to wait for pending HSI transfers to complete */ 7362306a36Sopenharmony_ci#define CS_HSI_TRANSFER_TIMEOUT_MS 500 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define RX_PTR_BOUNDARY_SHIFT 8 7762306a36Sopenharmony_ci#define RX_PTR_MAX_SHIFT (RX_PTR_BOUNDARY_SHIFT + \ 7862306a36Sopenharmony_ci CS_MAX_BUFFERS_SHIFT) 7962306a36Sopenharmony_cistruct cs_hsi_iface { 8062306a36Sopenharmony_ci struct hsi_client *cl; 8162306a36Sopenharmony_ci struct hsi_client *master; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci unsigned int iface_state; 8462306a36Sopenharmony_ci unsigned int wakeline_state; 8562306a36Sopenharmony_ci unsigned int control_state; 8662306a36Sopenharmony_ci unsigned int data_state; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* state exposed to application */ 8962306a36Sopenharmony_ci struct cs_mmap_config_block *mmap_cfg; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci unsigned long mmap_base; 9262306a36Sopenharmony_ci unsigned long mmap_size; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci unsigned int rx_slot; 9562306a36Sopenharmony_ci unsigned int tx_slot; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* note: for security reasons, we do not trust the contents of 9862306a36Sopenharmony_ci * mmap_cfg, but instead duplicate the variables here */ 9962306a36Sopenharmony_ci unsigned int buf_size; 10062306a36Sopenharmony_ci unsigned int rx_bufs; 10162306a36Sopenharmony_ci unsigned int tx_bufs; 10262306a36Sopenharmony_ci unsigned int rx_ptr_boundary; 10362306a36Sopenharmony_ci unsigned int rx_offsets[CS_MAX_BUFFERS]; 10462306a36Sopenharmony_ci unsigned int tx_offsets[CS_MAX_BUFFERS]; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* size of aligned memory blocks */ 10762306a36Sopenharmony_ci unsigned int slot_size; 10862306a36Sopenharmony_ci unsigned int flags; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci struct list_head cmdqueue; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci struct hsi_msg *data_rx_msg; 11362306a36Sopenharmony_ci struct hsi_msg *data_tx_msg; 11462306a36Sopenharmony_ci wait_queue_head_t datawait; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci struct pm_qos_request pm_qos_req; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci spinlock_t lock; 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic struct cs_char cs_char_data; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic void cs_hsi_read_on_control(struct cs_hsi_iface *hi); 12462306a36Sopenharmony_cistatic void cs_hsi_read_on_data(struct cs_hsi_iface *hi); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic inline void rx_ptr_shift_too_big(void) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci BUILD_BUG_ON((1LLU << RX_PTR_MAX_SHIFT) > UINT_MAX); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void cs_notify(u32 message, struct list_head *head) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct char_queue *entry; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci spin_lock(&cs_char_data.lock); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (!cs_char_data.opened) { 13862306a36Sopenharmony_ci spin_unlock(&cs_char_data.lock); 13962306a36Sopenharmony_ci goto out; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_ATOMIC); 14362306a36Sopenharmony_ci if (!entry) { 14462306a36Sopenharmony_ci dev_err(&cs_char_data.cl->device, 14562306a36Sopenharmony_ci "Can't allocate new entry for the queue.\n"); 14662306a36Sopenharmony_ci spin_unlock(&cs_char_data.lock); 14762306a36Sopenharmony_ci goto out; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci entry->msg = message; 15162306a36Sopenharmony_ci list_add_tail(&entry->list, head); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci spin_unlock(&cs_char_data.lock); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci wake_up_interruptible(&cs_char_data.wait); 15662306a36Sopenharmony_ci kill_fasync(&cs_char_data.async_queue, SIGIO, POLL_IN); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ciout: 15962306a36Sopenharmony_ci return; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic u32 cs_pop_entry(struct list_head *head) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct char_queue *entry; 16562306a36Sopenharmony_ci u32 data; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci entry = list_entry(head->next, struct char_queue, list); 16862306a36Sopenharmony_ci data = entry->msg; 16962306a36Sopenharmony_ci list_del(&entry->list); 17062306a36Sopenharmony_ci kfree(entry); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return data; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic void cs_notify_control(u32 message) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci cs_notify(message, &cs_char_data.chardev_queue); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void cs_notify_data(u32 message, int maxlength) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci cs_notify(message, &cs_char_data.dataind_queue); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci spin_lock(&cs_char_data.lock); 18562306a36Sopenharmony_ci cs_char_data.dataind_pending++; 18662306a36Sopenharmony_ci while (cs_char_data.dataind_pending > maxlength && 18762306a36Sopenharmony_ci !list_empty(&cs_char_data.dataind_queue)) { 18862306a36Sopenharmony_ci dev_dbg(&cs_char_data.cl->device, "data notification " 18962306a36Sopenharmony_ci "queue overrun (%u entries)\n", cs_char_data.dataind_pending); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci cs_pop_entry(&cs_char_data.dataind_queue); 19262306a36Sopenharmony_ci cs_char_data.dataind_pending--; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci spin_unlock(&cs_char_data.lock); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic inline void cs_set_cmd(struct hsi_msg *msg, u32 cmd) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci u32 *data = sg_virt(msg->sgt.sgl); 20062306a36Sopenharmony_ci *data = cmd; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic inline u32 cs_get_cmd(struct hsi_msg *msg) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci u32 *data = sg_virt(msg->sgt.sgl); 20662306a36Sopenharmony_ci return *data; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic void cs_release_cmd(struct hsi_msg *msg) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct cs_hsi_iface *hi = msg->context; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci list_add_tail(&msg->link, &hi->cmdqueue); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic void cs_cmd_destructor(struct hsi_msg *msg) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct cs_hsi_iface *hi = msg->context; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci spin_lock(&hi->lock); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci dev_dbg(&cs_char_data.cl->device, "control cmd destructor\n"); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (hi->iface_state != CS_STATE_CLOSED) 22562306a36Sopenharmony_ci dev_err(&hi->cl->device, "Cmd flushed while driver active\n"); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (msg->ttype == HSI_MSG_READ) 22862306a36Sopenharmony_ci hi->control_state &= 22962306a36Sopenharmony_ci ~(SSI_CHANNEL_STATE_POLL | SSI_CHANNEL_STATE_READING); 23062306a36Sopenharmony_ci else if (msg->ttype == HSI_MSG_WRITE && 23162306a36Sopenharmony_ci hi->control_state & SSI_CHANNEL_STATE_WRITING) 23262306a36Sopenharmony_ci hi->control_state &= ~SSI_CHANNEL_STATE_WRITING; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci cs_release_cmd(msg); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci spin_unlock(&hi->lock); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic struct hsi_msg *cs_claim_cmd(struct cs_hsi_iface* ssi) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct hsi_msg *msg; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci BUG_ON(list_empty(&ssi->cmdqueue)); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci msg = list_first_entry(&ssi->cmdqueue, struct hsi_msg, link); 24662306a36Sopenharmony_ci list_del(&msg->link); 24762306a36Sopenharmony_ci msg->destructor = cs_cmd_destructor; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return msg; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void cs_free_cmds(struct cs_hsi_iface *ssi) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct hsi_msg *msg, *tmp; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci list_for_each_entry_safe(msg, tmp, &ssi->cmdqueue, link) { 25762306a36Sopenharmony_ci list_del(&msg->link); 25862306a36Sopenharmony_ci msg->destructor = NULL; 25962306a36Sopenharmony_ci kfree(sg_virt(msg->sgt.sgl)); 26062306a36Sopenharmony_ci hsi_free_msg(msg); 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int cs_alloc_cmds(struct cs_hsi_iface *hi) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct hsi_msg *msg; 26762306a36Sopenharmony_ci u32 *buf; 26862306a36Sopenharmony_ci unsigned int i; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci INIT_LIST_HEAD(&hi->cmdqueue); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci for (i = 0; i < CS_MAX_CMDS; i++) { 27362306a36Sopenharmony_ci msg = hsi_alloc_msg(1, GFP_KERNEL); 27462306a36Sopenharmony_ci if (!msg) 27562306a36Sopenharmony_ci goto out; 27662306a36Sopenharmony_ci buf = kmalloc(sizeof(*buf), GFP_KERNEL); 27762306a36Sopenharmony_ci if (!buf) { 27862306a36Sopenharmony_ci hsi_free_msg(msg); 27962306a36Sopenharmony_ci goto out; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci sg_init_one(msg->sgt.sgl, buf, sizeof(*buf)); 28262306a36Sopenharmony_ci msg->channel = cs_char_data.channel_id_cmd; 28362306a36Sopenharmony_ci msg->context = hi; 28462306a36Sopenharmony_ci list_add_tail(&msg->link, &hi->cmdqueue); 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ciout: 29062306a36Sopenharmony_ci cs_free_cmds(hi); 29162306a36Sopenharmony_ci return -ENOMEM; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void cs_hsi_data_destructor(struct hsi_msg *msg) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct cs_hsi_iface *hi = msg->context; 29762306a36Sopenharmony_ci const char *dir = (msg->ttype == HSI_MSG_READ) ? "TX" : "RX"; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci dev_dbg(&cs_char_data.cl->device, "Freeing data %s message\n", dir); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci spin_lock(&hi->lock); 30262306a36Sopenharmony_ci if (hi->iface_state != CS_STATE_CLOSED) 30362306a36Sopenharmony_ci dev_err(&cs_char_data.cl->device, 30462306a36Sopenharmony_ci "Data %s flush while device active\n", dir); 30562306a36Sopenharmony_ci if (msg->ttype == HSI_MSG_READ) 30662306a36Sopenharmony_ci hi->data_state &= 30762306a36Sopenharmony_ci ~(SSI_CHANNEL_STATE_POLL | SSI_CHANNEL_STATE_READING); 30862306a36Sopenharmony_ci else 30962306a36Sopenharmony_ci hi->data_state &= ~SSI_CHANNEL_STATE_WRITING; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci msg->status = HSI_STATUS_COMPLETED; 31262306a36Sopenharmony_ci if (unlikely(waitqueue_active(&hi->datawait))) 31362306a36Sopenharmony_ci wake_up_interruptible(&hi->datawait); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci spin_unlock(&hi->lock); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int cs_hsi_alloc_data(struct cs_hsi_iface *hi) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct hsi_msg *txmsg, *rxmsg; 32162306a36Sopenharmony_ci int res = 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci rxmsg = hsi_alloc_msg(1, GFP_KERNEL); 32462306a36Sopenharmony_ci if (!rxmsg) { 32562306a36Sopenharmony_ci res = -ENOMEM; 32662306a36Sopenharmony_ci goto out1; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci rxmsg->channel = cs_char_data.channel_id_data; 32962306a36Sopenharmony_ci rxmsg->destructor = cs_hsi_data_destructor; 33062306a36Sopenharmony_ci rxmsg->context = hi; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci txmsg = hsi_alloc_msg(1, GFP_KERNEL); 33362306a36Sopenharmony_ci if (!txmsg) { 33462306a36Sopenharmony_ci res = -ENOMEM; 33562306a36Sopenharmony_ci goto out2; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci txmsg->channel = cs_char_data.channel_id_data; 33862306a36Sopenharmony_ci txmsg->destructor = cs_hsi_data_destructor; 33962306a36Sopenharmony_ci txmsg->context = hi; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci hi->data_rx_msg = rxmsg; 34262306a36Sopenharmony_ci hi->data_tx_msg = txmsg; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return 0; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ciout2: 34762306a36Sopenharmony_ci hsi_free_msg(rxmsg); 34862306a36Sopenharmony_ciout1: 34962306a36Sopenharmony_ci return res; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic void cs_hsi_free_data_msg(struct hsi_msg *msg) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci WARN_ON(msg->status != HSI_STATUS_COMPLETED && 35562306a36Sopenharmony_ci msg->status != HSI_STATUS_ERROR); 35662306a36Sopenharmony_ci hsi_free_msg(msg); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void cs_hsi_free_data(struct cs_hsi_iface *hi) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci cs_hsi_free_data_msg(hi->data_rx_msg); 36262306a36Sopenharmony_ci cs_hsi_free_data_msg(hi->data_tx_msg); 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic inline void __cs_hsi_error_pre(struct cs_hsi_iface *hi, 36662306a36Sopenharmony_ci struct hsi_msg *msg, const char *info, 36762306a36Sopenharmony_ci unsigned int *state) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci spin_lock(&hi->lock); 37062306a36Sopenharmony_ci dev_err(&hi->cl->device, "HSI %s error, msg %d, state %u\n", 37162306a36Sopenharmony_ci info, msg->status, *state); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic inline void __cs_hsi_error_post(struct cs_hsi_iface *hi) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci spin_unlock(&hi->lock); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic inline void __cs_hsi_error_read_bits(unsigned int *state) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci *state |= SSI_CHANNEL_STATE_ERROR; 38262306a36Sopenharmony_ci *state &= ~(SSI_CHANNEL_STATE_READING | SSI_CHANNEL_STATE_POLL); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic inline void __cs_hsi_error_write_bits(unsigned int *state) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci *state |= SSI_CHANNEL_STATE_ERROR; 38862306a36Sopenharmony_ci *state &= ~SSI_CHANNEL_STATE_WRITING; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic void cs_hsi_control_read_error(struct cs_hsi_iface *hi, 39262306a36Sopenharmony_ci struct hsi_msg *msg) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci __cs_hsi_error_pre(hi, msg, "control read", &hi->control_state); 39562306a36Sopenharmony_ci cs_release_cmd(msg); 39662306a36Sopenharmony_ci __cs_hsi_error_read_bits(&hi->control_state); 39762306a36Sopenharmony_ci __cs_hsi_error_post(hi); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic void cs_hsi_control_write_error(struct cs_hsi_iface *hi, 40162306a36Sopenharmony_ci struct hsi_msg *msg) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci __cs_hsi_error_pre(hi, msg, "control write", &hi->control_state); 40462306a36Sopenharmony_ci cs_release_cmd(msg); 40562306a36Sopenharmony_ci __cs_hsi_error_write_bits(&hi->control_state); 40662306a36Sopenharmony_ci __cs_hsi_error_post(hi); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic void cs_hsi_data_read_error(struct cs_hsi_iface *hi, struct hsi_msg *msg) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci __cs_hsi_error_pre(hi, msg, "data read", &hi->data_state); 41362306a36Sopenharmony_ci __cs_hsi_error_read_bits(&hi->data_state); 41462306a36Sopenharmony_ci __cs_hsi_error_post(hi); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic void cs_hsi_data_write_error(struct cs_hsi_iface *hi, 41862306a36Sopenharmony_ci struct hsi_msg *msg) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci __cs_hsi_error_pre(hi, msg, "data write", &hi->data_state); 42162306a36Sopenharmony_ci __cs_hsi_error_write_bits(&hi->data_state); 42262306a36Sopenharmony_ci __cs_hsi_error_post(hi); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void cs_hsi_read_on_control_complete(struct hsi_msg *msg) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci u32 cmd = cs_get_cmd(msg); 42862306a36Sopenharmony_ci struct cs_hsi_iface *hi = msg->context; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci spin_lock(&hi->lock); 43162306a36Sopenharmony_ci hi->control_state &= ~SSI_CHANNEL_STATE_READING; 43262306a36Sopenharmony_ci if (msg->status == HSI_STATUS_ERROR) { 43362306a36Sopenharmony_ci dev_err(&hi->cl->device, "Control RX error detected\n"); 43462306a36Sopenharmony_ci spin_unlock(&hi->lock); 43562306a36Sopenharmony_ci cs_hsi_control_read_error(hi, msg); 43662306a36Sopenharmony_ci goto out; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci dev_dbg(&hi->cl->device, "Read on control: %08X\n", cmd); 43962306a36Sopenharmony_ci cs_release_cmd(msg); 44062306a36Sopenharmony_ci if (hi->flags & CS_FEAT_TSTAMP_RX_CTRL) { 44162306a36Sopenharmony_ci struct timespec64 tspec; 44262306a36Sopenharmony_ci struct cs_timestamp *tstamp = 44362306a36Sopenharmony_ci &hi->mmap_cfg->tstamp_rx_ctrl; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ktime_get_ts64(&tspec); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci tstamp->tv_sec = (__u32) tspec.tv_sec; 44862306a36Sopenharmony_ci tstamp->tv_nsec = (__u32) tspec.tv_nsec; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci spin_unlock(&hi->lock); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci cs_notify_control(cmd); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ciout: 45562306a36Sopenharmony_ci cs_hsi_read_on_control(hi); 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic void cs_hsi_peek_on_control_complete(struct hsi_msg *msg) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct cs_hsi_iface *hi = msg->context; 46162306a36Sopenharmony_ci int ret; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (msg->status == HSI_STATUS_ERROR) { 46462306a36Sopenharmony_ci dev_err(&hi->cl->device, "Control peek RX error detected\n"); 46562306a36Sopenharmony_ci cs_hsi_control_read_error(hi, msg); 46662306a36Sopenharmony_ci return; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci WARN_ON(!(hi->control_state & SSI_CHANNEL_STATE_READING)); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci dev_dbg(&hi->cl->device, "Peek on control complete, reading\n"); 47262306a36Sopenharmony_ci msg->sgt.nents = 1; 47362306a36Sopenharmony_ci msg->complete = cs_hsi_read_on_control_complete; 47462306a36Sopenharmony_ci ret = hsi_async_read(hi->cl, msg); 47562306a36Sopenharmony_ci if (ret) 47662306a36Sopenharmony_ci cs_hsi_control_read_error(hi, msg); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic void cs_hsi_read_on_control(struct cs_hsi_iface *hi) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct hsi_msg *msg; 48262306a36Sopenharmony_ci int ret; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci spin_lock(&hi->lock); 48562306a36Sopenharmony_ci if (hi->control_state & SSI_CHANNEL_STATE_READING) { 48662306a36Sopenharmony_ci dev_err(&hi->cl->device, "Control read already pending (%d)\n", 48762306a36Sopenharmony_ci hi->control_state); 48862306a36Sopenharmony_ci spin_unlock(&hi->lock); 48962306a36Sopenharmony_ci return; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci if (hi->control_state & SSI_CHANNEL_STATE_ERROR) { 49262306a36Sopenharmony_ci dev_err(&hi->cl->device, "Control read error (%d)\n", 49362306a36Sopenharmony_ci hi->control_state); 49462306a36Sopenharmony_ci spin_unlock(&hi->lock); 49562306a36Sopenharmony_ci return; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci hi->control_state |= SSI_CHANNEL_STATE_READING; 49862306a36Sopenharmony_ci dev_dbg(&hi->cl->device, "Issuing RX on control\n"); 49962306a36Sopenharmony_ci msg = cs_claim_cmd(hi); 50062306a36Sopenharmony_ci spin_unlock(&hi->lock); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci msg->sgt.nents = 0; 50362306a36Sopenharmony_ci msg->complete = cs_hsi_peek_on_control_complete; 50462306a36Sopenharmony_ci ret = hsi_async_read(hi->cl, msg); 50562306a36Sopenharmony_ci if (ret) 50662306a36Sopenharmony_ci cs_hsi_control_read_error(hi, msg); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic void cs_hsi_write_on_control_complete(struct hsi_msg *msg) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct cs_hsi_iface *hi = msg->context; 51262306a36Sopenharmony_ci if (msg->status == HSI_STATUS_COMPLETED) { 51362306a36Sopenharmony_ci spin_lock(&hi->lock); 51462306a36Sopenharmony_ci hi->control_state &= ~SSI_CHANNEL_STATE_WRITING; 51562306a36Sopenharmony_ci cs_release_cmd(msg); 51662306a36Sopenharmony_ci spin_unlock(&hi->lock); 51762306a36Sopenharmony_ci } else if (msg->status == HSI_STATUS_ERROR) { 51862306a36Sopenharmony_ci cs_hsi_control_write_error(hi, msg); 51962306a36Sopenharmony_ci } else { 52062306a36Sopenharmony_ci dev_err(&hi->cl->device, 52162306a36Sopenharmony_ci "unexpected status in control write callback %d\n", 52262306a36Sopenharmony_ci msg->status); 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic int cs_hsi_write_on_control(struct cs_hsi_iface *hi, u32 message) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct hsi_msg *msg; 52962306a36Sopenharmony_ci int ret; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci spin_lock(&hi->lock); 53262306a36Sopenharmony_ci if (hi->control_state & SSI_CHANNEL_STATE_ERROR) { 53362306a36Sopenharmony_ci spin_unlock(&hi->lock); 53462306a36Sopenharmony_ci return -EIO; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci if (hi->control_state & SSI_CHANNEL_STATE_WRITING) { 53762306a36Sopenharmony_ci dev_err(&hi->cl->device, 53862306a36Sopenharmony_ci "Write still pending on control channel.\n"); 53962306a36Sopenharmony_ci spin_unlock(&hi->lock); 54062306a36Sopenharmony_ci return -EBUSY; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci hi->control_state |= SSI_CHANNEL_STATE_WRITING; 54362306a36Sopenharmony_ci msg = cs_claim_cmd(hi); 54462306a36Sopenharmony_ci spin_unlock(&hi->lock); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci cs_set_cmd(msg, message); 54762306a36Sopenharmony_ci msg->sgt.nents = 1; 54862306a36Sopenharmony_ci msg->complete = cs_hsi_write_on_control_complete; 54962306a36Sopenharmony_ci dev_dbg(&hi->cl->device, 55062306a36Sopenharmony_ci "Sending control message %08X\n", message); 55162306a36Sopenharmony_ci ret = hsi_async_write(hi->cl, msg); 55262306a36Sopenharmony_ci if (ret) { 55362306a36Sopenharmony_ci dev_err(&hi->cl->device, 55462306a36Sopenharmony_ci "async_write failed with %d\n", ret); 55562306a36Sopenharmony_ci cs_hsi_control_write_error(hi, msg); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* 55962306a36Sopenharmony_ci * Make sure control read is always pending when issuing 56062306a36Sopenharmony_ci * new control writes. This is needed as the controller 56162306a36Sopenharmony_ci * may flush our messages if e.g. the peer device reboots 56262306a36Sopenharmony_ci * unexpectedly (and we cannot directly resubmit a new read from 56362306a36Sopenharmony_ci * the message destructor; see cs_cmd_destructor()). 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_ci if (!(hi->control_state & SSI_CHANNEL_STATE_READING)) { 56662306a36Sopenharmony_ci dev_err(&hi->cl->device, "Restarting control reads\n"); 56762306a36Sopenharmony_ci cs_hsi_read_on_control(hi); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic void cs_hsi_read_on_data_complete(struct hsi_msg *msg) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct cs_hsi_iface *hi = msg->context; 57662306a36Sopenharmony_ci u32 payload; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (unlikely(msg->status == HSI_STATUS_ERROR)) { 57962306a36Sopenharmony_ci cs_hsi_data_read_error(hi, msg); 58062306a36Sopenharmony_ci return; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci spin_lock(&hi->lock); 58462306a36Sopenharmony_ci WARN_ON(!(hi->data_state & SSI_CHANNEL_STATE_READING)); 58562306a36Sopenharmony_ci hi->data_state &= ~SSI_CHANNEL_STATE_READING; 58662306a36Sopenharmony_ci payload = CS_RX_DATA_RECEIVED; 58762306a36Sopenharmony_ci payload |= hi->rx_slot; 58862306a36Sopenharmony_ci hi->rx_slot++; 58962306a36Sopenharmony_ci hi->rx_slot %= hi->rx_ptr_boundary; 59062306a36Sopenharmony_ci /* expose current rx ptr in mmap area */ 59162306a36Sopenharmony_ci hi->mmap_cfg->rx_ptr = hi->rx_slot; 59262306a36Sopenharmony_ci if (unlikely(waitqueue_active(&hi->datawait))) 59362306a36Sopenharmony_ci wake_up_interruptible(&hi->datawait); 59462306a36Sopenharmony_ci spin_unlock(&hi->lock); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci cs_notify_data(payload, hi->rx_bufs); 59762306a36Sopenharmony_ci cs_hsi_read_on_data(hi); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic void cs_hsi_peek_on_data_complete(struct hsi_msg *msg) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct cs_hsi_iface *hi = msg->context; 60362306a36Sopenharmony_ci u32 *address; 60462306a36Sopenharmony_ci int ret; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (unlikely(msg->status == HSI_STATUS_ERROR)) { 60762306a36Sopenharmony_ci cs_hsi_data_read_error(hi, msg); 60862306a36Sopenharmony_ci return; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci if (unlikely(hi->iface_state != CS_STATE_CONFIGURED)) { 61162306a36Sopenharmony_ci dev_err(&hi->cl->device, "Data received in invalid state\n"); 61262306a36Sopenharmony_ci cs_hsi_data_read_error(hi, msg); 61362306a36Sopenharmony_ci return; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci spin_lock(&hi->lock); 61762306a36Sopenharmony_ci WARN_ON(!(hi->data_state & SSI_CHANNEL_STATE_POLL)); 61862306a36Sopenharmony_ci hi->data_state &= ~SSI_CHANNEL_STATE_POLL; 61962306a36Sopenharmony_ci hi->data_state |= SSI_CHANNEL_STATE_READING; 62062306a36Sopenharmony_ci spin_unlock(&hi->lock); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci address = (u32 *)(hi->mmap_base + 62362306a36Sopenharmony_ci hi->rx_offsets[hi->rx_slot % hi->rx_bufs]); 62462306a36Sopenharmony_ci sg_init_one(msg->sgt.sgl, address, hi->buf_size); 62562306a36Sopenharmony_ci msg->sgt.nents = 1; 62662306a36Sopenharmony_ci msg->complete = cs_hsi_read_on_data_complete; 62762306a36Sopenharmony_ci ret = hsi_async_read(hi->cl, msg); 62862306a36Sopenharmony_ci if (ret) 62962306a36Sopenharmony_ci cs_hsi_data_read_error(hi, msg); 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci/* 63362306a36Sopenharmony_ci * Read/write transaction is ongoing. Returns false if in 63462306a36Sopenharmony_ci * SSI_CHANNEL_STATE_POLL state. 63562306a36Sopenharmony_ci */ 63662306a36Sopenharmony_cistatic inline int cs_state_xfer_active(unsigned int state) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci return (state & SSI_CHANNEL_STATE_WRITING) || 63962306a36Sopenharmony_ci (state & SSI_CHANNEL_STATE_READING); 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/* 64362306a36Sopenharmony_ci * No pending read/writes 64462306a36Sopenharmony_ci */ 64562306a36Sopenharmony_cistatic inline int cs_state_idle(unsigned int state) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci return !(state & ~SSI_CHANNEL_STATE_ERROR); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic void cs_hsi_read_on_data(struct cs_hsi_iface *hi) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct hsi_msg *rxmsg; 65362306a36Sopenharmony_ci int ret; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci spin_lock(&hi->lock); 65662306a36Sopenharmony_ci if (hi->data_state & 65762306a36Sopenharmony_ci (SSI_CHANNEL_STATE_READING | SSI_CHANNEL_STATE_POLL)) { 65862306a36Sopenharmony_ci dev_dbg(&hi->cl->device, "Data read already pending (%u)\n", 65962306a36Sopenharmony_ci hi->data_state); 66062306a36Sopenharmony_ci spin_unlock(&hi->lock); 66162306a36Sopenharmony_ci return; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci hi->data_state |= SSI_CHANNEL_STATE_POLL; 66462306a36Sopenharmony_ci spin_unlock(&hi->lock); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci rxmsg = hi->data_rx_msg; 66762306a36Sopenharmony_ci sg_init_one(rxmsg->sgt.sgl, (void *)hi->mmap_base, 0); 66862306a36Sopenharmony_ci rxmsg->sgt.nents = 0; 66962306a36Sopenharmony_ci rxmsg->complete = cs_hsi_peek_on_data_complete; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci ret = hsi_async_read(hi->cl, rxmsg); 67262306a36Sopenharmony_ci if (ret) 67362306a36Sopenharmony_ci cs_hsi_data_read_error(hi, rxmsg); 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic void cs_hsi_write_on_data_complete(struct hsi_msg *msg) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct cs_hsi_iface *hi = msg->context; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (msg->status == HSI_STATUS_COMPLETED) { 68162306a36Sopenharmony_ci spin_lock(&hi->lock); 68262306a36Sopenharmony_ci hi->data_state &= ~SSI_CHANNEL_STATE_WRITING; 68362306a36Sopenharmony_ci if (unlikely(waitqueue_active(&hi->datawait))) 68462306a36Sopenharmony_ci wake_up_interruptible(&hi->datawait); 68562306a36Sopenharmony_ci spin_unlock(&hi->lock); 68662306a36Sopenharmony_ci } else { 68762306a36Sopenharmony_ci cs_hsi_data_write_error(hi, msg); 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic int cs_hsi_write_on_data(struct cs_hsi_iface *hi, unsigned int slot) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci u32 *address; 69462306a36Sopenharmony_ci struct hsi_msg *txmsg; 69562306a36Sopenharmony_ci int ret; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci spin_lock(&hi->lock); 69862306a36Sopenharmony_ci if (hi->iface_state != CS_STATE_CONFIGURED) { 69962306a36Sopenharmony_ci dev_err(&hi->cl->device, "Not configured, aborting\n"); 70062306a36Sopenharmony_ci ret = -EINVAL; 70162306a36Sopenharmony_ci goto error; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci if (hi->data_state & SSI_CHANNEL_STATE_ERROR) { 70462306a36Sopenharmony_ci dev_err(&hi->cl->device, "HSI error, aborting\n"); 70562306a36Sopenharmony_ci ret = -EIO; 70662306a36Sopenharmony_ci goto error; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci if (hi->data_state & SSI_CHANNEL_STATE_WRITING) { 70962306a36Sopenharmony_ci dev_err(&hi->cl->device, "Write pending on data channel.\n"); 71062306a36Sopenharmony_ci ret = -EBUSY; 71162306a36Sopenharmony_ci goto error; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci hi->data_state |= SSI_CHANNEL_STATE_WRITING; 71462306a36Sopenharmony_ci spin_unlock(&hi->lock); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci hi->tx_slot = slot; 71762306a36Sopenharmony_ci address = (u32 *)(hi->mmap_base + hi->tx_offsets[hi->tx_slot]); 71862306a36Sopenharmony_ci txmsg = hi->data_tx_msg; 71962306a36Sopenharmony_ci sg_init_one(txmsg->sgt.sgl, address, hi->buf_size); 72062306a36Sopenharmony_ci txmsg->complete = cs_hsi_write_on_data_complete; 72162306a36Sopenharmony_ci ret = hsi_async_write(hi->cl, txmsg); 72262306a36Sopenharmony_ci if (ret) 72362306a36Sopenharmony_ci cs_hsi_data_write_error(hi, txmsg); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return ret; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cierror: 72862306a36Sopenharmony_ci spin_unlock(&hi->lock); 72962306a36Sopenharmony_ci if (ret == -EIO) 73062306a36Sopenharmony_ci cs_hsi_data_write_error(hi, hi->data_tx_msg); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci return ret; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic unsigned int cs_hsi_get_state(struct cs_hsi_iface *hi) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci return hi->iface_state; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic int cs_hsi_command(struct cs_hsi_iface *hi, u32 cmd) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci int ret = 0; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci local_bh_disable(); 74562306a36Sopenharmony_ci switch (cmd & TARGET_MASK) { 74662306a36Sopenharmony_ci case TARGET_REMOTE: 74762306a36Sopenharmony_ci ret = cs_hsi_write_on_control(hi, cmd); 74862306a36Sopenharmony_ci break; 74962306a36Sopenharmony_ci case TARGET_LOCAL: 75062306a36Sopenharmony_ci if ((cmd & CS_CMD_MASK) == CS_TX_DATA_READY) 75162306a36Sopenharmony_ci ret = cs_hsi_write_on_data(hi, cmd & CS_PARAM_MASK); 75262306a36Sopenharmony_ci else 75362306a36Sopenharmony_ci ret = -EINVAL; 75462306a36Sopenharmony_ci break; 75562306a36Sopenharmony_ci default: 75662306a36Sopenharmony_ci ret = -EINVAL; 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci local_bh_enable(); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci return ret; 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cistatic void cs_hsi_set_wakeline(struct cs_hsi_iface *hi, bool new_state) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci int change = 0; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci spin_lock_bh(&hi->lock); 76962306a36Sopenharmony_ci if (hi->wakeline_state != new_state) { 77062306a36Sopenharmony_ci hi->wakeline_state = new_state; 77162306a36Sopenharmony_ci change = 1; 77262306a36Sopenharmony_ci dev_dbg(&hi->cl->device, "setting wake line to %d (%p)\n", 77362306a36Sopenharmony_ci new_state, hi->cl); 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci spin_unlock_bh(&hi->lock); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (change) { 77862306a36Sopenharmony_ci if (new_state) 77962306a36Sopenharmony_ci ssip_slave_start_tx(hi->master); 78062306a36Sopenharmony_ci else 78162306a36Sopenharmony_ci ssip_slave_stop_tx(hi->master); 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci dev_dbg(&hi->cl->device, "wake line set to %d (%p)\n", 78562306a36Sopenharmony_ci new_state, hi->cl); 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic void set_buffer_sizes(struct cs_hsi_iface *hi, int rx_bufs, int tx_bufs) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci hi->rx_bufs = rx_bufs; 79162306a36Sopenharmony_ci hi->tx_bufs = tx_bufs; 79262306a36Sopenharmony_ci hi->mmap_cfg->rx_bufs = rx_bufs; 79362306a36Sopenharmony_ci hi->mmap_cfg->tx_bufs = tx_bufs; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (hi->flags & CS_FEAT_ROLLING_RX_COUNTER) { 79662306a36Sopenharmony_ci /* 79762306a36Sopenharmony_ci * For more robust overrun detection, let the rx 79862306a36Sopenharmony_ci * pointer run in range 0..'boundary-1'. Boundary 79962306a36Sopenharmony_ci * is a multiple of rx_bufs, and limited in max size 80062306a36Sopenharmony_ci * by RX_PTR_MAX_SHIFT to allow for fast ptr-diff 80162306a36Sopenharmony_ci * calculation. 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_ci hi->rx_ptr_boundary = (rx_bufs << RX_PTR_BOUNDARY_SHIFT); 80462306a36Sopenharmony_ci hi->mmap_cfg->rx_ptr_boundary = hi->rx_ptr_boundary; 80562306a36Sopenharmony_ci } else { 80662306a36Sopenharmony_ci hi->rx_ptr_boundary = hi->rx_bufs; 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic int check_buf_params(struct cs_hsi_iface *hi, 81162306a36Sopenharmony_ci const struct cs_buffer_config *buf_cfg) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci size_t buf_size_aligned = L1_CACHE_ALIGN(buf_cfg->buf_size) * 81462306a36Sopenharmony_ci (buf_cfg->rx_bufs + buf_cfg->tx_bufs); 81562306a36Sopenharmony_ci size_t ctrl_size_aligned = L1_CACHE_ALIGN(sizeof(*hi->mmap_cfg)); 81662306a36Sopenharmony_ci int r = 0; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (buf_cfg->rx_bufs > CS_MAX_BUFFERS || 81962306a36Sopenharmony_ci buf_cfg->tx_bufs > CS_MAX_BUFFERS) { 82062306a36Sopenharmony_ci r = -EINVAL; 82162306a36Sopenharmony_ci } else if ((buf_size_aligned + ctrl_size_aligned) >= hi->mmap_size) { 82262306a36Sopenharmony_ci dev_err(&hi->cl->device, "No space for the requested buffer " 82362306a36Sopenharmony_ci "configuration\n"); 82462306a36Sopenharmony_ci r = -ENOBUFS; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci return r; 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci/* 83162306a36Sopenharmony_ci * Block until pending data transfers have completed. 83262306a36Sopenharmony_ci */ 83362306a36Sopenharmony_cistatic int cs_hsi_data_sync(struct cs_hsi_iface *hi) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci int r = 0; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci spin_lock_bh(&hi->lock); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (!cs_state_xfer_active(hi->data_state)) { 84062306a36Sopenharmony_ci dev_dbg(&hi->cl->device, "hsi_data_sync break, idle\n"); 84162306a36Sopenharmony_ci goto out; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci for (;;) { 84562306a36Sopenharmony_ci int s; 84662306a36Sopenharmony_ci DEFINE_WAIT(wait); 84762306a36Sopenharmony_ci if (!cs_state_xfer_active(hi->data_state)) 84862306a36Sopenharmony_ci goto out; 84962306a36Sopenharmony_ci if (signal_pending(current)) { 85062306a36Sopenharmony_ci r = -ERESTARTSYS; 85162306a36Sopenharmony_ci goto out; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci /* 85462306a36Sopenharmony_ci * prepare_to_wait must be called with hi->lock held 85562306a36Sopenharmony_ci * so that callbacks can check for waitqueue_active() 85662306a36Sopenharmony_ci */ 85762306a36Sopenharmony_ci prepare_to_wait(&hi->datawait, &wait, TASK_INTERRUPTIBLE); 85862306a36Sopenharmony_ci spin_unlock_bh(&hi->lock); 85962306a36Sopenharmony_ci s = schedule_timeout( 86062306a36Sopenharmony_ci msecs_to_jiffies(CS_HSI_TRANSFER_TIMEOUT_MS)); 86162306a36Sopenharmony_ci spin_lock_bh(&hi->lock); 86262306a36Sopenharmony_ci finish_wait(&hi->datawait, &wait); 86362306a36Sopenharmony_ci if (!s) { 86462306a36Sopenharmony_ci dev_dbg(&hi->cl->device, 86562306a36Sopenharmony_ci "hsi_data_sync timeout after %d ms\n", 86662306a36Sopenharmony_ci CS_HSI_TRANSFER_TIMEOUT_MS); 86762306a36Sopenharmony_ci r = -EIO; 86862306a36Sopenharmony_ci goto out; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ciout: 87362306a36Sopenharmony_ci spin_unlock_bh(&hi->lock); 87462306a36Sopenharmony_ci dev_dbg(&hi->cl->device, "hsi_data_sync done with res %d\n", r); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci return r; 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic void cs_hsi_data_enable(struct cs_hsi_iface *hi, 88062306a36Sopenharmony_ci struct cs_buffer_config *buf_cfg) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci unsigned int data_start, i; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci BUG_ON(hi->buf_size == 0); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci set_buffer_sizes(hi, buf_cfg->rx_bufs, buf_cfg->tx_bufs); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci hi->slot_size = L1_CACHE_ALIGN(hi->buf_size); 88962306a36Sopenharmony_ci dev_dbg(&hi->cl->device, 89062306a36Sopenharmony_ci "setting slot size to %u, buf size %u, align %u\n", 89162306a36Sopenharmony_ci hi->slot_size, hi->buf_size, L1_CACHE_BYTES); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci data_start = L1_CACHE_ALIGN(sizeof(*hi->mmap_cfg)); 89462306a36Sopenharmony_ci dev_dbg(&hi->cl->device, 89562306a36Sopenharmony_ci "setting data start at %u, cfg block %u, align %u\n", 89662306a36Sopenharmony_ci data_start, sizeof(*hi->mmap_cfg), L1_CACHE_BYTES); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci for (i = 0; i < hi->mmap_cfg->rx_bufs; i++) { 89962306a36Sopenharmony_ci hi->rx_offsets[i] = data_start + i * hi->slot_size; 90062306a36Sopenharmony_ci hi->mmap_cfg->rx_offsets[i] = hi->rx_offsets[i]; 90162306a36Sopenharmony_ci dev_dbg(&hi->cl->device, "DL buf #%u at %u\n", 90262306a36Sopenharmony_ci i, hi->rx_offsets[i]); 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci for (i = 0; i < hi->mmap_cfg->tx_bufs; i++) { 90562306a36Sopenharmony_ci hi->tx_offsets[i] = data_start + 90662306a36Sopenharmony_ci (i + hi->mmap_cfg->rx_bufs) * hi->slot_size; 90762306a36Sopenharmony_ci hi->mmap_cfg->tx_offsets[i] = hi->tx_offsets[i]; 90862306a36Sopenharmony_ci dev_dbg(&hi->cl->device, "UL buf #%u at %u\n", 90962306a36Sopenharmony_ci i, hi->rx_offsets[i]); 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci hi->iface_state = CS_STATE_CONFIGURED; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cistatic void cs_hsi_data_disable(struct cs_hsi_iface *hi, int old_state) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci if (old_state == CS_STATE_CONFIGURED) { 91862306a36Sopenharmony_ci dev_dbg(&hi->cl->device, 91962306a36Sopenharmony_ci "closing data channel with slot size 0\n"); 92062306a36Sopenharmony_ci hi->iface_state = CS_STATE_OPENED; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic int cs_hsi_buf_config(struct cs_hsi_iface *hi, 92562306a36Sopenharmony_ci struct cs_buffer_config *buf_cfg) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci int r = 0; 92862306a36Sopenharmony_ci unsigned int old_state = hi->iface_state; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci spin_lock_bh(&hi->lock); 93162306a36Sopenharmony_ci /* Prevent new transactions during buffer reconfig */ 93262306a36Sopenharmony_ci if (old_state == CS_STATE_CONFIGURED) 93362306a36Sopenharmony_ci hi->iface_state = CS_STATE_OPENED; 93462306a36Sopenharmony_ci spin_unlock_bh(&hi->lock); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci /* 93762306a36Sopenharmony_ci * make sure that no non-zero data reads are ongoing before 93862306a36Sopenharmony_ci * proceeding to change the buffer layout 93962306a36Sopenharmony_ci */ 94062306a36Sopenharmony_ci r = cs_hsi_data_sync(hi); 94162306a36Sopenharmony_ci if (r < 0) 94262306a36Sopenharmony_ci return r; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci WARN_ON(cs_state_xfer_active(hi->data_state)); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci spin_lock_bh(&hi->lock); 94762306a36Sopenharmony_ci r = check_buf_params(hi, buf_cfg); 94862306a36Sopenharmony_ci if (r < 0) 94962306a36Sopenharmony_ci goto error; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci hi->buf_size = buf_cfg->buf_size; 95262306a36Sopenharmony_ci hi->mmap_cfg->buf_size = hi->buf_size; 95362306a36Sopenharmony_ci hi->flags = buf_cfg->flags; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci hi->rx_slot = 0; 95662306a36Sopenharmony_ci hi->tx_slot = 0; 95762306a36Sopenharmony_ci hi->slot_size = 0; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci if (hi->buf_size) 96062306a36Sopenharmony_ci cs_hsi_data_enable(hi, buf_cfg); 96162306a36Sopenharmony_ci else 96262306a36Sopenharmony_ci cs_hsi_data_disable(hi, old_state); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci spin_unlock_bh(&hi->lock); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (old_state != hi->iface_state) { 96762306a36Sopenharmony_ci if (hi->iface_state == CS_STATE_CONFIGURED) { 96862306a36Sopenharmony_ci cpu_latency_qos_add_request(&hi->pm_qos_req, 96962306a36Sopenharmony_ci CS_QOS_LATENCY_FOR_DATA_USEC); 97062306a36Sopenharmony_ci local_bh_disable(); 97162306a36Sopenharmony_ci cs_hsi_read_on_data(hi); 97262306a36Sopenharmony_ci local_bh_enable(); 97362306a36Sopenharmony_ci } else if (old_state == CS_STATE_CONFIGURED) { 97462306a36Sopenharmony_ci cpu_latency_qos_remove_request(&hi->pm_qos_req); 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci return r; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_cierror: 98062306a36Sopenharmony_ci spin_unlock_bh(&hi->lock); 98162306a36Sopenharmony_ci return r; 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic int cs_hsi_start(struct cs_hsi_iface **hi, struct hsi_client *cl, 98562306a36Sopenharmony_ci unsigned long mmap_base, unsigned long mmap_size) 98662306a36Sopenharmony_ci{ 98762306a36Sopenharmony_ci int err = 0; 98862306a36Sopenharmony_ci struct cs_hsi_iface *hsi_if = kzalloc(sizeof(*hsi_if), GFP_KERNEL); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci dev_dbg(&cl->device, "cs_hsi_start\n"); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (!hsi_if) { 99362306a36Sopenharmony_ci err = -ENOMEM; 99462306a36Sopenharmony_ci goto leave0; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci spin_lock_init(&hsi_if->lock); 99762306a36Sopenharmony_ci hsi_if->cl = cl; 99862306a36Sopenharmony_ci hsi_if->iface_state = CS_STATE_CLOSED; 99962306a36Sopenharmony_ci hsi_if->mmap_cfg = (struct cs_mmap_config_block *)mmap_base; 100062306a36Sopenharmony_ci hsi_if->mmap_base = mmap_base; 100162306a36Sopenharmony_ci hsi_if->mmap_size = mmap_size; 100262306a36Sopenharmony_ci memset(hsi_if->mmap_cfg, 0, sizeof(*hsi_if->mmap_cfg)); 100362306a36Sopenharmony_ci init_waitqueue_head(&hsi_if->datawait); 100462306a36Sopenharmony_ci err = cs_alloc_cmds(hsi_if); 100562306a36Sopenharmony_ci if (err < 0) { 100662306a36Sopenharmony_ci dev_err(&cl->device, "Unable to alloc HSI messages\n"); 100762306a36Sopenharmony_ci goto leave1; 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci err = cs_hsi_alloc_data(hsi_if); 101062306a36Sopenharmony_ci if (err < 0) { 101162306a36Sopenharmony_ci dev_err(&cl->device, "Unable to alloc HSI messages for data\n"); 101262306a36Sopenharmony_ci goto leave2; 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci err = hsi_claim_port(cl, 1); 101562306a36Sopenharmony_ci if (err < 0) { 101662306a36Sopenharmony_ci dev_err(&cl->device, 101762306a36Sopenharmony_ci "Could not open, HSI port already claimed\n"); 101862306a36Sopenharmony_ci goto leave3; 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci hsi_if->master = ssip_slave_get_master(cl); 102162306a36Sopenharmony_ci if (IS_ERR(hsi_if->master)) { 102262306a36Sopenharmony_ci err = PTR_ERR(hsi_if->master); 102362306a36Sopenharmony_ci dev_err(&cl->device, "Could not get HSI master client\n"); 102462306a36Sopenharmony_ci goto leave4; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci if (!ssip_slave_running(hsi_if->master)) { 102762306a36Sopenharmony_ci err = -ENODEV; 102862306a36Sopenharmony_ci dev_err(&cl->device, 102962306a36Sopenharmony_ci "HSI port not initialized\n"); 103062306a36Sopenharmony_ci goto leave4; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci hsi_if->iface_state = CS_STATE_OPENED; 103462306a36Sopenharmony_ci local_bh_disable(); 103562306a36Sopenharmony_ci cs_hsi_read_on_control(hsi_if); 103662306a36Sopenharmony_ci local_bh_enable(); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci dev_dbg(&cl->device, "cs_hsi_start...done\n"); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci BUG_ON(!hi); 104162306a36Sopenharmony_ci *hi = hsi_if; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci return 0; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cileave4: 104662306a36Sopenharmony_ci hsi_release_port(cl); 104762306a36Sopenharmony_cileave3: 104862306a36Sopenharmony_ci cs_hsi_free_data(hsi_if); 104962306a36Sopenharmony_cileave2: 105062306a36Sopenharmony_ci cs_free_cmds(hsi_if); 105162306a36Sopenharmony_cileave1: 105262306a36Sopenharmony_ci kfree(hsi_if); 105362306a36Sopenharmony_cileave0: 105462306a36Sopenharmony_ci dev_dbg(&cl->device, "cs_hsi_start...done/error\n\n"); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci return err; 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic void cs_hsi_stop(struct cs_hsi_iface *hi) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci dev_dbg(&hi->cl->device, "cs_hsi_stop\n"); 106262306a36Sopenharmony_ci cs_hsi_set_wakeline(hi, 0); 106362306a36Sopenharmony_ci ssip_slave_put_master(hi->master); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci /* hsi_release_port() needs to be called with CS_STATE_CLOSED */ 106662306a36Sopenharmony_ci hi->iface_state = CS_STATE_CLOSED; 106762306a36Sopenharmony_ci hsi_release_port(hi->cl); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* 107062306a36Sopenharmony_ci * hsi_release_port() should flush out all the pending 107162306a36Sopenharmony_ci * messages, so cs_state_idle() should be true for both 107262306a36Sopenharmony_ci * control and data channels. 107362306a36Sopenharmony_ci */ 107462306a36Sopenharmony_ci WARN_ON(!cs_state_idle(hi->control_state)); 107562306a36Sopenharmony_ci WARN_ON(!cs_state_idle(hi->data_state)); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci if (cpu_latency_qos_request_active(&hi->pm_qos_req)) 107862306a36Sopenharmony_ci cpu_latency_qos_remove_request(&hi->pm_qos_req); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci spin_lock_bh(&hi->lock); 108162306a36Sopenharmony_ci cs_hsi_free_data(hi); 108262306a36Sopenharmony_ci cs_free_cmds(hi); 108362306a36Sopenharmony_ci spin_unlock_bh(&hi->lock); 108462306a36Sopenharmony_ci kfree(hi); 108562306a36Sopenharmony_ci} 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_cistatic vm_fault_t cs_char_vma_fault(struct vm_fault *vmf) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci struct cs_char *csdata = vmf->vma->vm_private_data; 109062306a36Sopenharmony_ci struct page *page; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci page = virt_to_page((void *)csdata->mmap_base); 109362306a36Sopenharmony_ci get_page(page); 109462306a36Sopenharmony_ci vmf->page = page; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci return 0; 109762306a36Sopenharmony_ci} 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_cistatic const struct vm_operations_struct cs_char_vm_ops = { 110062306a36Sopenharmony_ci .fault = cs_char_vma_fault, 110162306a36Sopenharmony_ci}; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_cistatic int cs_char_fasync(int fd, struct file *file, int on) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci struct cs_char *csdata = file->private_data; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci if (fasync_helper(fd, file, on, &csdata->async_queue) < 0) 110862306a36Sopenharmony_ci return -EIO; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci return 0; 111162306a36Sopenharmony_ci} 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_cistatic __poll_t cs_char_poll(struct file *file, poll_table *wait) 111462306a36Sopenharmony_ci{ 111562306a36Sopenharmony_ci struct cs_char *csdata = file->private_data; 111662306a36Sopenharmony_ci __poll_t ret = 0; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci poll_wait(file, &cs_char_data.wait, wait); 111962306a36Sopenharmony_ci spin_lock_bh(&csdata->lock); 112062306a36Sopenharmony_ci if (!list_empty(&csdata->chardev_queue)) 112162306a36Sopenharmony_ci ret = EPOLLIN | EPOLLRDNORM; 112262306a36Sopenharmony_ci else if (!list_empty(&csdata->dataind_queue)) 112362306a36Sopenharmony_ci ret = EPOLLIN | EPOLLRDNORM; 112462306a36Sopenharmony_ci spin_unlock_bh(&csdata->lock); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci return ret; 112762306a36Sopenharmony_ci} 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_cistatic ssize_t cs_char_read(struct file *file, char __user *buf, size_t count, 113062306a36Sopenharmony_ci loff_t *unused) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci struct cs_char *csdata = file->private_data; 113362306a36Sopenharmony_ci u32 data; 113462306a36Sopenharmony_ci ssize_t retval; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci if (count < sizeof(data)) 113762306a36Sopenharmony_ci return -EINVAL; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci for (;;) { 114062306a36Sopenharmony_ci DEFINE_WAIT(wait); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci spin_lock_bh(&csdata->lock); 114362306a36Sopenharmony_ci if (!list_empty(&csdata->chardev_queue)) { 114462306a36Sopenharmony_ci data = cs_pop_entry(&csdata->chardev_queue); 114562306a36Sopenharmony_ci } else if (!list_empty(&csdata->dataind_queue)) { 114662306a36Sopenharmony_ci data = cs_pop_entry(&csdata->dataind_queue); 114762306a36Sopenharmony_ci csdata->dataind_pending--; 114862306a36Sopenharmony_ci } else { 114962306a36Sopenharmony_ci data = 0; 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci spin_unlock_bh(&csdata->lock); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (data) 115462306a36Sopenharmony_ci break; 115562306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 115662306a36Sopenharmony_ci retval = -EAGAIN; 115762306a36Sopenharmony_ci goto out; 115862306a36Sopenharmony_ci } else if (signal_pending(current)) { 115962306a36Sopenharmony_ci retval = -ERESTARTSYS; 116062306a36Sopenharmony_ci goto out; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci prepare_to_wait_exclusive(&csdata->wait, &wait, 116362306a36Sopenharmony_ci TASK_INTERRUPTIBLE); 116462306a36Sopenharmony_ci schedule(); 116562306a36Sopenharmony_ci finish_wait(&csdata->wait, &wait); 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci retval = put_user(data, (u32 __user *)buf); 116962306a36Sopenharmony_ci if (!retval) 117062306a36Sopenharmony_ci retval = sizeof(data); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ciout: 117362306a36Sopenharmony_ci return retval; 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic ssize_t cs_char_write(struct file *file, const char __user *buf, 117762306a36Sopenharmony_ci size_t count, loff_t *unused) 117862306a36Sopenharmony_ci{ 117962306a36Sopenharmony_ci struct cs_char *csdata = file->private_data; 118062306a36Sopenharmony_ci u32 data; 118162306a36Sopenharmony_ci int err; 118262306a36Sopenharmony_ci ssize_t retval; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci if (count < sizeof(data)) 118562306a36Sopenharmony_ci return -EINVAL; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci if (get_user(data, (u32 __user *)buf)) 118862306a36Sopenharmony_ci retval = -EFAULT; 118962306a36Sopenharmony_ci else 119062306a36Sopenharmony_ci retval = count; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci err = cs_hsi_command(csdata->hi, data); 119362306a36Sopenharmony_ci if (err < 0) 119462306a36Sopenharmony_ci retval = err; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci return retval; 119762306a36Sopenharmony_ci} 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_cistatic long cs_char_ioctl(struct file *file, unsigned int cmd, 120062306a36Sopenharmony_ci unsigned long arg) 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci struct cs_char *csdata = file->private_data; 120362306a36Sopenharmony_ci int r = 0; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci switch (cmd) { 120662306a36Sopenharmony_ci case CS_GET_STATE: { 120762306a36Sopenharmony_ci unsigned int state; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci state = cs_hsi_get_state(csdata->hi); 121062306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, &state, sizeof(state))) 121162306a36Sopenharmony_ci r = -EFAULT; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci break; 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci case CS_SET_WAKELINE: { 121662306a36Sopenharmony_ci unsigned int state; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (copy_from_user(&state, (void __user *)arg, sizeof(state))) { 121962306a36Sopenharmony_ci r = -EFAULT; 122062306a36Sopenharmony_ci break; 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci if (state > 1) { 122462306a36Sopenharmony_ci r = -EINVAL; 122562306a36Sopenharmony_ci break; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci cs_hsi_set_wakeline(csdata->hi, !!state); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci break; 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci case CS_GET_IF_VERSION: { 123362306a36Sopenharmony_ci unsigned int ifver = CS_IF_VERSION; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, &ifver, sizeof(ifver))) 123662306a36Sopenharmony_ci r = -EFAULT; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci break; 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci case CS_CONFIG_BUFS: { 124162306a36Sopenharmony_ci struct cs_buffer_config buf_cfg; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (copy_from_user(&buf_cfg, (void __user *)arg, 124462306a36Sopenharmony_ci sizeof(buf_cfg))) 124562306a36Sopenharmony_ci r = -EFAULT; 124662306a36Sopenharmony_ci else 124762306a36Sopenharmony_ci r = cs_hsi_buf_config(csdata->hi, &buf_cfg); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci break; 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci default: 125262306a36Sopenharmony_ci r = -ENOTTY; 125362306a36Sopenharmony_ci break; 125462306a36Sopenharmony_ci } 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci return r; 125762306a36Sopenharmony_ci} 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_cistatic int cs_char_mmap(struct file *file, struct vm_area_struct *vma) 126062306a36Sopenharmony_ci{ 126162306a36Sopenharmony_ci if (vma->vm_end < vma->vm_start) 126262306a36Sopenharmony_ci return -EINVAL; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci if (vma_pages(vma) != 1) 126562306a36Sopenharmony_ci return -EINVAL; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci vm_flags_set(vma, VM_IO | VM_DONTDUMP | VM_DONTEXPAND); 126862306a36Sopenharmony_ci vma->vm_ops = &cs_char_vm_ops; 126962306a36Sopenharmony_ci vma->vm_private_data = file->private_data; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci return 0; 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic int cs_char_open(struct inode *unused, struct file *file) 127562306a36Sopenharmony_ci{ 127662306a36Sopenharmony_ci int ret = 0; 127762306a36Sopenharmony_ci unsigned long p; 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci spin_lock_bh(&cs_char_data.lock); 128062306a36Sopenharmony_ci if (cs_char_data.opened) { 128162306a36Sopenharmony_ci ret = -EBUSY; 128262306a36Sopenharmony_ci spin_unlock_bh(&cs_char_data.lock); 128362306a36Sopenharmony_ci goto out1; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci cs_char_data.opened = 1; 128662306a36Sopenharmony_ci cs_char_data.dataind_pending = 0; 128762306a36Sopenharmony_ci spin_unlock_bh(&cs_char_data.lock); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci p = get_zeroed_page(GFP_KERNEL); 129062306a36Sopenharmony_ci if (!p) { 129162306a36Sopenharmony_ci ret = -ENOMEM; 129262306a36Sopenharmony_ci goto out2; 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci ret = cs_hsi_start(&cs_char_data.hi, cs_char_data.cl, p, CS_MMAP_SIZE); 129662306a36Sopenharmony_ci if (ret) { 129762306a36Sopenharmony_ci dev_err(&cs_char_data.cl->device, "Unable to initialize HSI\n"); 129862306a36Sopenharmony_ci goto out3; 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci /* these are only used in release so lock not needed */ 130262306a36Sopenharmony_ci cs_char_data.mmap_base = p; 130362306a36Sopenharmony_ci cs_char_data.mmap_size = CS_MMAP_SIZE; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci file->private_data = &cs_char_data; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci return 0; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ciout3: 131062306a36Sopenharmony_ci free_page(p); 131162306a36Sopenharmony_ciout2: 131262306a36Sopenharmony_ci spin_lock_bh(&cs_char_data.lock); 131362306a36Sopenharmony_ci cs_char_data.opened = 0; 131462306a36Sopenharmony_ci spin_unlock_bh(&cs_char_data.lock); 131562306a36Sopenharmony_ciout1: 131662306a36Sopenharmony_ci return ret; 131762306a36Sopenharmony_ci} 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_cistatic void cs_free_char_queue(struct list_head *head) 132062306a36Sopenharmony_ci{ 132162306a36Sopenharmony_ci struct char_queue *entry; 132262306a36Sopenharmony_ci struct list_head *cursor, *next; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci if (!list_empty(head)) { 132562306a36Sopenharmony_ci list_for_each_safe(cursor, next, head) { 132662306a36Sopenharmony_ci entry = list_entry(cursor, struct char_queue, list); 132762306a36Sopenharmony_ci list_del(&entry->list); 132862306a36Sopenharmony_ci kfree(entry); 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci } 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci} 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_cistatic int cs_char_release(struct inode *unused, struct file *file) 133562306a36Sopenharmony_ci{ 133662306a36Sopenharmony_ci struct cs_char *csdata = file->private_data; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci cs_hsi_stop(csdata->hi); 133962306a36Sopenharmony_ci spin_lock_bh(&csdata->lock); 134062306a36Sopenharmony_ci csdata->hi = NULL; 134162306a36Sopenharmony_ci free_page(csdata->mmap_base); 134262306a36Sopenharmony_ci cs_free_char_queue(&csdata->chardev_queue); 134362306a36Sopenharmony_ci cs_free_char_queue(&csdata->dataind_queue); 134462306a36Sopenharmony_ci csdata->opened = 0; 134562306a36Sopenharmony_ci spin_unlock_bh(&csdata->lock); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci return 0; 134862306a36Sopenharmony_ci} 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_cistatic const struct file_operations cs_char_fops = { 135162306a36Sopenharmony_ci .owner = THIS_MODULE, 135262306a36Sopenharmony_ci .read = cs_char_read, 135362306a36Sopenharmony_ci .write = cs_char_write, 135462306a36Sopenharmony_ci .poll = cs_char_poll, 135562306a36Sopenharmony_ci .unlocked_ioctl = cs_char_ioctl, 135662306a36Sopenharmony_ci .mmap = cs_char_mmap, 135762306a36Sopenharmony_ci .open = cs_char_open, 135862306a36Sopenharmony_ci .release = cs_char_release, 135962306a36Sopenharmony_ci .fasync = cs_char_fasync, 136062306a36Sopenharmony_ci}; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_cistatic struct miscdevice cs_char_miscdev = { 136362306a36Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 136462306a36Sopenharmony_ci .name = "cmt_speech", 136562306a36Sopenharmony_ci .fops = &cs_char_fops 136662306a36Sopenharmony_ci}; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_cistatic int cs_hsi_client_probe(struct device *dev) 136962306a36Sopenharmony_ci{ 137062306a36Sopenharmony_ci int err = 0; 137162306a36Sopenharmony_ci struct hsi_client *cl = to_hsi_client(dev); 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci dev_dbg(dev, "hsi_client_probe\n"); 137462306a36Sopenharmony_ci init_waitqueue_head(&cs_char_data.wait); 137562306a36Sopenharmony_ci spin_lock_init(&cs_char_data.lock); 137662306a36Sopenharmony_ci cs_char_data.opened = 0; 137762306a36Sopenharmony_ci cs_char_data.cl = cl; 137862306a36Sopenharmony_ci cs_char_data.hi = NULL; 137962306a36Sopenharmony_ci INIT_LIST_HEAD(&cs_char_data.chardev_queue); 138062306a36Sopenharmony_ci INIT_LIST_HEAD(&cs_char_data.dataind_queue); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci cs_char_data.channel_id_cmd = hsi_get_channel_id_by_name(cl, 138362306a36Sopenharmony_ci "speech-control"); 138462306a36Sopenharmony_ci if (cs_char_data.channel_id_cmd < 0) { 138562306a36Sopenharmony_ci err = cs_char_data.channel_id_cmd; 138662306a36Sopenharmony_ci dev_err(dev, "Could not get cmd channel (%d)\n", err); 138762306a36Sopenharmony_ci return err; 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci cs_char_data.channel_id_data = hsi_get_channel_id_by_name(cl, 139162306a36Sopenharmony_ci "speech-data"); 139262306a36Sopenharmony_ci if (cs_char_data.channel_id_data < 0) { 139362306a36Sopenharmony_ci err = cs_char_data.channel_id_data; 139462306a36Sopenharmony_ci dev_err(dev, "Could not get data channel (%d)\n", err); 139562306a36Sopenharmony_ci return err; 139662306a36Sopenharmony_ci } 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci err = misc_register(&cs_char_miscdev); 139962306a36Sopenharmony_ci if (err) 140062306a36Sopenharmony_ci dev_err(dev, "Failed to register: %d\n", err); 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci return err; 140362306a36Sopenharmony_ci} 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_cistatic int cs_hsi_client_remove(struct device *dev) 140662306a36Sopenharmony_ci{ 140762306a36Sopenharmony_ci struct cs_hsi_iface *hi; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci dev_dbg(dev, "hsi_client_remove\n"); 141062306a36Sopenharmony_ci misc_deregister(&cs_char_miscdev); 141162306a36Sopenharmony_ci spin_lock_bh(&cs_char_data.lock); 141262306a36Sopenharmony_ci hi = cs_char_data.hi; 141362306a36Sopenharmony_ci cs_char_data.hi = NULL; 141462306a36Sopenharmony_ci spin_unlock_bh(&cs_char_data.lock); 141562306a36Sopenharmony_ci if (hi) 141662306a36Sopenharmony_ci cs_hsi_stop(hi); 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci return 0; 141962306a36Sopenharmony_ci} 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_cistatic struct hsi_client_driver cs_hsi_driver = { 142262306a36Sopenharmony_ci .driver = { 142362306a36Sopenharmony_ci .name = "cmt-speech", 142462306a36Sopenharmony_ci .owner = THIS_MODULE, 142562306a36Sopenharmony_ci .probe = cs_hsi_client_probe, 142662306a36Sopenharmony_ci .remove = cs_hsi_client_remove, 142762306a36Sopenharmony_ci }, 142862306a36Sopenharmony_ci}; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_cistatic int __init cs_char_init(void) 143162306a36Sopenharmony_ci{ 143262306a36Sopenharmony_ci pr_info("CMT speech driver added\n"); 143362306a36Sopenharmony_ci return hsi_register_client_driver(&cs_hsi_driver); 143462306a36Sopenharmony_ci} 143562306a36Sopenharmony_cimodule_init(cs_char_init); 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_cistatic void __exit cs_char_exit(void) 143862306a36Sopenharmony_ci{ 143962306a36Sopenharmony_ci hsi_unregister_client_driver(&cs_hsi_driver); 144062306a36Sopenharmony_ci pr_info("CMT speech driver removed\n"); 144162306a36Sopenharmony_ci} 144262306a36Sopenharmony_cimodule_exit(cs_char_exit); 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ciMODULE_ALIAS("hsi:cmt-speech"); 144562306a36Sopenharmony_ciMODULE_AUTHOR("Kai Vehmanen <kai.vehmanen@nokia.com>"); 144662306a36Sopenharmony_ciMODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>"); 144762306a36Sopenharmony_ciMODULE_DESCRIPTION("CMT speech driver"); 144862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1449