162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * core.c - Implementation of core module of MOST Linux driver stack 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013-2020 Microchip Technology Germany II GmbH & Co. KG 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/list.h> 1462306a36Sopenharmony_ci#include <linux/poll.h> 1562306a36Sopenharmony_ci#include <linux/wait.h> 1662306a36Sopenharmony_ci#include <linux/kobject.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/completion.h> 1962306a36Sopenharmony_ci#include <linux/sysfs.h> 2062306a36Sopenharmony_ci#include <linux/kthread.h> 2162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2262306a36Sopenharmony_ci#include <linux/idr.h> 2362306a36Sopenharmony_ci#include <linux/most.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define MAX_CHANNELS 64 2662306a36Sopenharmony_ci#define STRING_SIZE 80 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic struct ida mdev_id; 2962306a36Sopenharmony_cistatic int dummy_num_buffers; 3062306a36Sopenharmony_cistatic struct list_head comp_list; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct pipe { 3362306a36Sopenharmony_ci struct most_component *comp; 3462306a36Sopenharmony_ci int refs; 3562306a36Sopenharmony_ci int num_buffers; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct most_channel { 3962306a36Sopenharmony_ci struct device dev; 4062306a36Sopenharmony_ci struct completion cleanup; 4162306a36Sopenharmony_ci atomic_t mbo_ref; 4262306a36Sopenharmony_ci atomic_t mbo_nq_level; 4362306a36Sopenharmony_ci u16 channel_id; 4462306a36Sopenharmony_ci char name[STRING_SIZE]; 4562306a36Sopenharmony_ci bool is_poisoned; 4662306a36Sopenharmony_ci struct mutex start_mutex; /* channel activation synchronization */ 4762306a36Sopenharmony_ci struct mutex nq_mutex; /* nq thread synchronization */ 4862306a36Sopenharmony_ci int is_starving; 4962306a36Sopenharmony_ci struct most_interface *iface; 5062306a36Sopenharmony_ci struct most_channel_config cfg; 5162306a36Sopenharmony_ci bool keep_mbo; 5262306a36Sopenharmony_ci bool enqueue_halt; 5362306a36Sopenharmony_ci struct list_head fifo; 5462306a36Sopenharmony_ci spinlock_t fifo_lock; /* fifo access synchronization */ 5562306a36Sopenharmony_ci struct list_head halt_fifo; 5662306a36Sopenharmony_ci struct list_head list; 5762306a36Sopenharmony_ci struct pipe pipe0; 5862306a36Sopenharmony_ci struct pipe pipe1; 5962306a36Sopenharmony_ci struct list_head trash_fifo; 6062306a36Sopenharmony_ci struct task_struct *hdm_enqueue_task; 6162306a36Sopenharmony_ci wait_queue_head_t hdm_fifo_wq; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define to_channel(d) container_of(d, struct most_channel, dev) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistruct interface_private { 6862306a36Sopenharmony_ci int dev_id; 6962306a36Sopenharmony_ci char name[STRING_SIZE]; 7062306a36Sopenharmony_ci struct most_channel *channel[MAX_CHANNELS]; 7162306a36Sopenharmony_ci struct list_head channel_list; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic const struct { 7562306a36Sopenharmony_ci int most_ch_data_type; 7662306a36Sopenharmony_ci const char *name; 7762306a36Sopenharmony_ci} ch_data_type[] = { 7862306a36Sopenharmony_ci { MOST_CH_CONTROL, "control" }, 7962306a36Sopenharmony_ci { MOST_CH_ASYNC, "async" }, 8062306a36Sopenharmony_ci { MOST_CH_SYNC, "sync" }, 8162306a36Sopenharmony_ci { MOST_CH_ISOC, "isoc"}, 8262306a36Sopenharmony_ci { MOST_CH_ISOC, "isoc_avp"}, 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/** 8662306a36Sopenharmony_ci * list_pop_mbo - retrieves the first MBO of the list and removes it 8762306a36Sopenharmony_ci * @ptr: the list head to grab the MBO from. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci#define list_pop_mbo(ptr) \ 9062306a36Sopenharmony_ci({ \ 9162306a36Sopenharmony_ci struct mbo *_mbo = list_first_entry(ptr, struct mbo, list); \ 9262306a36Sopenharmony_ci list_del(&_mbo->list); \ 9362306a36Sopenharmony_ci _mbo; \ 9462306a36Sopenharmony_ci}) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/** 9762306a36Sopenharmony_ci * most_free_mbo_coherent - free an MBO and its coherent buffer 9862306a36Sopenharmony_ci * @mbo: most buffer 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_cistatic void most_free_mbo_coherent(struct mbo *mbo) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct most_channel *c = mbo->context; 10362306a36Sopenharmony_ci u16 const coherent_buf_size = c->cfg.buffer_size + c->cfg.extra_len; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (c->iface->dma_free) 10662306a36Sopenharmony_ci c->iface->dma_free(mbo, coherent_buf_size); 10762306a36Sopenharmony_ci else 10862306a36Sopenharmony_ci kfree(mbo->virt_address); 10962306a36Sopenharmony_ci kfree(mbo); 11062306a36Sopenharmony_ci if (atomic_sub_and_test(1, &c->mbo_ref)) 11162306a36Sopenharmony_ci complete(&c->cleanup); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/** 11562306a36Sopenharmony_ci * flush_channel_fifos - clear the channel fifos 11662306a36Sopenharmony_ci * @c: pointer to channel object 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_cistatic void flush_channel_fifos(struct most_channel *c) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci unsigned long flags, hf_flags; 12162306a36Sopenharmony_ci struct mbo *mbo, *tmp; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (list_empty(&c->fifo) && list_empty(&c->halt_fifo)) 12462306a36Sopenharmony_ci return; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci spin_lock_irqsave(&c->fifo_lock, flags); 12762306a36Sopenharmony_ci list_for_each_entry_safe(mbo, tmp, &c->fifo, list) { 12862306a36Sopenharmony_ci list_del(&mbo->list); 12962306a36Sopenharmony_ci spin_unlock_irqrestore(&c->fifo_lock, flags); 13062306a36Sopenharmony_ci most_free_mbo_coherent(mbo); 13162306a36Sopenharmony_ci spin_lock_irqsave(&c->fifo_lock, flags); 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci spin_unlock_irqrestore(&c->fifo_lock, flags); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci spin_lock_irqsave(&c->fifo_lock, hf_flags); 13662306a36Sopenharmony_ci list_for_each_entry_safe(mbo, tmp, &c->halt_fifo, list) { 13762306a36Sopenharmony_ci list_del(&mbo->list); 13862306a36Sopenharmony_ci spin_unlock_irqrestore(&c->fifo_lock, hf_flags); 13962306a36Sopenharmony_ci most_free_mbo_coherent(mbo); 14062306a36Sopenharmony_ci spin_lock_irqsave(&c->fifo_lock, hf_flags); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci spin_unlock_irqrestore(&c->fifo_lock, hf_flags); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (unlikely((!list_empty(&c->fifo) || !list_empty(&c->halt_fifo)))) 14562306a36Sopenharmony_ci dev_warn(&c->dev, "Channel or trash fifo not empty\n"); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/** 14962306a36Sopenharmony_ci * flush_trash_fifo - clear the trash fifo 15062306a36Sopenharmony_ci * @c: pointer to channel object 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cistatic int flush_trash_fifo(struct most_channel *c) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct mbo *mbo, *tmp; 15562306a36Sopenharmony_ci unsigned long flags; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci spin_lock_irqsave(&c->fifo_lock, flags); 15862306a36Sopenharmony_ci list_for_each_entry_safe(mbo, tmp, &c->trash_fifo, list) { 15962306a36Sopenharmony_ci list_del(&mbo->list); 16062306a36Sopenharmony_ci spin_unlock_irqrestore(&c->fifo_lock, flags); 16162306a36Sopenharmony_ci most_free_mbo_coherent(mbo); 16262306a36Sopenharmony_ci spin_lock_irqsave(&c->fifo_lock, flags); 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci spin_unlock_irqrestore(&c->fifo_lock, flags); 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic ssize_t available_directions_show(struct device *dev, 16962306a36Sopenharmony_ci struct device_attribute *attr, 17062306a36Sopenharmony_ci char *buf) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 17362306a36Sopenharmony_ci unsigned int i = c->channel_id; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci strcpy(buf, ""); 17662306a36Sopenharmony_ci if (c->iface->channel_vector[i].direction & MOST_CH_RX) 17762306a36Sopenharmony_ci strcat(buf, "rx "); 17862306a36Sopenharmony_ci if (c->iface->channel_vector[i].direction & MOST_CH_TX) 17962306a36Sopenharmony_ci strcat(buf, "tx "); 18062306a36Sopenharmony_ci strcat(buf, "\n"); 18162306a36Sopenharmony_ci return strlen(buf); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic ssize_t available_datatypes_show(struct device *dev, 18562306a36Sopenharmony_ci struct device_attribute *attr, 18662306a36Sopenharmony_ci char *buf) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 18962306a36Sopenharmony_ci unsigned int i = c->channel_id; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci strcpy(buf, ""); 19262306a36Sopenharmony_ci if (c->iface->channel_vector[i].data_type & MOST_CH_CONTROL) 19362306a36Sopenharmony_ci strcat(buf, "control "); 19462306a36Sopenharmony_ci if (c->iface->channel_vector[i].data_type & MOST_CH_ASYNC) 19562306a36Sopenharmony_ci strcat(buf, "async "); 19662306a36Sopenharmony_ci if (c->iface->channel_vector[i].data_type & MOST_CH_SYNC) 19762306a36Sopenharmony_ci strcat(buf, "sync "); 19862306a36Sopenharmony_ci if (c->iface->channel_vector[i].data_type & MOST_CH_ISOC) 19962306a36Sopenharmony_ci strcat(buf, "isoc "); 20062306a36Sopenharmony_ci strcat(buf, "\n"); 20162306a36Sopenharmony_ci return strlen(buf); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic ssize_t number_of_packet_buffers_show(struct device *dev, 20562306a36Sopenharmony_ci struct device_attribute *attr, 20662306a36Sopenharmony_ci char *buf) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 20962306a36Sopenharmony_ci unsigned int i = c->channel_id; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", 21262306a36Sopenharmony_ci c->iface->channel_vector[i].num_buffers_packet); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic ssize_t number_of_stream_buffers_show(struct device *dev, 21662306a36Sopenharmony_ci struct device_attribute *attr, 21762306a36Sopenharmony_ci char *buf) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 22062306a36Sopenharmony_ci unsigned int i = c->channel_id; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", 22362306a36Sopenharmony_ci c->iface->channel_vector[i].num_buffers_streaming); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic ssize_t size_of_packet_buffer_show(struct device *dev, 22762306a36Sopenharmony_ci struct device_attribute *attr, 22862306a36Sopenharmony_ci char *buf) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 23162306a36Sopenharmony_ci unsigned int i = c->channel_id; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", 23462306a36Sopenharmony_ci c->iface->channel_vector[i].buffer_size_packet); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic ssize_t size_of_stream_buffer_show(struct device *dev, 23862306a36Sopenharmony_ci struct device_attribute *attr, 23962306a36Sopenharmony_ci char *buf) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 24262306a36Sopenharmony_ci unsigned int i = c->channel_id; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", 24562306a36Sopenharmony_ci c->iface->channel_vector[i].buffer_size_streaming); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic ssize_t channel_starving_show(struct device *dev, 24962306a36Sopenharmony_ci struct device_attribute *attr, 25062306a36Sopenharmony_ci char *buf) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", c->is_starving); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic ssize_t set_number_of_buffers_show(struct device *dev, 25862306a36Sopenharmony_ci struct device_attribute *attr, 25962306a36Sopenharmony_ci char *buf) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.num_buffers); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic ssize_t set_buffer_size_show(struct device *dev, 26762306a36Sopenharmony_ci struct device_attribute *attr, 26862306a36Sopenharmony_ci char *buf) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.buffer_size); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic ssize_t set_direction_show(struct device *dev, 27662306a36Sopenharmony_ci struct device_attribute *attr, 27762306a36Sopenharmony_ci char *buf) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (c->cfg.direction & MOST_CH_TX) 28262306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "tx\n"); 28362306a36Sopenharmony_ci else if (c->cfg.direction & MOST_CH_RX) 28462306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "rx\n"); 28562306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "unconfigured\n"); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic ssize_t set_datatype_show(struct device *dev, 28962306a36Sopenharmony_ci struct device_attribute *attr, 29062306a36Sopenharmony_ci char *buf) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci int i; 29362306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ch_data_type); i++) { 29662306a36Sopenharmony_ci if (c->cfg.data_type & ch_data_type[i].most_ch_data_type) 29762306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s", 29862306a36Sopenharmony_ci ch_data_type[i].name); 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "unconfigured\n"); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic ssize_t set_subbuffer_size_show(struct device *dev, 30462306a36Sopenharmony_ci struct device_attribute *attr, 30562306a36Sopenharmony_ci char *buf) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.subbuffer_size); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic ssize_t set_packets_per_xact_show(struct device *dev, 31362306a36Sopenharmony_ci struct device_attribute *attr, 31462306a36Sopenharmony_ci char *buf) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.packets_per_xact); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic ssize_t set_dbr_size_show(struct device *dev, 32262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.dbr_size); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci#define to_dev_attr(a) container_of(a, struct device_attribute, attr) 33062306a36Sopenharmony_cistatic umode_t channel_attr_is_visible(struct kobject *kobj, 33162306a36Sopenharmony_ci struct attribute *attr, int index) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct device_attribute *dev_attr = to_dev_attr(attr); 33462306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 33562306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (!strcmp(dev_attr->attr.name, "set_dbr_size") && 33862306a36Sopenharmony_ci (c->iface->interface != ITYPE_MEDIALB_DIM2)) 33962306a36Sopenharmony_ci return 0; 34062306a36Sopenharmony_ci if (!strcmp(dev_attr->attr.name, "set_packets_per_xact") && 34162306a36Sopenharmony_ci (c->iface->interface != ITYPE_USB)) 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return attr->mode; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci#define DEV_ATTR(_name) (&dev_attr_##_name.attr) 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(available_directions); 35062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(available_datatypes); 35162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(number_of_packet_buffers); 35262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(number_of_stream_buffers); 35362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(size_of_stream_buffer); 35462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(size_of_packet_buffer); 35562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(channel_starving); 35662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(set_buffer_size); 35762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(set_number_of_buffers); 35862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(set_direction); 35962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(set_datatype); 36062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(set_subbuffer_size); 36162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(set_packets_per_xact); 36262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(set_dbr_size); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic struct attribute *channel_attrs[] = { 36562306a36Sopenharmony_ci DEV_ATTR(available_directions), 36662306a36Sopenharmony_ci DEV_ATTR(available_datatypes), 36762306a36Sopenharmony_ci DEV_ATTR(number_of_packet_buffers), 36862306a36Sopenharmony_ci DEV_ATTR(number_of_stream_buffers), 36962306a36Sopenharmony_ci DEV_ATTR(size_of_stream_buffer), 37062306a36Sopenharmony_ci DEV_ATTR(size_of_packet_buffer), 37162306a36Sopenharmony_ci DEV_ATTR(channel_starving), 37262306a36Sopenharmony_ci DEV_ATTR(set_buffer_size), 37362306a36Sopenharmony_ci DEV_ATTR(set_number_of_buffers), 37462306a36Sopenharmony_ci DEV_ATTR(set_direction), 37562306a36Sopenharmony_ci DEV_ATTR(set_datatype), 37662306a36Sopenharmony_ci DEV_ATTR(set_subbuffer_size), 37762306a36Sopenharmony_ci DEV_ATTR(set_packets_per_xact), 37862306a36Sopenharmony_ci DEV_ATTR(set_dbr_size), 37962306a36Sopenharmony_ci NULL, 38062306a36Sopenharmony_ci}; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic const struct attribute_group channel_attr_group = { 38362306a36Sopenharmony_ci .attrs = channel_attrs, 38462306a36Sopenharmony_ci .is_visible = channel_attr_is_visible, 38562306a36Sopenharmony_ci}; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic const struct attribute_group *channel_attr_groups[] = { 38862306a36Sopenharmony_ci &channel_attr_group, 38962306a36Sopenharmony_ci NULL, 39062306a36Sopenharmony_ci}; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic ssize_t description_show(struct device *dev, 39362306a36Sopenharmony_ci struct device_attribute *attr, 39462306a36Sopenharmony_ci char *buf) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct most_interface *iface = dev_get_drvdata(dev); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", iface->description); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic ssize_t interface_show(struct device *dev, 40262306a36Sopenharmony_ci struct device_attribute *attr, 40362306a36Sopenharmony_ci char *buf) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct most_interface *iface = dev_get_drvdata(dev); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci switch (iface->interface) { 40862306a36Sopenharmony_ci case ITYPE_LOOPBACK: 40962306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "loopback\n"); 41062306a36Sopenharmony_ci case ITYPE_I2C: 41162306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "i2c\n"); 41262306a36Sopenharmony_ci case ITYPE_I2S: 41362306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "i2s\n"); 41462306a36Sopenharmony_ci case ITYPE_TSI: 41562306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "tsi\n"); 41662306a36Sopenharmony_ci case ITYPE_HBI: 41762306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "hbi\n"); 41862306a36Sopenharmony_ci case ITYPE_MEDIALB_DIM: 41962306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "mlb_dim\n"); 42062306a36Sopenharmony_ci case ITYPE_MEDIALB_DIM2: 42162306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "mlb_dim2\n"); 42262306a36Sopenharmony_ci case ITYPE_USB: 42362306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "usb\n"); 42462306a36Sopenharmony_ci case ITYPE_PCIE: 42562306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "pcie\n"); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "unknown\n"); 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(description); 43162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(interface); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic struct attribute *interface_attrs[] = { 43462306a36Sopenharmony_ci DEV_ATTR(description), 43562306a36Sopenharmony_ci DEV_ATTR(interface), 43662306a36Sopenharmony_ci NULL, 43762306a36Sopenharmony_ci}; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic const struct attribute_group interface_attr_group = { 44062306a36Sopenharmony_ci .attrs = interface_attrs, 44162306a36Sopenharmony_ci}; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic const struct attribute_group *interface_attr_groups[] = { 44462306a36Sopenharmony_ci &interface_attr_group, 44562306a36Sopenharmony_ci NULL, 44662306a36Sopenharmony_ci}; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic struct most_component *match_component(char *name) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct most_component *comp; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci list_for_each_entry(comp, &comp_list, list) { 45362306a36Sopenharmony_ci if (!strcmp(comp->name, name)) 45462306a36Sopenharmony_ci return comp; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci return NULL; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistruct show_links_data { 46062306a36Sopenharmony_ci int offs; 46162306a36Sopenharmony_ci char *buf; 46262306a36Sopenharmony_ci}; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic int print_links(struct device *dev, void *data) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct show_links_data *d = data; 46762306a36Sopenharmony_ci int offs = d->offs; 46862306a36Sopenharmony_ci char *buf = d->buf; 46962306a36Sopenharmony_ci struct most_channel *c; 47062306a36Sopenharmony_ci struct most_interface *iface = dev_get_drvdata(dev); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci list_for_each_entry(c, &iface->p->channel_list, list) { 47362306a36Sopenharmony_ci if (c->pipe0.comp) { 47462306a36Sopenharmony_ci offs += scnprintf(buf + offs, 47562306a36Sopenharmony_ci PAGE_SIZE - offs, 47662306a36Sopenharmony_ci "%s:%s:%s\n", 47762306a36Sopenharmony_ci c->pipe0.comp->name, 47862306a36Sopenharmony_ci dev_name(iface->dev), 47962306a36Sopenharmony_ci dev_name(&c->dev)); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci if (c->pipe1.comp) { 48262306a36Sopenharmony_ci offs += scnprintf(buf + offs, 48362306a36Sopenharmony_ci PAGE_SIZE - offs, 48462306a36Sopenharmony_ci "%s:%s:%s\n", 48562306a36Sopenharmony_ci c->pipe1.comp->name, 48662306a36Sopenharmony_ci dev_name(iface->dev), 48762306a36Sopenharmony_ci dev_name(&c->dev)); 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci d->offs = offs; 49162306a36Sopenharmony_ci return 0; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic int most_match(struct device *dev, struct device_driver *drv) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci if (!strcmp(dev_name(dev), "most")) 49762306a36Sopenharmony_ci return 0; 49862306a36Sopenharmony_ci else 49962306a36Sopenharmony_ci return 1; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic struct bus_type mostbus = { 50362306a36Sopenharmony_ci .name = "most", 50462306a36Sopenharmony_ci .match = most_match, 50562306a36Sopenharmony_ci}; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic ssize_t links_show(struct device_driver *drv, char *buf) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci struct show_links_data d = { .buf = buf }; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci bus_for_each_dev(&mostbus, NULL, &d, print_links); 51262306a36Sopenharmony_ci return d.offs; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic ssize_t components_show(struct device_driver *drv, char *buf) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct most_component *comp; 51862306a36Sopenharmony_ci int offs = 0; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci list_for_each_entry(comp, &comp_list, list) { 52162306a36Sopenharmony_ci offs += scnprintf(buf + offs, PAGE_SIZE - offs, "%s\n", 52262306a36Sopenharmony_ci comp->name); 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci return offs; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/** 52862306a36Sopenharmony_ci * get_channel - get pointer to channel 52962306a36Sopenharmony_ci * @mdev: name of the device interface 53062306a36Sopenharmony_ci * @mdev_ch: name of channel 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_cistatic struct most_channel *get_channel(char *mdev, char *mdev_ch) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct device *dev = NULL; 53562306a36Sopenharmony_ci struct most_interface *iface; 53662306a36Sopenharmony_ci struct most_channel *c, *tmp; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci dev = bus_find_device_by_name(&mostbus, NULL, mdev); 53962306a36Sopenharmony_ci if (!dev) 54062306a36Sopenharmony_ci return NULL; 54162306a36Sopenharmony_ci put_device(dev); 54262306a36Sopenharmony_ci iface = dev_get_drvdata(dev); 54362306a36Sopenharmony_ci list_for_each_entry_safe(c, tmp, &iface->p->channel_list, list) { 54462306a36Sopenharmony_ci if (!strcmp(dev_name(&c->dev), mdev_ch)) 54562306a36Sopenharmony_ci return c; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci return NULL; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic 55162306a36Sopenharmony_ciinline int link_channel_to_component(struct most_channel *c, 55262306a36Sopenharmony_ci struct most_component *comp, 55362306a36Sopenharmony_ci char *name, 55462306a36Sopenharmony_ci char *comp_param) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci int ret; 55762306a36Sopenharmony_ci struct most_component **comp_ptr; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (!c->pipe0.comp) 56062306a36Sopenharmony_ci comp_ptr = &c->pipe0.comp; 56162306a36Sopenharmony_ci else if (!c->pipe1.comp) 56262306a36Sopenharmony_ci comp_ptr = &c->pipe1.comp; 56362306a36Sopenharmony_ci else 56462306a36Sopenharmony_ci return -ENOSPC; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci *comp_ptr = comp; 56762306a36Sopenharmony_ci ret = comp->probe_channel(c->iface, c->channel_id, &c->cfg, name, 56862306a36Sopenharmony_ci comp_param); 56962306a36Sopenharmony_ci if (ret) { 57062306a36Sopenharmony_ci *comp_ptr = NULL; 57162306a36Sopenharmony_ci return ret; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ciint most_set_cfg_buffer_size(char *mdev, char *mdev_ch, u16 val) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct most_channel *c = get_channel(mdev, mdev_ch); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (!c) 58162306a36Sopenharmony_ci return -ENODEV; 58262306a36Sopenharmony_ci c->cfg.buffer_size = val; 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ciint most_set_cfg_subbuffer_size(char *mdev, char *mdev_ch, u16 val) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci struct most_channel *c = get_channel(mdev, mdev_ch); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (!c) 59162306a36Sopenharmony_ci return -ENODEV; 59262306a36Sopenharmony_ci c->cfg.subbuffer_size = val; 59362306a36Sopenharmony_ci return 0; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ciint most_set_cfg_dbr_size(char *mdev, char *mdev_ch, u16 val) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct most_channel *c = get_channel(mdev, mdev_ch); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (!c) 60162306a36Sopenharmony_ci return -ENODEV; 60262306a36Sopenharmony_ci c->cfg.dbr_size = val; 60362306a36Sopenharmony_ci return 0; 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ciint most_set_cfg_num_buffers(char *mdev, char *mdev_ch, u16 val) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct most_channel *c = get_channel(mdev, mdev_ch); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (!c) 61162306a36Sopenharmony_ci return -ENODEV; 61262306a36Sopenharmony_ci c->cfg.num_buffers = val; 61362306a36Sopenharmony_ci return 0; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ciint most_set_cfg_datatype(char *mdev, char *mdev_ch, char *buf) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci int i; 61962306a36Sopenharmony_ci struct most_channel *c = get_channel(mdev, mdev_ch); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (!c) 62262306a36Sopenharmony_ci return -ENODEV; 62362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ch_data_type); i++) { 62462306a36Sopenharmony_ci if (!strcmp(buf, ch_data_type[i].name)) { 62562306a36Sopenharmony_ci c->cfg.data_type = ch_data_type[i].most_ch_data_type; 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (i == ARRAY_SIZE(ch_data_type)) 63162306a36Sopenharmony_ci dev_warn(&c->dev, "Invalid attribute settings\n"); 63262306a36Sopenharmony_ci return 0; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ciint most_set_cfg_direction(char *mdev, char *mdev_ch, char *buf) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci struct most_channel *c = get_channel(mdev, mdev_ch); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (!c) 64062306a36Sopenharmony_ci return -ENODEV; 64162306a36Sopenharmony_ci if (!strcmp(buf, "dir_rx")) { 64262306a36Sopenharmony_ci c->cfg.direction = MOST_CH_RX; 64362306a36Sopenharmony_ci } else if (!strcmp(buf, "rx")) { 64462306a36Sopenharmony_ci c->cfg.direction = MOST_CH_RX; 64562306a36Sopenharmony_ci } else if (!strcmp(buf, "dir_tx")) { 64662306a36Sopenharmony_ci c->cfg.direction = MOST_CH_TX; 64762306a36Sopenharmony_ci } else if (!strcmp(buf, "tx")) { 64862306a36Sopenharmony_ci c->cfg.direction = MOST_CH_TX; 64962306a36Sopenharmony_ci } else { 65062306a36Sopenharmony_ci dev_err(&c->dev, "Invalid direction\n"); 65162306a36Sopenharmony_ci return -ENODATA; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci return 0; 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ciint most_set_cfg_packets_xact(char *mdev, char *mdev_ch, u16 val) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci struct most_channel *c = get_channel(mdev, mdev_ch); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (!c) 66162306a36Sopenharmony_ci return -ENODEV; 66262306a36Sopenharmony_ci c->cfg.packets_per_xact = val; 66362306a36Sopenharmony_ci return 0; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ciint most_cfg_complete(char *comp_name) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci struct most_component *comp; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci comp = match_component(comp_name); 67162306a36Sopenharmony_ci if (!comp) 67262306a36Sopenharmony_ci return -ENODEV; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return comp->cfg_complete(); 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ciint most_add_link(char *mdev, char *mdev_ch, char *comp_name, char *link_name, 67862306a36Sopenharmony_ci char *comp_param) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci struct most_channel *c = get_channel(mdev, mdev_ch); 68162306a36Sopenharmony_ci struct most_component *comp = match_component(comp_name); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (!c || !comp) 68462306a36Sopenharmony_ci return -ENODEV; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return link_channel_to_component(c, comp, link_name, comp_param); 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ciint most_remove_link(char *mdev, char *mdev_ch, char *comp_name) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci struct most_channel *c; 69262306a36Sopenharmony_ci struct most_component *comp; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci comp = match_component(comp_name); 69562306a36Sopenharmony_ci if (!comp) 69662306a36Sopenharmony_ci return -ENODEV; 69762306a36Sopenharmony_ci c = get_channel(mdev, mdev_ch); 69862306a36Sopenharmony_ci if (!c) 69962306a36Sopenharmony_ci return -ENODEV; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (comp->disconnect_channel(c->iface, c->channel_id)) 70262306a36Sopenharmony_ci return -EIO; 70362306a36Sopenharmony_ci if (c->pipe0.comp == comp) 70462306a36Sopenharmony_ci c->pipe0.comp = NULL; 70562306a36Sopenharmony_ci if (c->pipe1.comp == comp) 70662306a36Sopenharmony_ci c->pipe1.comp = NULL; 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci#define DRV_ATTR(_name) (&driver_attr_##_name.attr) 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic DRIVER_ATTR_RO(links); 71362306a36Sopenharmony_cistatic DRIVER_ATTR_RO(components); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic struct attribute *mc_attrs[] = { 71662306a36Sopenharmony_ci DRV_ATTR(links), 71762306a36Sopenharmony_ci DRV_ATTR(components), 71862306a36Sopenharmony_ci NULL, 71962306a36Sopenharmony_ci}; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic const struct attribute_group mc_attr_group = { 72262306a36Sopenharmony_ci .attrs = mc_attrs, 72362306a36Sopenharmony_ci}; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic const struct attribute_group *mc_attr_groups[] = { 72662306a36Sopenharmony_ci &mc_attr_group, 72762306a36Sopenharmony_ci NULL, 72862306a36Sopenharmony_ci}; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic struct device_driver mostbus_driver = { 73162306a36Sopenharmony_ci .name = "most_core", 73262306a36Sopenharmony_ci .bus = &mostbus, 73362306a36Sopenharmony_ci .groups = mc_attr_groups, 73462306a36Sopenharmony_ci}; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic inline void trash_mbo(struct mbo *mbo) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci unsigned long flags; 73962306a36Sopenharmony_ci struct most_channel *c = mbo->context; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci spin_lock_irqsave(&c->fifo_lock, flags); 74262306a36Sopenharmony_ci list_add(&mbo->list, &c->trash_fifo); 74362306a36Sopenharmony_ci spin_unlock_irqrestore(&c->fifo_lock, flags); 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic bool hdm_mbo_ready(struct most_channel *c) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci bool empty; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (c->enqueue_halt) 75162306a36Sopenharmony_ci return false; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci spin_lock_irq(&c->fifo_lock); 75462306a36Sopenharmony_ci empty = list_empty(&c->halt_fifo); 75562306a36Sopenharmony_ci spin_unlock_irq(&c->fifo_lock); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci return !empty; 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic void nq_hdm_mbo(struct mbo *mbo) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci unsigned long flags; 76362306a36Sopenharmony_ci struct most_channel *c = mbo->context; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci spin_lock_irqsave(&c->fifo_lock, flags); 76662306a36Sopenharmony_ci list_add_tail(&mbo->list, &c->halt_fifo); 76762306a36Sopenharmony_ci spin_unlock_irqrestore(&c->fifo_lock, flags); 76862306a36Sopenharmony_ci wake_up_interruptible(&c->hdm_fifo_wq); 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic int hdm_enqueue_thread(void *data) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci struct most_channel *c = data; 77462306a36Sopenharmony_ci struct mbo *mbo; 77562306a36Sopenharmony_ci int ret; 77662306a36Sopenharmony_ci typeof(c->iface->enqueue) enqueue = c->iface->enqueue; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci while (likely(!kthread_should_stop())) { 77962306a36Sopenharmony_ci wait_event_interruptible(c->hdm_fifo_wq, 78062306a36Sopenharmony_ci hdm_mbo_ready(c) || 78162306a36Sopenharmony_ci kthread_should_stop()); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci mutex_lock(&c->nq_mutex); 78462306a36Sopenharmony_ci spin_lock_irq(&c->fifo_lock); 78562306a36Sopenharmony_ci if (unlikely(c->enqueue_halt || list_empty(&c->halt_fifo))) { 78662306a36Sopenharmony_ci spin_unlock_irq(&c->fifo_lock); 78762306a36Sopenharmony_ci mutex_unlock(&c->nq_mutex); 78862306a36Sopenharmony_ci continue; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci mbo = list_pop_mbo(&c->halt_fifo); 79262306a36Sopenharmony_ci spin_unlock_irq(&c->fifo_lock); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (c->cfg.direction == MOST_CH_RX) 79562306a36Sopenharmony_ci mbo->buffer_length = c->cfg.buffer_size; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci ret = enqueue(mbo->ifp, mbo->hdm_channel_id, mbo); 79862306a36Sopenharmony_ci mutex_unlock(&c->nq_mutex); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (unlikely(ret)) { 80162306a36Sopenharmony_ci dev_err(&c->dev, "Buffer enqueue failed\n"); 80262306a36Sopenharmony_ci nq_hdm_mbo(mbo); 80362306a36Sopenharmony_ci c->hdm_enqueue_task = NULL; 80462306a36Sopenharmony_ci return 0; 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci return 0; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic int run_enqueue_thread(struct most_channel *c, int channel_id) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci struct task_struct *task = 81462306a36Sopenharmony_ci kthread_run(hdm_enqueue_thread, c, "hdm_fifo_%d", 81562306a36Sopenharmony_ci channel_id); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (IS_ERR(task)) 81862306a36Sopenharmony_ci return PTR_ERR(task); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci c->hdm_enqueue_task = task; 82162306a36Sopenharmony_ci return 0; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci/** 82562306a36Sopenharmony_ci * arm_mbo - recycle MBO for further usage 82662306a36Sopenharmony_ci * @mbo: most buffer 82762306a36Sopenharmony_ci * 82862306a36Sopenharmony_ci * This puts an MBO back to the list to have it ready for up coming 82962306a36Sopenharmony_ci * tx transactions. 83062306a36Sopenharmony_ci * 83162306a36Sopenharmony_ci * In case the MBO belongs to a channel that recently has been 83262306a36Sopenharmony_ci * poisoned, the MBO is scheduled to be trashed. 83362306a36Sopenharmony_ci * Calls the completion handler of an attached component. 83462306a36Sopenharmony_ci */ 83562306a36Sopenharmony_cistatic void arm_mbo(struct mbo *mbo) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci unsigned long flags; 83862306a36Sopenharmony_ci struct most_channel *c; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci c = mbo->context; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (c->is_poisoned) { 84362306a36Sopenharmony_ci trash_mbo(mbo); 84462306a36Sopenharmony_ci return; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci spin_lock_irqsave(&c->fifo_lock, flags); 84862306a36Sopenharmony_ci ++*mbo->num_buffers_ptr; 84962306a36Sopenharmony_ci list_add_tail(&mbo->list, &c->fifo); 85062306a36Sopenharmony_ci spin_unlock_irqrestore(&c->fifo_lock, flags); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if (c->pipe0.refs && c->pipe0.comp->tx_completion) 85362306a36Sopenharmony_ci c->pipe0.comp->tx_completion(c->iface, c->channel_id); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (c->pipe1.refs && c->pipe1.comp->tx_completion) 85662306a36Sopenharmony_ci c->pipe1.comp->tx_completion(c->iface, c->channel_id); 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci/** 86062306a36Sopenharmony_ci * arm_mbo_chain - helper function that arms an MBO chain for the HDM 86162306a36Sopenharmony_ci * @c: pointer to interface channel 86262306a36Sopenharmony_ci * @dir: direction of the channel 86362306a36Sopenharmony_ci * @compl: pointer to completion function 86462306a36Sopenharmony_ci * 86562306a36Sopenharmony_ci * This allocates buffer objects including the containing DMA coherent 86662306a36Sopenharmony_ci * buffer and puts them in the fifo. 86762306a36Sopenharmony_ci * Buffers of Rx channels are put in the kthread fifo, hence immediately 86862306a36Sopenharmony_ci * submitted to the HDM. 86962306a36Sopenharmony_ci * 87062306a36Sopenharmony_ci * Returns the number of allocated and enqueued MBOs. 87162306a36Sopenharmony_ci */ 87262306a36Sopenharmony_cistatic int arm_mbo_chain(struct most_channel *c, int dir, 87362306a36Sopenharmony_ci void (*compl)(struct mbo *)) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci unsigned int i; 87662306a36Sopenharmony_ci struct mbo *mbo; 87762306a36Sopenharmony_ci unsigned long flags; 87862306a36Sopenharmony_ci u32 coherent_buf_size = c->cfg.buffer_size + c->cfg.extra_len; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci atomic_set(&c->mbo_nq_level, 0); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci for (i = 0; i < c->cfg.num_buffers; i++) { 88362306a36Sopenharmony_ci mbo = kzalloc(sizeof(*mbo), GFP_KERNEL); 88462306a36Sopenharmony_ci if (!mbo) 88562306a36Sopenharmony_ci goto flush_fifos; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci mbo->context = c; 88862306a36Sopenharmony_ci mbo->ifp = c->iface; 88962306a36Sopenharmony_ci mbo->hdm_channel_id = c->channel_id; 89062306a36Sopenharmony_ci if (c->iface->dma_alloc) { 89162306a36Sopenharmony_ci mbo->virt_address = 89262306a36Sopenharmony_ci c->iface->dma_alloc(mbo, coherent_buf_size); 89362306a36Sopenharmony_ci } else { 89462306a36Sopenharmony_ci mbo->virt_address = 89562306a36Sopenharmony_ci kzalloc(coherent_buf_size, GFP_KERNEL); 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci if (!mbo->virt_address) 89862306a36Sopenharmony_ci goto release_mbo; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci mbo->complete = compl; 90162306a36Sopenharmony_ci mbo->num_buffers_ptr = &dummy_num_buffers; 90262306a36Sopenharmony_ci if (dir == MOST_CH_RX) { 90362306a36Sopenharmony_ci nq_hdm_mbo(mbo); 90462306a36Sopenharmony_ci atomic_inc(&c->mbo_nq_level); 90562306a36Sopenharmony_ci } else { 90662306a36Sopenharmony_ci spin_lock_irqsave(&c->fifo_lock, flags); 90762306a36Sopenharmony_ci list_add_tail(&mbo->list, &c->fifo); 90862306a36Sopenharmony_ci spin_unlock_irqrestore(&c->fifo_lock, flags); 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci return c->cfg.num_buffers; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cirelease_mbo: 91462306a36Sopenharmony_ci kfree(mbo); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ciflush_fifos: 91762306a36Sopenharmony_ci flush_channel_fifos(c); 91862306a36Sopenharmony_ci return 0; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci/** 92262306a36Sopenharmony_ci * most_submit_mbo - submits an MBO to fifo 92362306a36Sopenharmony_ci * @mbo: most buffer 92462306a36Sopenharmony_ci */ 92562306a36Sopenharmony_civoid most_submit_mbo(struct mbo *mbo) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci if (WARN_ONCE(!mbo || !mbo->context, 92862306a36Sopenharmony_ci "Bad buffer or missing channel reference\n")) 92962306a36Sopenharmony_ci return; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci nq_hdm_mbo(mbo); 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(most_submit_mbo); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci/** 93662306a36Sopenharmony_ci * most_write_completion - write completion handler 93762306a36Sopenharmony_ci * @mbo: most buffer 93862306a36Sopenharmony_ci * 93962306a36Sopenharmony_ci * This recycles the MBO for further usage. In case the channel has been 94062306a36Sopenharmony_ci * poisoned, the MBO is scheduled to be trashed. 94162306a36Sopenharmony_ci */ 94262306a36Sopenharmony_cistatic void most_write_completion(struct mbo *mbo) 94362306a36Sopenharmony_ci{ 94462306a36Sopenharmony_ci struct most_channel *c; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci c = mbo->context; 94762306a36Sopenharmony_ci if (unlikely(c->is_poisoned || (mbo->status == MBO_E_CLOSE))) 94862306a36Sopenharmony_ci trash_mbo(mbo); 94962306a36Sopenharmony_ci else 95062306a36Sopenharmony_ci arm_mbo(mbo); 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ciint channel_has_mbo(struct most_interface *iface, int id, 95462306a36Sopenharmony_ci struct most_component *comp) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci struct most_channel *c = iface->p->channel[id]; 95762306a36Sopenharmony_ci unsigned long flags; 95862306a36Sopenharmony_ci int empty; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci if (unlikely(!c)) 96162306a36Sopenharmony_ci return -EINVAL; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci if (c->pipe0.refs && c->pipe1.refs && 96462306a36Sopenharmony_ci ((comp == c->pipe0.comp && c->pipe0.num_buffers <= 0) || 96562306a36Sopenharmony_ci (comp == c->pipe1.comp && c->pipe1.num_buffers <= 0))) 96662306a36Sopenharmony_ci return 0; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci spin_lock_irqsave(&c->fifo_lock, flags); 96962306a36Sopenharmony_ci empty = list_empty(&c->fifo); 97062306a36Sopenharmony_ci spin_unlock_irqrestore(&c->fifo_lock, flags); 97162306a36Sopenharmony_ci return !empty; 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(channel_has_mbo); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci/** 97662306a36Sopenharmony_ci * most_get_mbo - get pointer to an MBO of pool 97762306a36Sopenharmony_ci * @iface: pointer to interface instance 97862306a36Sopenharmony_ci * @id: channel ID 97962306a36Sopenharmony_ci * @comp: driver component 98062306a36Sopenharmony_ci * 98162306a36Sopenharmony_ci * This attempts to get a free buffer out of the channel fifo. 98262306a36Sopenharmony_ci * Returns a pointer to MBO on success or NULL otherwise. 98362306a36Sopenharmony_ci */ 98462306a36Sopenharmony_cistruct mbo *most_get_mbo(struct most_interface *iface, int id, 98562306a36Sopenharmony_ci struct most_component *comp) 98662306a36Sopenharmony_ci{ 98762306a36Sopenharmony_ci struct mbo *mbo; 98862306a36Sopenharmony_ci struct most_channel *c; 98962306a36Sopenharmony_ci unsigned long flags; 99062306a36Sopenharmony_ci int *num_buffers_ptr; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci c = iface->p->channel[id]; 99362306a36Sopenharmony_ci if (unlikely(!c)) 99462306a36Sopenharmony_ci return NULL; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci if (c->pipe0.refs && c->pipe1.refs && 99762306a36Sopenharmony_ci ((comp == c->pipe0.comp && c->pipe0.num_buffers <= 0) || 99862306a36Sopenharmony_ci (comp == c->pipe1.comp && c->pipe1.num_buffers <= 0))) 99962306a36Sopenharmony_ci return NULL; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if (comp == c->pipe0.comp) 100262306a36Sopenharmony_ci num_buffers_ptr = &c->pipe0.num_buffers; 100362306a36Sopenharmony_ci else if (comp == c->pipe1.comp) 100462306a36Sopenharmony_ci num_buffers_ptr = &c->pipe1.num_buffers; 100562306a36Sopenharmony_ci else 100662306a36Sopenharmony_ci num_buffers_ptr = &dummy_num_buffers; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci spin_lock_irqsave(&c->fifo_lock, flags); 100962306a36Sopenharmony_ci if (list_empty(&c->fifo)) { 101062306a36Sopenharmony_ci spin_unlock_irqrestore(&c->fifo_lock, flags); 101162306a36Sopenharmony_ci return NULL; 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci mbo = list_pop_mbo(&c->fifo); 101462306a36Sopenharmony_ci --*num_buffers_ptr; 101562306a36Sopenharmony_ci spin_unlock_irqrestore(&c->fifo_lock, flags); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci mbo->num_buffers_ptr = num_buffers_ptr; 101862306a36Sopenharmony_ci mbo->buffer_length = c->cfg.buffer_size; 101962306a36Sopenharmony_ci return mbo; 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(most_get_mbo); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci/** 102462306a36Sopenharmony_ci * most_put_mbo - return buffer to pool 102562306a36Sopenharmony_ci * @mbo: most buffer 102662306a36Sopenharmony_ci */ 102762306a36Sopenharmony_civoid most_put_mbo(struct mbo *mbo) 102862306a36Sopenharmony_ci{ 102962306a36Sopenharmony_ci struct most_channel *c = mbo->context; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (c->cfg.direction == MOST_CH_TX) { 103262306a36Sopenharmony_ci arm_mbo(mbo); 103362306a36Sopenharmony_ci return; 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci nq_hdm_mbo(mbo); 103662306a36Sopenharmony_ci atomic_inc(&c->mbo_nq_level); 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(most_put_mbo); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci/** 104162306a36Sopenharmony_ci * most_read_completion - read completion handler 104262306a36Sopenharmony_ci * @mbo: most buffer 104362306a36Sopenharmony_ci * 104462306a36Sopenharmony_ci * This function is called by the HDM when data has been received from the 104562306a36Sopenharmony_ci * hardware and copied to the buffer of the MBO. 104662306a36Sopenharmony_ci * 104762306a36Sopenharmony_ci * In case the channel has been poisoned it puts the buffer in the trash queue. 104862306a36Sopenharmony_ci * Otherwise, it passes the buffer to an component for further processing. 104962306a36Sopenharmony_ci */ 105062306a36Sopenharmony_cistatic void most_read_completion(struct mbo *mbo) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci struct most_channel *c = mbo->context; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (unlikely(c->is_poisoned || (mbo->status == MBO_E_CLOSE))) { 105562306a36Sopenharmony_ci trash_mbo(mbo); 105662306a36Sopenharmony_ci return; 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (mbo->status == MBO_E_INVAL) { 106062306a36Sopenharmony_ci nq_hdm_mbo(mbo); 106162306a36Sopenharmony_ci atomic_inc(&c->mbo_nq_level); 106262306a36Sopenharmony_ci return; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci if (atomic_sub_and_test(1, &c->mbo_nq_level)) 106662306a36Sopenharmony_ci c->is_starving = 1; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci if (c->pipe0.refs && c->pipe0.comp->rx_completion && 106962306a36Sopenharmony_ci c->pipe0.comp->rx_completion(mbo) == 0) 107062306a36Sopenharmony_ci return; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci if (c->pipe1.refs && c->pipe1.comp->rx_completion && 107362306a36Sopenharmony_ci c->pipe1.comp->rx_completion(mbo) == 0) 107462306a36Sopenharmony_ci return; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci most_put_mbo(mbo); 107762306a36Sopenharmony_ci} 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci/** 108062306a36Sopenharmony_ci * most_start_channel - prepares a channel for communication 108162306a36Sopenharmony_ci * @iface: pointer to interface instance 108262306a36Sopenharmony_ci * @id: channel ID 108362306a36Sopenharmony_ci * @comp: driver component 108462306a36Sopenharmony_ci * 108562306a36Sopenharmony_ci * This prepares the channel for usage. Cross-checks whether the 108662306a36Sopenharmony_ci * channel's been properly configured. 108762306a36Sopenharmony_ci * 108862306a36Sopenharmony_ci * Returns 0 on success or error code otherwise. 108962306a36Sopenharmony_ci */ 109062306a36Sopenharmony_ciint most_start_channel(struct most_interface *iface, int id, 109162306a36Sopenharmony_ci struct most_component *comp) 109262306a36Sopenharmony_ci{ 109362306a36Sopenharmony_ci int num_buffer; 109462306a36Sopenharmony_ci int ret; 109562306a36Sopenharmony_ci struct most_channel *c = iface->p->channel[id]; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci if (unlikely(!c)) 109862306a36Sopenharmony_ci return -EINVAL; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci mutex_lock(&c->start_mutex); 110162306a36Sopenharmony_ci if (c->pipe0.refs + c->pipe1.refs > 0) 110262306a36Sopenharmony_ci goto out; /* already started by another component */ 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci if (!try_module_get(iface->mod)) { 110562306a36Sopenharmony_ci dev_err(&c->dev, "Failed to acquire HDM lock\n"); 110662306a36Sopenharmony_ci mutex_unlock(&c->start_mutex); 110762306a36Sopenharmony_ci return -ENOLCK; 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci c->cfg.extra_len = 0; 111162306a36Sopenharmony_ci if (c->iface->configure(c->iface, c->channel_id, &c->cfg)) { 111262306a36Sopenharmony_ci dev_err(&c->dev, "Channel configuration failed. Go check settings...\n"); 111362306a36Sopenharmony_ci ret = -EINVAL; 111462306a36Sopenharmony_ci goto err_put_module; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci init_waitqueue_head(&c->hdm_fifo_wq); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci if (c->cfg.direction == MOST_CH_RX) 112062306a36Sopenharmony_ci num_buffer = arm_mbo_chain(c, c->cfg.direction, 112162306a36Sopenharmony_ci most_read_completion); 112262306a36Sopenharmony_ci else 112362306a36Sopenharmony_ci num_buffer = arm_mbo_chain(c, c->cfg.direction, 112462306a36Sopenharmony_ci most_write_completion); 112562306a36Sopenharmony_ci if (unlikely(!num_buffer)) { 112662306a36Sopenharmony_ci ret = -ENOMEM; 112762306a36Sopenharmony_ci goto err_put_module; 112862306a36Sopenharmony_ci } 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci ret = run_enqueue_thread(c, id); 113162306a36Sopenharmony_ci if (ret) 113262306a36Sopenharmony_ci goto err_put_module; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci c->is_starving = 0; 113562306a36Sopenharmony_ci c->pipe0.num_buffers = c->cfg.num_buffers / 2; 113662306a36Sopenharmony_ci c->pipe1.num_buffers = c->cfg.num_buffers - c->pipe0.num_buffers; 113762306a36Sopenharmony_ci atomic_set(&c->mbo_ref, num_buffer); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ciout: 114062306a36Sopenharmony_ci if (comp == c->pipe0.comp) 114162306a36Sopenharmony_ci c->pipe0.refs++; 114262306a36Sopenharmony_ci if (comp == c->pipe1.comp) 114362306a36Sopenharmony_ci c->pipe1.refs++; 114462306a36Sopenharmony_ci mutex_unlock(&c->start_mutex); 114562306a36Sopenharmony_ci return 0; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_cierr_put_module: 114862306a36Sopenharmony_ci module_put(iface->mod); 114962306a36Sopenharmony_ci mutex_unlock(&c->start_mutex); 115062306a36Sopenharmony_ci return ret; 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(most_start_channel); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci/** 115562306a36Sopenharmony_ci * most_stop_channel - stops a running channel 115662306a36Sopenharmony_ci * @iface: pointer to interface instance 115762306a36Sopenharmony_ci * @id: channel ID 115862306a36Sopenharmony_ci * @comp: driver component 115962306a36Sopenharmony_ci */ 116062306a36Sopenharmony_ciint most_stop_channel(struct most_interface *iface, int id, 116162306a36Sopenharmony_ci struct most_component *comp) 116262306a36Sopenharmony_ci{ 116362306a36Sopenharmony_ci struct most_channel *c; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci if (unlikely((!iface) || (id >= iface->num_channels) || (id < 0))) { 116662306a36Sopenharmony_ci pr_err("Bad interface or index out of range\n"); 116762306a36Sopenharmony_ci return -EINVAL; 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci c = iface->p->channel[id]; 117062306a36Sopenharmony_ci if (unlikely(!c)) 117162306a36Sopenharmony_ci return -EINVAL; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci mutex_lock(&c->start_mutex); 117462306a36Sopenharmony_ci if (c->pipe0.refs + c->pipe1.refs >= 2) 117562306a36Sopenharmony_ci goto out; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (c->hdm_enqueue_task) 117862306a36Sopenharmony_ci kthread_stop(c->hdm_enqueue_task); 117962306a36Sopenharmony_ci c->hdm_enqueue_task = NULL; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci if (iface->mod) 118262306a36Sopenharmony_ci module_put(iface->mod); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci c->is_poisoned = true; 118562306a36Sopenharmony_ci if (c->iface->poison_channel(c->iface, c->channel_id)) { 118662306a36Sopenharmony_ci dev_err(&c->dev, "Failed to stop channel %d of interface %s\n", c->channel_id, 118762306a36Sopenharmony_ci c->iface->description); 118862306a36Sopenharmony_ci mutex_unlock(&c->start_mutex); 118962306a36Sopenharmony_ci return -EAGAIN; 119062306a36Sopenharmony_ci } 119162306a36Sopenharmony_ci flush_trash_fifo(c); 119262306a36Sopenharmony_ci flush_channel_fifos(c); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci#ifdef CMPL_INTERRUPTIBLE 119562306a36Sopenharmony_ci if (wait_for_completion_interruptible(&c->cleanup)) { 119662306a36Sopenharmony_ci dev_err(&c->dev, "Interrupted while cleaning up channel %d\n", c->channel_id); 119762306a36Sopenharmony_ci mutex_unlock(&c->start_mutex); 119862306a36Sopenharmony_ci return -EINTR; 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci#else 120162306a36Sopenharmony_ci wait_for_completion(&c->cleanup); 120262306a36Sopenharmony_ci#endif 120362306a36Sopenharmony_ci c->is_poisoned = false; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ciout: 120662306a36Sopenharmony_ci if (comp == c->pipe0.comp) 120762306a36Sopenharmony_ci c->pipe0.refs--; 120862306a36Sopenharmony_ci if (comp == c->pipe1.comp) 120962306a36Sopenharmony_ci c->pipe1.refs--; 121062306a36Sopenharmony_ci mutex_unlock(&c->start_mutex); 121162306a36Sopenharmony_ci return 0; 121262306a36Sopenharmony_ci} 121362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(most_stop_channel); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci/** 121662306a36Sopenharmony_ci * most_register_component - registers a driver component with the core 121762306a36Sopenharmony_ci * @comp: driver component 121862306a36Sopenharmony_ci */ 121962306a36Sopenharmony_ciint most_register_component(struct most_component *comp) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci if (!comp) { 122262306a36Sopenharmony_ci pr_err("Bad component\n"); 122362306a36Sopenharmony_ci return -EINVAL; 122462306a36Sopenharmony_ci } 122562306a36Sopenharmony_ci list_add_tail(&comp->list, &comp_list); 122662306a36Sopenharmony_ci return 0; 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(most_register_component); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistatic int disconnect_channels(struct device *dev, void *data) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci struct most_interface *iface; 123362306a36Sopenharmony_ci struct most_channel *c, *tmp; 123462306a36Sopenharmony_ci struct most_component *comp = data; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci iface = dev_get_drvdata(dev); 123762306a36Sopenharmony_ci list_for_each_entry_safe(c, tmp, &iface->p->channel_list, list) { 123862306a36Sopenharmony_ci if (c->pipe0.comp == comp || c->pipe1.comp == comp) 123962306a36Sopenharmony_ci comp->disconnect_channel(c->iface, c->channel_id); 124062306a36Sopenharmony_ci if (c->pipe0.comp == comp) 124162306a36Sopenharmony_ci c->pipe0.comp = NULL; 124262306a36Sopenharmony_ci if (c->pipe1.comp == comp) 124362306a36Sopenharmony_ci c->pipe1.comp = NULL; 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci return 0; 124662306a36Sopenharmony_ci} 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci/** 124962306a36Sopenharmony_ci * most_deregister_component - deregisters a driver component with the core 125062306a36Sopenharmony_ci * @comp: driver component 125162306a36Sopenharmony_ci */ 125262306a36Sopenharmony_ciint most_deregister_component(struct most_component *comp) 125362306a36Sopenharmony_ci{ 125462306a36Sopenharmony_ci if (!comp) { 125562306a36Sopenharmony_ci pr_err("Bad component\n"); 125662306a36Sopenharmony_ci return -EINVAL; 125762306a36Sopenharmony_ci } 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci bus_for_each_dev(&mostbus, NULL, comp, disconnect_channels); 126062306a36Sopenharmony_ci list_del(&comp->list); 126162306a36Sopenharmony_ci return 0; 126262306a36Sopenharmony_ci} 126362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(most_deregister_component); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_cistatic void release_channel(struct device *dev) 126662306a36Sopenharmony_ci{ 126762306a36Sopenharmony_ci struct most_channel *c = to_channel(dev); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci kfree(c); 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci/** 127362306a36Sopenharmony_ci * most_register_interface - registers an interface with core 127462306a36Sopenharmony_ci * @iface: device interface 127562306a36Sopenharmony_ci * 127662306a36Sopenharmony_ci * Allocates and initializes a new interface instance and all of its channels. 127762306a36Sopenharmony_ci * Returns a pointer to kobject or an error pointer. 127862306a36Sopenharmony_ci */ 127962306a36Sopenharmony_ciint most_register_interface(struct most_interface *iface) 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci unsigned int i; 128262306a36Sopenharmony_ci int id; 128362306a36Sopenharmony_ci struct most_channel *c; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci if (!iface || !iface->enqueue || !iface->configure || 128662306a36Sopenharmony_ci !iface->poison_channel || (iface->num_channels > MAX_CHANNELS)) 128762306a36Sopenharmony_ci return -EINVAL; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci id = ida_simple_get(&mdev_id, 0, 0, GFP_KERNEL); 129062306a36Sopenharmony_ci if (id < 0) { 129162306a36Sopenharmony_ci dev_err(iface->dev, "Failed to allocate device ID\n"); 129262306a36Sopenharmony_ci return id; 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci iface->p = kzalloc(sizeof(*iface->p), GFP_KERNEL); 129662306a36Sopenharmony_ci if (!iface->p) { 129762306a36Sopenharmony_ci ida_simple_remove(&mdev_id, id); 129862306a36Sopenharmony_ci return -ENOMEM; 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci INIT_LIST_HEAD(&iface->p->channel_list); 130262306a36Sopenharmony_ci iface->p->dev_id = id; 130362306a36Sopenharmony_ci strscpy(iface->p->name, iface->description, sizeof(iface->p->name)); 130462306a36Sopenharmony_ci iface->dev->bus = &mostbus; 130562306a36Sopenharmony_ci iface->dev->groups = interface_attr_groups; 130662306a36Sopenharmony_ci dev_set_drvdata(iface->dev, iface); 130762306a36Sopenharmony_ci if (device_register(iface->dev)) { 130862306a36Sopenharmony_ci dev_err(iface->dev, "Failed to register interface device\n"); 130962306a36Sopenharmony_ci kfree(iface->p); 131062306a36Sopenharmony_ci put_device(iface->dev); 131162306a36Sopenharmony_ci ida_simple_remove(&mdev_id, id); 131262306a36Sopenharmony_ci return -ENOMEM; 131362306a36Sopenharmony_ci } 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci for (i = 0; i < iface->num_channels; i++) { 131662306a36Sopenharmony_ci const char *name_suffix = iface->channel_vector[i].name_suffix; 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci c = kzalloc(sizeof(*c), GFP_KERNEL); 131962306a36Sopenharmony_ci if (!c) 132062306a36Sopenharmony_ci goto err_free_resources; 132162306a36Sopenharmony_ci if (!name_suffix) 132262306a36Sopenharmony_ci snprintf(c->name, STRING_SIZE, "ch%d", i); 132362306a36Sopenharmony_ci else 132462306a36Sopenharmony_ci snprintf(c->name, STRING_SIZE, "%s", name_suffix); 132562306a36Sopenharmony_ci c->dev.init_name = c->name; 132662306a36Sopenharmony_ci c->dev.parent = iface->dev; 132762306a36Sopenharmony_ci c->dev.groups = channel_attr_groups; 132862306a36Sopenharmony_ci c->dev.release = release_channel; 132962306a36Sopenharmony_ci iface->p->channel[i] = c; 133062306a36Sopenharmony_ci c->is_starving = 0; 133162306a36Sopenharmony_ci c->iface = iface; 133262306a36Sopenharmony_ci c->channel_id = i; 133362306a36Sopenharmony_ci c->keep_mbo = false; 133462306a36Sopenharmony_ci c->enqueue_halt = false; 133562306a36Sopenharmony_ci c->is_poisoned = false; 133662306a36Sopenharmony_ci c->cfg.direction = 0; 133762306a36Sopenharmony_ci c->cfg.data_type = 0; 133862306a36Sopenharmony_ci c->cfg.num_buffers = 0; 133962306a36Sopenharmony_ci c->cfg.buffer_size = 0; 134062306a36Sopenharmony_ci c->cfg.subbuffer_size = 0; 134162306a36Sopenharmony_ci c->cfg.packets_per_xact = 0; 134262306a36Sopenharmony_ci spin_lock_init(&c->fifo_lock); 134362306a36Sopenharmony_ci INIT_LIST_HEAD(&c->fifo); 134462306a36Sopenharmony_ci INIT_LIST_HEAD(&c->trash_fifo); 134562306a36Sopenharmony_ci INIT_LIST_HEAD(&c->halt_fifo); 134662306a36Sopenharmony_ci init_completion(&c->cleanup); 134762306a36Sopenharmony_ci atomic_set(&c->mbo_ref, 0); 134862306a36Sopenharmony_ci mutex_init(&c->start_mutex); 134962306a36Sopenharmony_ci mutex_init(&c->nq_mutex); 135062306a36Sopenharmony_ci list_add_tail(&c->list, &iface->p->channel_list); 135162306a36Sopenharmony_ci if (device_register(&c->dev)) { 135262306a36Sopenharmony_ci dev_err(&c->dev, "Failed to register channel device\n"); 135362306a36Sopenharmony_ci goto err_free_most_channel; 135462306a36Sopenharmony_ci } 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci most_interface_register_notify(iface->description); 135762306a36Sopenharmony_ci return 0; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_cierr_free_most_channel: 136062306a36Sopenharmony_ci put_device(&c->dev); 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_cierr_free_resources: 136362306a36Sopenharmony_ci while (i > 0) { 136462306a36Sopenharmony_ci c = iface->p->channel[--i]; 136562306a36Sopenharmony_ci device_unregister(&c->dev); 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci kfree(iface->p); 136862306a36Sopenharmony_ci device_unregister(iface->dev); 136962306a36Sopenharmony_ci ida_simple_remove(&mdev_id, id); 137062306a36Sopenharmony_ci return -ENOMEM; 137162306a36Sopenharmony_ci} 137262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(most_register_interface); 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci/** 137562306a36Sopenharmony_ci * most_deregister_interface - deregisters an interface with core 137662306a36Sopenharmony_ci * @iface: device interface 137762306a36Sopenharmony_ci * 137862306a36Sopenharmony_ci * Before removing an interface instance from the list, all running 137962306a36Sopenharmony_ci * channels are stopped and poisoned. 138062306a36Sopenharmony_ci */ 138162306a36Sopenharmony_civoid most_deregister_interface(struct most_interface *iface) 138262306a36Sopenharmony_ci{ 138362306a36Sopenharmony_ci int i; 138462306a36Sopenharmony_ci struct most_channel *c; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci for (i = 0; i < iface->num_channels; i++) { 138762306a36Sopenharmony_ci c = iface->p->channel[i]; 138862306a36Sopenharmony_ci if (c->pipe0.comp) 138962306a36Sopenharmony_ci c->pipe0.comp->disconnect_channel(c->iface, 139062306a36Sopenharmony_ci c->channel_id); 139162306a36Sopenharmony_ci if (c->pipe1.comp) 139262306a36Sopenharmony_ci c->pipe1.comp->disconnect_channel(c->iface, 139362306a36Sopenharmony_ci c->channel_id); 139462306a36Sopenharmony_ci c->pipe0.comp = NULL; 139562306a36Sopenharmony_ci c->pipe1.comp = NULL; 139662306a36Sopenharmony_ci list_del(&c->list); 139762306a36Sopenharmony_ci device_unregister(&c->dev); 139862306a36Sopenharmony_ci } 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci ida_simple_remove(&mdev_id, iface->p->dev_id); 140162306a36Sopenharmony_ci kfree(iface->p); 140262306a36Sopenharmony_ci device_unregister(iface->dev); 140362306a36Sopenharmony_ci} 140462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(most_deregister_interface); 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci/** 140762306a36Sopenharmony_ci * most_stop_enqueue - prevents core from enqueueing MBOs 140862306a36Sopenharmony_ci * @iface: pointer to interface 140962306a36Sopenharmony_ci * @id: channel id 141062306a36Sopenharmony_ci * 141162306a36Sopenharmony_ci * This is called by an HDM that _cannot_ attend to its duties and 141262306a36Sopenharmony_ci * is imminent to get run over by the core. The core is not going to 141362306a36Sopenharmony_ci * enqueue any further packets unless the flagging HDM calls 141462306a36Sopenharmony_ci * most_resume enqueue(). 141562306a36Sopenharmony_ci */ 141662306a36Sopenharmony_civoid most_stop_enqueue(struct most_interface *iface, int id) 141762306a36Sopenharmony_ci{ 141862306a36Sopenharmony_ci struct most_channel *c = iface->p->channel[id]; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci if (!c) 142162306a36Sopenharmony_ci return; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci mutex_lock(&c->nq_mutex); 142462306a36Sopenharmony_ci c->enqueue_halt = true; 142562306a36Sopenharmony_ci mutex_unlock(&c->nq_mutex); 142662306a36Sopenharmony_ci} 142762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(most_stop_enqueue); 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci/** 143062306a36Sopenharmony_ci * most_resume_enqueue - allow core to enqueue MBOs again 143162306a36Sopenharmony_ci * @iface: pointer to interface 143262306a36Sopenharmony_ci * @id: channel id 143362306a36Sopenharmony_ci * 143462306a36Sopenharmony_ci * This clears the enqueue halt flag and enqueues all MBOs currently 143562306a36Sopenharmony_ci * sitting in the wait fifo. 143662306a36Sopenharmony_ci */ 143762306a36Sopenharmony_civoid most_resume_enqueue(struct most_interface *iface, int id) 143862306a36Sopenharmony_ci{ 143962306a36Sopenharmony_ci struct most_channel *c = iface->p->channel[id]; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci if (!c) 144262306a36Sopenharmony_ci return; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci mutex_lock(&c->nq_mutex); 144562306a36Sopenharmony_ci c->enqueue_halt = false; 144662306a36Sopenharmony_ci mutex_unlock(&c->nq_mutex); 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci wake_up_interruptible(&c->hdm_fifo_wq); 144962306a36Sopenharmony_ci} 145062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(most_resume_enqueue); 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_cistatic int __init most_init(void) 145362306a36Sopenharmony_ci{ 145462306a36Sopenharmony_ci int err; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci INIT_LIST_HEAD(&comp_list); 145762306a36Sopenharmony_ci ida_init(&mdev_id); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci err = bus_register(&mostbus); 146062306a36Sopenharmony_ci if (err) { 146162306a36Sopenharmony_ci pr_err("Failed to register most bus\n"); 146262306a36Sopenharmony_ci return err; 146362306a36Sopenharmony_ci } 146462306a36Sopenharmony_ci err = driver_register(&mostbus_driver); 146562306a36Sopenharmony_ci if (err) { 146662306a36Sopenharmony_ci pr_err("Failed to register core driver\n"); 146762306a36Sopenharmony_ci goto err_unregister_bus; 146862306a36Sopenharmony_ci } 146962306a36Sopenharmony_ci configfs_init(); 147062306a36Sopenharmony_ci return 0; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_cierr_unregister_bus: 147362306a36Sopenharmony_ci bus_unregister(&mostbus); 147462306a36Sopenharmony_ci return err; 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_cistatic void __exit most_exit(void) 147862306a36Sopenharmony_ci{ 147962306a36Sopenharmony_ci driver_unregister(&mostbus_driver); 148062306a36Sopenharmony_ci bus_unregister(&mostbus); 148162306a36Sopenharmony_ci ida_destroy(&mdev_id); 148262306a36Sopenharmony_ci} 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_cisubsys_initcall(most_init); 148562306a36Sopenharmony_cimodule_exit(most_exit); 148662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 148762306a36Sopenharmony_ciMODULE_AUTHOR("Christian Gromm <christian.gromm@microchip.com>"); 148862306a36Sopenharmony_ciMODULE_DESCRIPTION("Core module of stacked MOST Linux driver"); 1489