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