162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Keystone Queue Manager subsystem driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
662306a36Sopenharmony_ci * Authors:	Sandeep Nair <sandeep_n@ti.com>
762306a36Sopenharmony_ci *		Cyril Chemparathy <cyril@ti.com>
862306a36Sopenharmony_ci *		Santosh Shilimkar <santosh.shilimkar@ti.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/debugfs.h>
1262306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1362306a36Sopenharmony_ci#include <linux/firmware.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/of_address.h>
1862306a36Sopenharmony_ci#include <linux/of_device.h>
1962306a36Sopenharmony_ci#include <linux/of_irq.h>
2062306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/soc/ti/knav_qmss.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "knav_qmss.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic struct knav_device *kdev;
2762306a36Sopenharmony_cistatic DEFINE_MUTEX(knav_dev_lock);
2862306a36Sopenharmony_ci#define knav_dev_lock_held() \
2962306a36Sopenharmony_ci	lockdep_is_held(&knav_dev_lock)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* Queue manager register indices in DTS */
3262306a36Sopenharmony_ci#define KNAV_QUEUE_PEEK_REG_INDEX	0
3362306a36Sopenharmony_ci#define KNAV_QUEUE_STATUS_REG_INDEX	1
3462306a36Sopenharmony_ci#define KNAV_QUEUE_CONFIG_REG_INDEX	2
3562306a36Sopenharmony_ci#define KNAV_QUEUE_REGION_REG_INDEX	3
3662306a36Sopenharmony_ci#define KNAV_QUEUE_PUSH_REG_INDEX	4
3762306a36Sopenharmony_ci#define KNAV_QUEUE_POP_REG_INDEX	5
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* Queue manager register indices in DTS for QMSS in K2G NAVSS.
4062306a36Sopenharmony_ci * There are no status and vbusm push registers on this version
4162306a36Sopenharmony_ci * of QMSS. Push registers are same as pop, So all indices above 1
4262306a36Sopenharmony_ci * are to be re-defined
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci#define KNAV_L_QUEUE_CONFIG_REG_INDEX	1
4562306a36Sopenharmony_ci#define KNAV_L_QUEUE_REGION_REG_INDEX	2
4662306a36Sopenharmony_ci#define KNAV_L_QUEUE_PUSH_REG_INDEX	3
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* PDSP register indices in DTS */
4962306a36Sopenharmony_ci#define KNAV_QUEUE_PDSP_IRAM_REG_INDEX	0
5062306a36Sopenharmony_ci#define KNAV_QUEUE_PDSP_REGS_REG_INDEX	1
5162306a36Sopenharmony_ci#define KNAV_QUEUE_PDSP_INTD_REG_INDEX	2
5262306a36Sopenharmony_ci#define KNAV_QUEUE_PDSP_CMD_REG_INDEX	3
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define knav_queue_idx_to_inst(kdev, idx)			\
5562306a36Sopenharmony_ci	(kdev->instances + (idx << kdev->inst_shift))
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define for_each_handle_rcu(qh, inst)				\
5862306a36Sopenharmony_ci	list_for_each_entry_rcu(qh, &inst->handles, list,	\
5962306a36Sopenharmony_ci				knav_dev_lock_held())
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define for_each_instance(idx, inst, kdev)		\
6262306a36Sopenharmony_ci	for (idx = 0, inst = kdev->instances;		\
6362306a36Sopenharmony_ci	     idx < (kdev)->num_queues_in_use;			\
6462306a36Sopenharmony_ci	     idx++, inst = knav_queue_idx_to_inst(kdev, idx))
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* All firmware file names end up here. List the firmware file names below.
6762306a36Sopenharmony_ci * Newest followed by older ones. Search is done from start of the array
6862306a36Sopenharmony_ci * until a firmware file is found.
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_cistatic const char * const knav_acc_firmwares[] = {"ks2_qmss_pdsp_acc48.bin"};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic bool device_ready;
7362306a36Sopenharmony_cibool knav_qmss_device_ready(void)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	return device_ready;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_qmss_device_ready);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/**
8062306a36Sopenharmony_ci * knav_queue_notify: qmss queue notfier call
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * @inst:		- qmss queue instance like accumulator
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_civoid knav_queue_notify(struct knav_queue_inst *inst)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct knav_queue *qh;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (!inst)
8962306a36Sopenharmony_ci		return;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	rcu_read_lock();
9262306a36Sopenharmony_ci	for_each_handle_rcu(qh, inst) {
9362306a36Sopenharmony_ci		if (atomic_read(&qh->notifier_enabled) <= 0)
9462306a36Sopenharmony_ci			continue;
9562306a36Sopenharmony_ci		if (WARN_ON(!qh->notifier_fn))
9662306a36Sopenharmony_ci			continue;
9762306a36Sopenharmony_ci		this_cpu_inc(qh->stats->notifies);
9862306a36Sopenharmony_ci		qh->notifier_fn(qh->notifier_fn_arg);
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci	rcu_read_unlock();
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_queue_notify);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic irqreturn_t knav_queue_int_handler(int irq, void *_instdata)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct knav_queue_inst *inst = _instdata;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	knav_queue_notify(inst);
10962306a36Sopenharmony_ci	return IRQ_HANDLED;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int knav_queue_setup_irq(struct knav_range_info *range,
11362306a36Sopenharmony_ci			  struct knav_queue_inst *inst)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	unsigned queue = inst->id - range->queue_base;
11662306a36Sopenharmony_ci	int ret = 0, irq;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (range->flags & RANGE_HAS_IRQ) {
11962306a36Sopenharmony_ci		irq = range->irqs[queue].irq;
12062306a36Sopenharmony_ci		ret = request_irq(irq, knav_queue_int_handler, 0,
12162306a36Sopenharmony_ci					inst->irq_name, inst);
12262306a36Sopenharmony_ci		if (ret)
12362306a36Sopenharmony_ci			return ret;
12462306a36Sopenharmony_ci		disable_irq(irq);
12562306a36Sopenharmony_ci		if (range->irqs[queue].cpu_mask) {
12662306a36Sopenharmony_ci			ret = irq_set_affinity_hint(irq, range->irqs[queue].cpu_mask);
12762306a36Sopenharmony_ci			if (ret) {
12862306a36Sopenharmony_ci				dev_warn(range->kdev->dev,
12962306a36Sopenharmony_ci					 "Failed to set IRQ affinity\n");
13062306a36Sopenharmony_ci				return ret;
13162306a36Sopenharmony_ci			}
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci	return ret;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic void knav_queue_free_irq(struct knav_queue_inst *inst)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct knav_range_info *range = inst->range;
14062306a36Sopenharmony_ci	unsigned queue = inst->id - inst->range->queue_base;
14162306a36Sopenharmony_ci	int irq;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (range->flags & RANGE_HAS_IRQ) {
14462306a36Sopenharmony_ci		irq = range->irqs[queue].irq;
14562306a36Sopenharmony_ci		irq_set_affinity_hint(irq, NULL);
14662306a36Sopenharmony_ci		free_irq(irq, inst);
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic inline bool knav_queue_is_busy(struct knav_queue_inst *inst)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	return !list_empty(&inst->handles);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic inline bool knav_queue_is_reserved(struct knav_queue_inst *inst)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	return inst->range->flags & RANGE_RESERVED;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic inline bool knav_queue_is_shared(struct knav_queue_inst *inst)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct knav_queue *tmp;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	rcu_read_lock();
16562306a36Sopenharmony_ci	for_each_handle_rcu(tmp, inst) {
16662306a36Sopenharmony_ci		if (tmp->flags & KNAV_QUEUE_SHARED) {
16762306a36Sopenharmony_ci			rcu_read_unlock();
16862306a36Sopenharmony_ci			return true;
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci	rcu_read_unlock();
17262306a36Sopenharmony_ci	return false;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic inline bool knav_queue_match_type(struct knav_queue_inst *inst,
17662306a36Sopenharmony_ci						unsigned type)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	if ((type == KNAV_QUEUE_QPEND) &&
17962306a36Sopenharmony_ci	    (inst->range->flags & RANGE_HAS_IRQ)) {
18062306a36Sopenharmony_ci		return true;
18162306a36Sopenharmony_ci	} else if ((type == KNAV_QUEUE_ACC) &&
18262306a36Sopenharmony_ci		(inst->range->flags & RANGE_HAS_ACCUMULATOR)) {
18362306a36Sopenharmony_ci		return true;
18462306a36Sopenharmony_ci	} else if ((type == KNAV_QUEUE_GP) &&
18562306a36Sopenharmony_ci		!(inst->range->flags &
18662306a36Sopenharmony_ci			(RANGE_HAS_ACCUMULATOR | RANGE_HAS_IRQ))) {
18762306a36Sopenharmony_ci		return true;
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci	return false;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic inline struct knav_queue_inst *
19362306a36Sopenharmony_ciknav_queue_match_id_to_inst(struct knav_device *kdev, unsigned id)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct knav_queue_inst *inst;
19662306a36Sopenharmony_ci	int idx;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	for_each_instance(idx, inst, kdev) {
19962306a36Sopenharmony_ci		if (inst->id == id)
20062306a36Sopenharmony_ci			return inst;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	return NULL;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic inline struct knav_queue_inst *knav_queue_find_by_id(int id)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	if (kdev->base_id <= id &&
20862306a36Sopenharmony_ci	    kdev->base_id + kdev->num_queues > id) {
20962306a36Sopenharmony_ci		id -= kdev->base_id;
21062306a36Sopenharmony_ci		return knav_queue_match_id_to_inst(kdev, id);
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci	return NULL;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic struct knav_queue *__knav_queue_open(struct knav_queue_inst *inst,
21662306a36Sopenharmony_ci				      const char *name, unsigned flags)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	struct knav_queue *qh;
21962306a36Sopenharmony_ci	unsigned id;
22062306a36Sopenharmony_ci	int ret = 0;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	qh = devm_kzalloc(inst->kdev->dev, sizeof(*qh), GFP_KERNEL);
22362306a36Sopenharmony_ci	if (!qh)
22462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	qh->stats = alloc_percpu(struct knav_queue_stats);
22762306a36Sopenharmony_ci	if (!qh->stats) {
22862306a36Sopenharmony_ci		ret = -ENOMEM;
22962306a36Sopenharmony_ci		goto err;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	qh->flags = flags;
23362306a36Sopenharmony_ci	qh->inst = inst;
23462306a36Sopenharmony_ci	id = inst->id - inst->qmgr->start_queue;
23562306a36Sopenharmony_ci	qh->reg_push = &inst->qmgr->reg_push[id];
23662306a36Sopenharmony_ci	qh->reg_pop = &inst->qmgr->reg_pop[id];
23762306a36Sopenharmony_ci	qh->reg_peek = &inst->qmgr->reg_peek[id];
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/* first opener? */
24062306a36Sopenharmony_ci	if (!knav_queue_is_busy(inst)) {
24162306a36Sopenharmony_ci		struct knav_range_info *range = inst->range;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		inst->name = kstrndup(name, KNAV_NAME_SIZE - 1, GFP_KERNEL);
24462306a36Sopenharmony_ci		if (range->ops && range->ops->open_queue)
24562306a36Sopenharmony_ci			ret = range->ops->open_queue(range, inst, flags);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		if (ret)
24862306a36Sopenharmony_ci			goto err;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci	list_add_tail_rcu(&qh->list, &inst->handles);
25162306a36Sopenharmony_ci	return qh;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cierr:
25462306a36Sopenharmony_ci	if (qh->stats)
25562306a36Sopenharmony_ci		free_percpu(qh->stats);
25662306a36Sopenharmony_ci	devm_kfree(inst->kdev->dev, qh);
25762306a36Sopenharmony_ci	return ERR_PTR(ret);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic struct knav_queue *
26162306a36Sopenharmony_ciknav_queue_open_by_id(const char *name, unsigned id, unsigned flags)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct knav_queue_inst *inst;
26462306a36Sopenharmony_ci	struct knav_queue *qh;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	mutex_lock(&knav_dev_lock);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	qh = ERR_PTR(-ENODEV);
26962306a36Sopenharmony_ci	inst = knav_queue_find_by_id(id);
27062306a36Sopenharmony_ci	if (!inst)
27162306a36Sopenharmony_ci		goto unlock_ret;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	qh = ERR_PTR(-EEXIST);
27462306a36Sopenharmony_ci	if (!(flags & KNAV_QUEUE_SHARED) && knav_queue_is_busy(inst))
27562306a36Sopenharmony_ci		goto unlock_ret;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	qh = ERR_PTR(-EBUSY);
27862306a36Sopenharmony_ci	if ((flags & KNAV_QUEUE_SHARED) &&
27962306a36Sopenharmony_ci	    (knav_queue_is_busy(inst) && !knav_queue_is_shared(inst)))
28062306a36Sopenharmony_ci		goto unlock_ret;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	qh = __knav_queue_open(inst, name, flags);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ciunlock_ret:
28562306a36Sopenharmony_ci	mutex_unlock(&knav_dev_lock);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return qh;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic struct knav_queue *knav_queue_open_by_type(const char *name,
29162306a36Sopenharmony_ci						unsigned type, unsigned flags)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct knav_queue_inst *inst;
29462306a36Sopenharmony_ci	struct knav_queue *qh = ERR_PTR(-EINVAL);
29562306a36Sopenharmony_ci	int idx;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	mutex_lock(&knav_dev_lock);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	for_each_instance(idx, inst, kdev) {
30062306a36Sopenharmony_ci		if (knav_queue_is_reserved(inst))
30162306a36Sopenharmony_ci			continue;
30262306a36Sopenharmony_ci		if (!knav_queue_match_type(inst, type))
30362306a36Sopenharmony_ci			continue;
30462306a36Sopenharmony_ci		if (knav_queue_is_busy(inst))
30562306a36Sopenharmony_ci			continue;
30662306a36Sopenharmony_ci		qh = __knav_queue_open(inst, name, flags);
30762306a36Sopenharmony_ci		goto unlock_ret;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ciunlock_ret:
31162306a36Sopenharmony_ci	mutex_unlock(&knav_dev_lock);
31262306a36Sopenharmony_ci	return qh;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic void knav_queue_set_notify(struct knav_queue_inst *inst, bool enabled)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct knav_range_info *range = inst->range;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (range->ops && range->ops->set_notify)
32062306a36Sopenharmony_ci		range->ops->set_notify(range, inst, enabled);
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic int knav_queue_enable_notifier(struct knav_queue *qh)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct knav_queue_inst *inst = qh->inst;
32662306a36Sopenharmony_ci	bool first;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (WARN_ON(!qh->notifier_fn))
32962306a36Sopenharmony_ci		return -EINVAL;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	/* Adjust the per handle notifier count */
33262306a36Sopenharmony_ci	first = (atomic_inc_return(&qh->notifier_enabled) == 1);
33362306a36Sopenharmony_ci	if (!first)
33462306a36Sopenharmony_ci		return 0; /* nothing to do */
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* Now adjust the per instance notifier count */
33762306a36Sopenharmony_ci	first = (atomic_inc_return(&inst->num_notifiers) == 1);
33862306a36Sopenharmony_ci	if (first)
33962306a36Sopenharmony_ci		knav_queue_set_notify(inst, true);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	return 0;
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic int knav_queue_disable_notifier(struct knav_queue *qh)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct knav_queue_inst *inst = qh->inst;
34762306a36Sopenharmony_ci	bool last;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	last = (atomic_dec_return(&qh->notifier_enabled) == 0);
35062306a36Sopenharmony_ci	if (!last)
35162306a36Sopenharmony_ci		return 0; /* nothing to do */
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	last = (atomic_dec_return(&inst->num_notifiers) == 0);
35462306a36Sopenharmony_ci	if (last)
35562306a36Sopenharmony_ci		knav_queue_set_notify(inst, false);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return 0;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic int knav_queue_set_notifier(struct knav_queue *qh,
36162306a36Sopenharmony_ci				struct knav_queue_notify_config *cfg)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	knav_queue_notify_fn old_fn = qh->notifier_fn;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (!cfg)
36662306a36Sopenharmony_ci		return -EINVAL;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (!(qh->inst->range->flags & (RANGE_HAS_ACCUMULATOR | RANGE_HAS_IRQ)))
36962306a36Sopenharmony_ci		return -ENOTSUPP;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (!cfg->fn && old_fn)
37262306a36Sopenharmony_ci		knav_queue_disable_notifier(qh);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	qh->notifier_fn = cfg->fn;
37562306a36Sopenharmony_ci	qh->notifier_fn_arg = cfg->fn_arg;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (cfg->fn && !old_fn)
37862306a36Sopenharmony_ci		knav_queue_enable_notifier(qh);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return 0;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic int knav_gp_set_notify(struct knav_range_info *range,
38462306a36Sopenharmony_ci			       struct knav_queue_inst *inst,
38562306a36Sopenharmony_ci			       bool enabled)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	unsigned queue;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (range->flags & RANGE_HAS_IRQ) {
39062306a36Sopenharmony_ci		queue = inst->id - range->queue_base;
39162306a36Sopenharmony_ci		if (enabled)
39262306a36Sopenharmony_ci			enable_irq(range->irqs[queue].irq);
39362306a36Sopenharmony_ci		else
39462306a36Sopenharmony_ci			disable_irq_nosync(range->irqs[queue].irq);
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci	return 0;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic int knav_gp_open_queue(struct knav_range_info *range,
40062306a36Sopenharmony_ci				struct knav_queue_inst *inst, unsigned flags)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	return knav_queue_setup_irq(range, inst);
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic int knav_gp_close_queue(struct knav_range_info *range,
40662306a36Sopenharmony_ci				struct knav_queue_inst *inst)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	knav_queue_free_irq(inst);
40962306a36Sopenharmony_ci	return 0;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic struct knav_range_ops knav_gp_range_ops = {
41362306a36Sopenharmony_ci	.set_notify	= knav_gp_set_notify,
41462306a36Sopenharmony_ci	.open_queue	= knav_gp_open_queue,
41562306a36Sopenharmony_ci	.close_queue	= knav_gp_close_queue,
41662306a36Sopenharmony_ci};
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic int knav_queue_get_count(void *qhandle)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	struct knav_queue *qh = qhandle;
42262306a36Sopenharmony_ci	struct knav_queue_inst *inst = qh->inst;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	return readl_relaxed(&qh->reg_peek[0].entry_count) +
42562306a36Sopenharmony_ci		atomic_read(&inst->desc_count);
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic void knav_queue_debug_show_instance(struct seq_file *s,
42962306a36Sopenharmony_ci					struct knav_queue_inst *inst)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	struct knav_device *kdev = inst->kdev;
43262306a36Sopenharmony_ci	struct knav_queue *qh;
43362306a36Sopenharmony_ci	int cpu = 0;
43462306a36Sopenharmony_ci	int pushes = 0;
43562306a36Sopenharmony_ci	int pops = 0;
43662306a36Sopenharmony_ci	int push_errors = 0;
43762306a36Sopenharmony_ci	int pop_errors = 0;
43862306a36Sopenharmony_ci	int notifies = 0;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (!knav_queue_is_busy(inst))
44162306a36Sopenharmony_ci		return;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	seq_printf(s, "\tqueue id %d (%s)\n",
44462306a36Sopenharmony_ci		   kdev->base_id + inst->id, inst->name);
44562306a36Sopenharmony_ci	for_each_handle_rcu(qh, inst) {
44662306a36Sopenharmony_ci		for_each_possible_cpu(cpu) {
44762306a36Sopenharmony_ci			pushes += per_cpu_ptr(qh->stats, cpu)->pushes;
44862306a36Sopenharmony_ci			pops += per_cpu_ptr(qh->stats, cpu)->pops;
44962306a36Sopenharmony_ci			push_errors += per_cpu_ptr(qh->stats, cpu)->push_errors;
45062306a36Sopenharmony_ci			pop_errors += per_cpu_ptr(qh->stats, cpu)->pop_errors;
45162306a36Sopenharmony_ci			notifies += per_cpu_ptr(qh->stats, cpu)->notifies;
45262306a36Sopenharmony_ci		}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		seq_printf(s, "\t\thandle %p: pushes %8d, pops %8d, count %8d, notifies %8d, push errors %8d, pop errors %8d\n",
45562306a36Sopenharmony_ci				qh,
45662306a36Sopenharmony_ci				pushes,
45762306a36Sopenharmony_ci				pops,
45862306a36Sopenharmony_ci				knav_queue_get_count(qh),
45962306a36Sopenharmony_ci				notifies,
46062306a36Sopenharmony_ci				push_errors,
46162306a36Sopenharmony_ci				pop_errors);
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic int knav_queue_debug_show(struct seq_file *s, void *v)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct knav_queue_inst *inst;
46862306a36Sopenharmony_ci	int idx;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	mutex_lock(&knav_dev_lock);
47162306a36Sopenharmony_ci	seq_printf(s, "%s: %u-%u\n",
47262306a36Sopenharmony_ci		   dev_name(kdev->dev), kdev->base_id,
47362306a36Sopenharmony_ci		   kdev->base_id + kdev->num_queues - 1);
47462306a36Sopenharmony_ci	for_each_instance(idx, inst, kdev)
47562306a36Sopenharmony_ci		knav_queue_debug_show_instance(s, inst);
47662306a36Sopenharmony_ci	mutex_unlock(&knav_dev_lock);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	return 0;
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(knav_queue_debug);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic inline int knav_queue_pdsp_wait(u32 * __iomem addr, unsigned timeout,
48462306a36Sopenharmony_ci					u32 flags)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	unsigned long end;
48762306a36Sopenharmony_ci	u32 val = 0;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	end = jiffies + msecs_to_jiffies(timeout);
49062306a36Sopenharmony_ci	while (time_after(end, jiffies)) {
49162306a36Sopenharmony_ci		val = readl_relaxed(addr);
49262306a36Sopenharmony_ci		if (flags)
49362306a36Sopenharmony_ci			val &= flags;
49462306a36Sopenharmony_ci		if (!val)
49562306a36Sopenharmony_ci			break;
49662306a36Sopenharmony_ci		cpu_relax();
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci	return val ? -ETIMEDOUT : 0;
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic int knav_queue_flush(struct knav_queue *qh)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct knav_queue_inst *inst = qh->inst;
50562306a36Sopenharmony_ci	unsigned id = inst->id - inst->qmgr->start_queue;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	atomic_set(&inst->desc_count, 0);
50862306a36Sopenharmony_ci	writel_relaxed(0, &inst->qmgr->reg_push[id].ptr_size_thresh);
50962306a36Sopenharmony_ci	return 0;
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci/**
51362306a36Sopenharmony_ci * knav_queue_open()	- open a hardware queue
51462306a36Sopenharmony_ci * @name:		- name to give the queue handle
51562306a36Sopenharmony_ci * @id:			- desired queue number if any or specifes the type
51662306a36Sopenharmony_ci *			  of queue
51762306a36Sopenharmony_ci * @flags:		- the following flags are applicable to queues:
51862306a36Sopenharmony_ci *	KNAV_QUEUE_SHARED - allow the queue to be shared. Queues are
51962306a36Sopenharmony_ci *			     exclusive by default.
52062306a36Sopenharmony_ci *			     Subsequent attempts to open a shared queue should
52162306a36Sopenharmony_ci *			     also have this flag.
52262306a36Sopenharmony_ci *
52362306a36Sopenharmony_ci * Returns a handle to the open hardware queue if successful. Use IS_ERR()
52462306a36Sopenharmony_ci * to check the returned value for error codes.
52562306a36Sopenharmony_ci */
52662306a36Sopenharmony_civoid *knav_queue_open(const char *name, unsigned id,
52762306a36Sopenharmony_ci					unsigned flags)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	struct knav_queue *qh = ERR_PTR(-EINVAL);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	switch (id) {
53262306a36Sopenharmony_ci	case KNAV_QUEUE_QPEND:
53362306a36Sopenharmony_ci	case KNAV_QUEUE_ACC:
53462306a36Sopenharmony_ci	case KNAV_QUEUE_GP:
53562306a36Sopenharmony_ci		qh = knav_queue_open_by_type(name, id, flags);
53662306a36Sopenharmony_ci		break;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	default:
53962306a36Sopenharmony_ci		qh = knav_queue_open_by_id(name, id, flags);
54062306a36Sopenharmony_ci		break;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci	return qh;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_queue_open);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci/**
54762306a36Sopenharmony_ci * knav_queue_close()	- close a hardware queue handle
54862306a36Sopenharmony_ci * @qhandle:		- handle to close
54962306a36Sopenharmony_ci */
55062306a36Sopenharmony_civoid knav_queue_close(void *qhandle)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	struct knav_queue *qh = qhandle;
55362306a36Sopenharmony_ci	struct knav_queue_inst *inst = qh->inst;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	while (atomic_read(&qh->notifier_enabled) > 0)
55662306a36Sopenharmony_ci		knav_queue_disable_notifier(qh);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	mutex_lock(&knav_dev_lock);
55962306a36Sopenharmony_ci	list_del_rcu(&qh->list);
56062306a36Sopenharmony_ci	mutex_unlock(&knav_dev_lock);
56162306a36Sopenharmony_ci	synchronize_rcu();
56262306a36Sopenharmony_ci	if (!knav_queue_is_busy(inst)) {
56362306a36Sopenharmony_ci		struct knav_range_info *range = inst->range;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci		if (range->ops && range->ops->close_queue)
56662306a36Sopenharmony_ci			range->ops->close_queue(range, inst);
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci	free_percpu(qh->stats);
56962306a36Sopenharmony_ci	devm_kfree(inst->kdev->dev, qh);
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_queue_close);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci/**
57462306a36Sopenharmony_ci * knav_queue_device_control()	- Perform control operations on a queue
57562306a36Sopenharmony_ci * @qhandle:			- queue handle
57662306a36Sopenharmony_ci * @cmd:			- control commands
57762306a36Sopenharmony_ci * @arg:			- command argument
57862306a36Sopenharmony_ci *
57962306a36Sopenharmony_ci * Returns 0 on success, errno otherwise.
58062306a36Sopenharmony_ci */
58162306a36Sopenharmony_ciint knav_queue_device_control(void *qhandle, enum knav_queue_ctrl_cmd cmd,
58262306a36Sopenharmony_ci				unsigned long arg)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	struct knav_queue *qh = qhandle;
58562306a36Sopenharmony_ci	struct knav_queue_notify_config *cfg;
58662306a36Sopenharmony_ci	int ret;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	switch ((int)cmd) {
58962306a36Sopenharmony_ci	case KNAV_QUEUE_GET_ID:
59062306a36Sopenharmony_ci		ret = qh->inst->kdev->base_id + qh->inst->id;
59162306a36Sopenharmony_ci		break;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	case KNAV_QUEUE_FLUSH:
59462306a36Sopenharmony_ci		ret = knav_queue_flush(qh);
59562306a36Sopenharmony_ci		break;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	case KNAV_QUEUE_SET_NOTIFIER:
59862306a36Sopenharmony_ci		cfg = (void *)arg;
59962306a36Sopenharmony_ci		ret = knav_queue_set_notifier(qh, cfg);
60062306a36Sopenharmony_ci		break;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	case KNAV_QUEUE_ENABLE_NOTIFY:
60362306a36Sopenharmony_ci		ret = knav_queue_enable_notifier(qh);
60462306a36Sopenharmony_ci		break;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	case KNAV_QUEUE_DISABLE_NOTIFY:
60762306a36Sopenharmony_ci		ret = knav_queue_disable_notifier(qh);
60862306a36Sopenharmony_ci		break;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	case KNAV_QUEUE_GET_COUNT:
61162306a36Sopenharmony_ci		ret = knav_queue_get_count(qh);
61262306a36Sopenharmony_ci		break;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	default:
61562306a36Sopenharmony_ci		ret = -ENOTSUPP;
61662306a36Sopenharmony_ci		break;
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci	return ret;
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_queue_device_control);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci/**
62562306a36Sopenharmony_ci * knav_queue_push()	- push data (or descriptor) to the tail of a queue
62662306a36Sopenharmony_ci * @qhandle:		- hardware queue handle
62762306a36Sopenharmony_ci * @dma:		- DMA data to push
62862306a36Sopenharmony_ci * @size:		- size of data to push
62962306a36Sopenharmony_ci * @flags:		- can be used to pass additional information
63062306a36Sopenharmony_ci *
63162306a36Sopenharmony_ci * Returns 0 on success, errno otherwise.
63262306a36Sopenharmony_ci */
63362306a36Sopenharmony_ciint knav_queue_push(void *qhandle, dma_addr_t dma,
63462306a36Sopenharmony_ci					unsigned size, unsigned flags)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	struct knav_queue *qh = qhandle;
63762306a36Sopenharmony_ci	u32 val;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	val = (u32)dma | ((size / 16) - 1);
64062306a36Sopenharmony_ci	writel_relaxed(val, &qh->reg_push[0].ptr_size_thresh);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	this_cpu_inc(qh->stats->pushes);
64362306a36Sopenharmony_ci	return 0;
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_queue_push);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci/**
64862306a36Sopenharmony_ci * knav_queue_pop()	- pop data (or descriptor) from the head of a queue
64962306a36Sopenharmony_ci * @qhandle:		- hardware queue handle
65062306a36Sopenharmony_ci * @size:		- (optional) size of the data pop'ed.
65162306a36Sopenharmony_ci *
65262306a36Sopenharmony_ci * Returns a DMA address on success, 0 on failure.
65362306a36Sopenharmony_ci */
65462306a36Sopenharmony_cidma_addr_t knav_queue_pop(void *qhandle, unsigned *size)
65562306a36Sopenharmony_ci{
65662306a36Sopenharmony_ci	struct knav_queue *qh = qhandle;
65762306a36Sopenharmony_ci	struct knav_queue_inst *inst = qh->inst;
65862306a36Sopenharmony_ci	dma_addr_t dma;
65962306a36Sopenharmony_ci	u32 val, idx;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	/* are we accumulated? */
66262306a36Sopenharmony_ci	if (inst->descs) {
66362306a36Sopenharmony_ci		if (unlikely(atomic_dec_return(&inst->desc_count) < 0)) {
66462306a36Sopenharmony_ci			atomic_inc(&inst->desc_count);
66562306a36Sopenharmony_ci			return 0;
66662306a36Sopenharmony_ci		}
66762306a36Sopenharmony_ci		idx  = atomic_inc_return(&inst->desc_head);
66862306a36Sopenharmony_ci		idx &= ACC_DESCS_MASK;
66962306a36Sopenharmony_ci		val = inst->descs[idx];
67062306a36Sopenharmony_ci	} else {
67162306a36Sopenharmony_ci		val = readl_relaxed(&qh->reg_pop[0].ptr_size_thresh);
67262306a36Sopenharmony_ci		if (unlikely(!val))
67362306a36Sopenharmony_ci			return 0;
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	dma = val & DESC_PTR_MASK;
67762306a36Sopenharmony_ci	if (size)
67862306a36Sopenharmony_ci		*size = ((val & DESC_SIZE_MASK) + 1) * 16;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	this_cpu_inc(qh->stats->pops);
68162306a36Sopenharmony_ci	return dma;
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_queue_pop);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci/* carve out descriptors and push into queue */
68662306a36Sopenharmony_cistatic void kdesc_fill_pool(struct knav_pool *pool)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	struct knav_region *region;
68962306a36Sopenharmony_ci	int i;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	region = pool->region;
69262306a36Sopenharmony_ci	pool->desc_size = region->desc_size;
69362306a36Sopenharmony_ci	for (i = 0; i < pool->num_desc; i++) {
69462306a36Sopenharmony_ci		int index = pool->region_offset + i;
69562306a36Sopenharmony_ci		dma_addr_t dma_addr;
69662306a36Sopenharmony_ci		unsigned dma_size;
69762306a36Sopenharmony_ci		dma_addr = region->dma_start + (region->desc_size * index);
69862306a36Sopenharmony_ci		dma_size = ALIGN(pool->desc_size, SMP_CACHE_BYTES);
69962306a36Sopenharmony_ci		dma_sync_single_for_device(pool->dev, dma_addr, dma_size,
70062306a36Sopenharmony_ci					   DMA_TO_DEVICE);
70162306a36Sopenharmony_ci		knav_queue_push(pool->queue, dma_addr, dma_size, 0);
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci/* pop out descriptors and close the queue */
70662306a36Sopenharmony_cistatic void kdesc_empty_pool(struct knav_pool *pool)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	dma_addr_t dma;
70962306a36Sopenharmony_ci	unsigned size;
71062306a36Sopenharmony_ci	void *desc;
71162306a36Sopenharmony_ci	int i;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	if (!pool->queue)
71462306a36Sopenharmony_ci		return;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	for (i = 0;; i++) {
71762306a36Sopenharmony_ci		dma = knav_queue_pop(pool->queue, &size);
71862306a36Sopenharmony_ci		if (!dma)
71962306a36Sopenharmony_ci			break;
72062306a36Sopenharmony_ci		desc = knav_pool_desc_dma_to_virt(pool, dma);
72162306a36Sopenharmony_ci		if (!desc) {
72262306a36Sopenharmony_ci			dev_dbg(pool->kdev->dev,
72362306a36Sopenharmony_ci				"couldn't unmap desc, continuing\n");
72462306a36Sopenharmony_ci			continue;
72562306a36Sopenharmony_ci		}
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci	WARN_ON(i != pool->num_desc);
72862306a36Sopenharmony_ci	knav_queue_close(pool->queue);
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci/* Get the DMA address of a descriptor */
73362306a36Sopenharmony_cidma_addr_t knav_pool_desc_virt_to_dma(void *ph, void *virt)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	struct knav_pool *pool = ph;
73662306a36Sopenharmony_ci	return pool->region->dma_start + (virt - pool->region->virt_start);
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_desc_virt_to_dma);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_civoid *knav_pool_desc_dma_to_virt(void *ph, dma_addr_t dma)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	struct knav_pool *pool = ph;
74362306a36Sopenharmony_ci	return pool->region->virt_start + (dma - pool->region->dma_start);
74462306a36Sopenharmony_ci}
74562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_desc_dma_to_virt);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci/**
74862306a36Sopenharmony_ci * knav_pool_create()	- Create a pool of descriptors
74962306a36Sopenharmony_ci * @name:		- name to give the pool handle
75062306a36Sopenharmony_ci * @num_desc:		- numbers of descriptors in the pool
75162306a36Sopenharmony_ci * @region_id:		- QMSS region id from which the descriptors are to be
75262306a36Sopenharmony_ci *			  allocated.
75362306a36Sopenharmony_ci *
75462306a36Sopenharmony_ci * Returns a pool handle on success.
75562306a36Sopenharmony_ci * Use IS_ERR_OR_NULL() to identify error values on return.
75662306a36Sopenharmony_ci */
75762306a36Sopenharmony_civoid *knav_pool_create(const char *name,
75862306a36Sopenharmony_ci					int num_desc, int region_id)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	struct knav_region *reg_itr, *region = NULL;
76162306a36Sopenharmony_ci	struct knav_pool *pool, *pi = NULL, *iter;
76262306a36Sopenharmony_ci	struct list_head *node;
76362306a36Sopenharmony_ci	unsigned last_offset;
76462306a36Sopenharmony_ci	int ret;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	if (!kdev)
76762306a36Sopenharmony_ci		return ERR_PTR(-EPROBE_DEFER);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	if (!kdev->dev)
77062306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	pool = devm_kzalloc(kdev->dev, sizeof(*pool), GFP_KERNEL);
77362306a36Sopenharmony_ci	if (!pool) {
77462306a36Sopenharmony_ci		dev_err(kdev->dev, "out of memory allocating pool\n");
77562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
77662306a36Sopenharmony_ci	}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	for_each_region(kdev, reg_itr) {
77962306a36Sopenharmony_ci		if (reg_itr->id != region_id)
78062306a36Sopenharmony_ci			continue;
78162306a36Sopenharmony_ci		region = reg_itr;
78262306a36Sopenharmony_ci		break;
78362306a36Sopenharmony_ci	}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	if (!region) {
78662306a36Sopenharmony_ci		dev_err(kdev->dev, "region-id(%d) not found\n", region_id);
78762306a36Sopenharmony_ci		ret = -EINVAL;
78862306a36Sopenharmony_ci		goto err;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	pool->queue = knav_queue_open(name, KNAV_QUEUE_GP, 0);
79262306a36Sopenharmony_ci	if (IS_ERR(pool->queue)) {
79362306a36Sopenharmony_ci		dev_err(kdev->dev,
79462306a36Sopenharmony_ci			"failed to open queue for pool(%s), error %ld\n",
79562306a36Sopenharmony_ci			name, PTR_ERR(pool->queue));
79662306a36Sopenharmony_ci		ret = PTR_ERR(pool->queue);
79762306a36Sopenharmony_ci		goto err;
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	pool->name = kstrndup(name, KNAV_NAME_SIZE - 1, GFP_KERNEL);
80162306a36Sopenharmony_ci	pool->kdev = kdev;
80262306a36Sopenharmony_ci	pool->dev = kdev->dev;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	mutex_lock(&knav_dev_lock);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	if (num_desc > (region->num_desc - region->used_desc)) {
80762306a36Sopenharmony_ci		dev_err(kdev->dev, "out of descs in region(%d) for pool(%s)\n",
80862306a36Sopenharmony_ci			region_id, name);
80962306a36Sopenharmony_ci		ret = -ENOMEM;
81062306a36Sopenharmony_ci		goto err_unlock;
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	/* Region maintains a sorted (by region offset) list of pools
81462306a36Sopenharmony_ci	 * use the first free slot which is large enough to accomodate
81562306a36Sopenharmony_ci	 * the request
81662306a36Sopenharmony_ci	 */
81762306a36Sopenharmony_ci	last_offset = 0;
81862306a36Sopenharmony_ci	node = &region->pools;
81962306a36Sopenharmony_ci	list_for_each_entry(iter, &region->pools, region_inst) {
82062306a36Sopenharmony_ci		if ((iter->region_offset - last_offset) >= num_desc) {
82162306a36Sopenharmony_ci			pi = iter;
82262306a36Sopenharmony_ci			break;
82362306a36Sopenharmony_ci		}
82462306a36Sopenharmony_ci		last_offset = iter->region_offset + iter->num_desc;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	if (pi) {
82862306a36Sopenharmony_ci		node = &pi->region_inst;
82962306a36Sopenharmony_ci		pool->region = region;
83062306a36Sopenharmony_ci		pool->num_desc = num_desc;
83162306a36Sopenharmony_ci		pool->region_offset = last_offset;
83262306a36Sopenharmony_ci		region->used_desc += num_desc;
83362306a36Sopenharmony_ci		list_add_tail(&pool->list, &kdev->pools);
83462306a36Sopenharmony_ci		list_add_tail(&pool->region_inst, node);
83562306a36Sopenharmony_ci	} else {
83662306a36Sopenharmony_ci		dev_err(kdev->dev, "pool(%s) create failed: fragmented desc pool in region(%d)\n",
83762306a36Sopenharmony_ci			name, region_id);
83862306a36Sopenharmony_ci		ret = -ENOMEM;
83962306a36Sopenharmony_ci		goto err_unlock;
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	mutex_unlock(&knav_dev_lock);
84362306a36Sopenharmony_ci	kdesc_fill_pool(pool);
84462306a36Sopenharmony_ci	return pool;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cierr_unlock:
84762306a36Sopenharmony_ci	mutex_unlock(&knav_dev_lock);
84862306a36Sopenharmony_cierr:
84962306a36Sopenharmony_ci	kfree(pool->name);
85062306a36Sopenharmony_ci	devm_kfree(kdev->dev, pool);
85162306a36Sopenharmony_ci	return ERR_PTR(ret);
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_create);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci/**
85662306a36Sopenharmony_ci * knav_pool_destroy()	- Free a pool of descriptors
85762306a36Sopenharmony_ci * @ph:		- pool handle
85862306a36Sopenharmony_ci */
85962306a36Sopenharmony_civoid knav_pool_destroy(void *ph)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	struct knav_pool *pool = ph;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	if (!pool)
86462306a36Sopenharmony_ci		return;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	if (!pool->region)
86762306a36Sopenharmony_ci		return;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	kdesc_empty_pool(pool);
87062306a36Sopenharmony_ci	mutex_lock(&knav_dev_lock);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	pool->region->used_desc -= pool->num_desc;
87362306a36Sopenharmony_ci	list_del(&pool->region_inst);
87462306a36Sopenharmony_ci	list_del(&pool->list);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	mutex_unlock(&knav_dev_lock);
87762306a36Sopenharmony_ci	kfree(pool->name);
87862306a36Sopenharmony_ci	devm_kfree(kdev->dev, pool);
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_destroy);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci/**
88462306a36Sopenharmony_ci * knav_pool_desc_get()	- Get a descriptor from the pool
88562306a36Sopenharmony_ci * @ph:		- pool handle
88662306a36Sopenharmony_ci *
88762306a36Sopenharmony_ci * Returns descriptor from the pool.
88862306a36Sopenharmony_ci */
88962306a36Sopenharmony_civoid *knav_pool_desc_get(void *ph)
89062306a36Sopenharmony_ci{
89162306a36Sopenharmony_ci	struct knav_pool *pool = ph;
89262306a36Sopenharmony_ci	dma_addr_t dma;
89362306a36Sopenharmony_ci	unsigned size;
89462306a36Sopenharmony_ci	void *data;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	dma = knav_queue_pop(pool->queue, &size);
89762306a36Sopenharmony_ci	if (unlikely(!dma))
89862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
89962306a36Sopenharmony_ci	data = knav_pool_desc_dma_to_virt(pool, dma);
90062306a36Sopenharmony_ci	return data;
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_desc_get);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci/**
90562306a36Sopenharmony_ci * knav_pool_desc_put()	- return a descriptor to the pool
90662306a36Sopenharmony_ci * @ph:		- pool handle
90762306a36Sopenharmony_ci * @desc:	- virtual address
90862306a36Sopenharmony_ci */
90962306a36Sopenharmony_civoid knav_pool_desc_put(void *ph, void *desc)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	struct knav_pool *pool = ph;
91262306a36Sopenharmony_ci	dma_addr_t dma;
91362306a36Sopenharmony_ci	dma = knav_pool_desc_virt_to_dma(pool, desc);
91462306a36Sopenharmony_ci	knav_queue_push(pool->queue, dma, pool->region->desc_size, 0);
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_desc_put);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci/**
91962306a36Sopenharmony_ci * knav_pool_desc_map()	- Map descriptor for DMA transfer
92062306a36Sopenharmony_ci * @ph:				- pool handle
92162306a36Sopenharmony_ci * @desc:			- address of descriptor to map
92262306a36Sopenharmony_ci * @size:			- size of descriptor to map
92362306a36Sopenharmony_ci * @dma:			- DMA address return pointer
92462306a36Sopenharmony_ci * @dma_sz:			- adjusted return pointer
92562306a36Sopenharmony_ci *
92662306a36Sopenharmony_ci * Returns 0 on success, errno otherwise.
92762306a36Sopenharmony_ci */
92862306a36Sopenharmony_ciint knav_pool_desc_map(void *ph, void *desc, unsigned size,
92962306a36Sopenharmony_ci					dma_addr_t *dma, unsigned *dma_sz)
93062306a36Sopenharmony_ci{
93162306a36Sopenharmony_ci	struct knav_pool *pool = ph;
93262306a36Sopenharmony_ci	*dma = knav_pool_desc_virt_to_dma(pool, desc);
93362306a36Sopenharmony_ci	size = min(size, pool->region->desc_size);
93462306a36Sopenharmony_ci	size = ALIGN(size, SMP_CACHE_BYTES);
93562306a36Sopenharmony_ci	*dma_sz = size;
93662306a36Sopenharmony_ci	dma_sync_single_for_device(pool->dev, *dma, size, DMA_TO_DEVICE);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	/* Ensure the descriptor reaches to the memory */
93962306a36Sopenharmony_ci	__iowmb();
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	return 0;
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_desc_map);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci/**
94662306a36Sopenharmony_ci * knav_pool_desc_unmap()	- Unmap descriptor after DMA transfer
94762306a36Sopenharmony_ci * @ph:				- pool handle
94862306a36Sopenharmony_ci * @dma:			- DMA address of descriptor to unmap
94962306a36Sopenharmony_ci * @dma_sz:			- size of descriptor to unmap
95062306a36Sopenharmony_ci *
95162306a36Sopenharmony_ci * Returns descriptor address on success, Use IS_ERR_OR_NULL() to identify
95262306a36Sopenharmony_ci * error values on return.
95362306a36Sopenharmony_ci */
95462306a36Sopenharmony_civoid *knav_pool_desc_unmap(void *ph, dma_addr_t dma, unsigned dma_sz)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	struct knav_pool *pool = ph;
95762306a36Sopenharmony_ci	unsigned desc_sz;
95862306a36Sopenharmony_ci	void *desc;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	desc_sz = min(dma_sz, pool->region->desc_size);
96162306a36Sopenharmony_ci	desc = knav_pool_desc_dma_to_virt(pool, dma);
96262306a36Sopenharmony_ci	dma_sync_single_for_cpu(pool->dev, dma, desc_sz, DMA_FROM_DEVICE);
96362306a36Sopenharmony_ci	prefetch(desc);
96462306a36Sopenharmony_ci	return desc;
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_desc_unmap);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci/**
96962306a36Sopenharmony_ci * knav_pool_count()	- Get the number of descriptors in pool.
97062306a36Sopenharmony_ci * @ph:			- pool handle
97162306a36Sopenharmony_ci * Returns number of elements in the pool.
97262306a36Sopenharmony_ci */
97362306a36Sopenharmony_ciint knav_pool_count(void *ph)
97462306a36Sopenharmony_ci{
97562306a36Sopenharmony_ci	struct knav_pool *pool = ph;
97662306a36Sopenharmony_ci	return knav_queue_get_count(pool->queue);
97762306a36Sopenharmony_ci}
97862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_count);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_cistatic void knav_queue_setup_region(struct knav_device *kdev,
98162306a36Sopenharmony_ci					struct knav_region *region)
98262306a36Sopenharmony_ci{
98362306a36Sopenharmony_ci	unsigned hw_num_desc, hw_desc_size, size;
98462306a36Sopenharmony_ci	struct knav_reg_region __iomem  *regs;
98562306a36Sopenharmony_ci	struct knav_qmgr_info *qmgr;
98662306a36Sopenharmony_ci	struct knav_pool *pool;
98762306a36Sopenharmony_ci	int id = region->id;
98862306a36Sopenharmony_ci	struct page *page;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	/* unused region? */
99162306a36Sopenharmony_ci	if (!region->num_desc) {
99262306a36Sopenharmony_ci		dev_warn(kdev->dev, "unused region %s\n", region->name);
99362306a36Sopenharmony_ci		return;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	/* get hardware descriptor value */
99762306a36Sopenharmony_ci	hw_num_desc = ilog2(region->num_desc - 1) + 1;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	/* did we force fit ourselves into nothingness? */
100062306a36Sopenharmony_ci	if (region->num_desc < 32) {
100162306a36Sopenharmony_ci		region->num_desc = 0;
100262306a36Sopenharmony_ci		dev_warn(kdev->dev, "too few descriptors in region %s\n",
100362306a36Sopenharmony_ci			 region->name);
100462306a36Sopenharmony_ci		return;
100562306a36Sopenharmony_ci	}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	size = region->num_desc * region->desc_size;
100862306a36Sopenharmony_ci	region->virt_start = alloc_pages_exact(size, GFP_KERNEL | GFP_DMA |
100962306a36Sopenharmony_ci						GFP_DMA32);
101062306a36Sopenharmony_ci	if (!region->virt_start) {
101162306a36Sopenharmony_ci		region->num_desc = 0;
101262306a36Sopenharmony_ci		dev_err(kdev->dev, "memory alloc failed for region %s\n",
101362306a36Sopenharmony_ci			region->name);
101462306a36Sopenharmony_ci		return;
101562306a36Sopenharmony_ci	}
101662306a36Sopenharmony_ci	region->virt_end = region->virt_start + size;
101762306a36Sopenharmony_ci	page = virt_to_page(region->virt_start);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	region->dma_start = dma_map_page(kdev->dev, page, 0, size,
102062306a36Sopenharmony_ci					 DMA_BIDIRECTIONAL);
102162306a36Sopenharmony_ci	if (dma_mapping_error(kdev->dev, region->dma_start)) {
102262306a36Sopenharmony_ci		dev_err(kdev->dev, "dma map failed for region %s\n",
102362306a36Sopenharmony_ci			region->name);
102462306a36Sopenharmony_ci		goto fail;
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci	region->dma_end = region->dma_start + size;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	pool = devm_kzalloc(kdev->dev, sizeof(*pool), GFP_KERNEL);
102962306a36Sopenharmony_ci	if (!pool) {
103062306a36Sopenharmony_ci		dev_err(kdev->dev, "out of memory allocating dummy pool\n");
103162306a36Sopenharmony_ci		goto fail;
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci	pool->num_desc = 0;
103462306a36Sopenharmony_ci	pool->region_offset = region->num_desc;
103562306a36Sopenharmony_ci	list_add(&pool->region_inst, &region->pools);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	dev_dbg(kdev->dev,
103862306a36Sopenharmony_ci		"region %s (%d): size:%d, link:%d@%d, dma:%pad-%pad, virt:%p-%p\n",
103962306a36Sopenharmony_ci		region->name, id, region->desc_size, region->num_desc,
104062306a36Sopenharmony_ci		region->link_index, &region->dma_start, &region->dma_end,
104162306a36Sopenharmony_ci		region->virt_start, region->virt_end);
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	hw_desc_size = (region->desc_size / 16) - 1;
104462306a36Sopenharmony_ci	hw_num_desc -= 5;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	for_each_qmgr(kdev, qmgr) {
104762306a36Sopenharmony_ci		regs = qmgr->reg_region + id;
104862306a36Sopenharmony_ci		writel_relaxed((u32)region->dma_start, &regs->base);
104962306a36Sopenharmony_ci		writel_relaxed(region->link_index, &regs->start_index);
105062306a36Sopenharmony_ci		writel_relaxed(hw_desc_size << 16 | hw_num_desc,
105162306a36Sopenharmony_ci			       &regs->size_count);
105262306a36Sopenharmony_ci	}
105362306a36Sopenharmony_ci	return;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_cifail:
105662306a36Sopenharmony_ci	if (region->dma_start)
105762306a36Sopenharmony_ci		dma_unmap_page(kdev->dev, region->dma_start, size,
105862306a36Sopenharmony_ci				DMA_BIDIRECTIONAL);
105962306a36Sopenharmony_ci	if (region->virt_start)
106062306a36Sopenharmony_ci		free_pages_exact(region->virt_start, size);
106162306a36Sopenharmony_ci	region->num_desc = 0;
106262306a36Sopenharmony_ci	return;
106362306a36Sopenharmony_ci}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_cistatic const char *knav_queue_find_name(struct device_node *node)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	const char *name;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	if (of_property_read_string(node, "label", &name) < 0)
107062306a36Sopenharmony_ci		name = node->name;
107162306a36Sopenharmony_ci	if (!name)
107262306a36Sopenharmony_ci		name = "unknown";
107362306a36Sopenharmony_ci	return name;
107462306a36Sopenharmony_ci}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_cistatic int knav_queue_setup_regions(struct knav_device *kdev,
107762306a36Sopenharmony_ci					struct device_node *regions)
107862306a36Sopenharmony_ci{
107962306a36Sopenharmony_ci	struct device *dev = kdev->dev;
108062306a36Sopenharmony_ci	struct knav_region *region;
108162306a36Sopenharmony_ci	struct device_node *child;
108262306a36Sopenharmony_ci	u32 temp[2];
108362306a36Sopenharmony_ci	int ret;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	for_each_child_of_node(regions, child) {
108662306a36Sopenharmony_ci		region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL);
108762306a36Sopenharmony_ci		if (!region) {
108862306a36Sopenharmony_ci			of_node_put(child);
108962306a36Sopenharmony_ci			dev_err(dev, "out of memory allocating region\n");
109062306a36Sopenharmony_ci			return -ENOMEM;
109162306a36Sopenharmony_ci		}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci		region->name = knav_queue_find_name(child);
109462306a36Sopenharmony_ci		of_property_read_u32(child, "id", &region->id);
109562306a36Sopenharmony_ci		ret = of_property_read_u32_array(child, "region-spec", temp, 2);
109662306a36Sopenharmony_ci		if (!ret) {
109762306a36Sopenharmony_ci			region->num_desc  = temp[0];
109862306a36Sopenharmony_ci			region->desc_size = temp[1];
109962306a36Sopenharmony_ci		} else {
110062306a36Sopenharmony_ci			dev_err(dev, "invalid region info %s\n", region->name);
110162306a36Sopenharmony_ci			devm_kfree(dev, region);
110262306a36Sopenharmony_ci			continue;
110362306a36Sopenharmony_ci		}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci		if (!of_get_property(child, "link-index", NULL)) {
110662306a36Sopenharmony_ci			dev_err(dev, "No link info for %s\n", region->name);
110762306a36Sopenharmony_ci			devm_kfree(dev, region);
110862306a36Sopenharmony_ci			continue;
110962306a36Sopenharmony_ci		}
111062306a36Sopenharmony_ci		ret = of_property_read_u32(child, "link-index",
111162306a36Sopenharmony_ci					   &region->link_index);
111262306a36Sopenharmony_ci		if (ret) {
111362306a36Sopenharmony_ci			dev_err(dev, "link index not found for %s\n",
111462306a36Sopenharmony_ci				region->name);
111562306a36Sopenharmony_ci			devm_kfree(dev, region);
111662306a36Sopenharmony_ci			continue;
111762306a36Sopenharmony_ci		}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci		INIT_LIST_HEAD(&region->pools);
112062306a36Sopenharmony_ci		list_add_tail(&region->list, &kdev->regions);
112162306a36Sopenharmony_ci	}
112262306a36Sopenharmony_ci	if (list_empty(&kdev->regions)) {
112362306a36Sopenharmony_ci		dev_err(dev, "no valid region information found\n");
112462306a36Sopenharmony_ci		return -ENODEV;
112562306a36Sopenharmony_ci	}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	/* Next, we run through the regions and set things up */
112862306a36Sopenharmony_ci	for_each_region(kdev, region)
112962306a36Sopenharmony_ci		knav_queue_setup_region(kdev, region);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	return 0;
113262306a36Sopenharmony_ci}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_cistatic int knav_get_link_ram(struct knav_device *kdev,
113562306a36Sopenharmony_ci				       const char *name,
113662306a36Sopenharmony_ci				       struct knav_link_ram_block *block)
113762306a36Sopenharmony_ci{
113862306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(kdev->dev);
113962306a36Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
114062306a36Sopenharmony_ci	u32 temp[2];
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	/*
114362306a36Sopenharmony_ci	 * Note: link ram resources are specified in "entry" sized units. In
114462306a36Sopenharmony_ci	 * reality, although entries are ~40bits in hardware, we treat them as
114562306a36Sopenharmony_ci	 * 64-bit entities here.
114662306a36Sopenharmony_ci	 *
114762306a36Sopenharmony_ci	 * For example, to specify the internal link ram for Keystone-I class
114862306a36Sopenharmony_ci	 * devices, we would set the linkram0 resource to 0x80000-0x83fff.
114962306a36Sopenharmony_ci	 *
115062306a36Sopenharmony_ci	 * This gets a bit weird when other link rams are used.  For example,
115162306a36Sopenharmony_ci	 * if the range specified is 0x0c000000-0x0c003fff (i.e., 16K entries
115262306a36Sopenharmony_ci	 * in MSMC SRAM), the actual memory used is 0x0c000000-0x0c020000,
115362306a36Sopenharmony_ci	 * which accounts for 64-bits per entry, for 16K entries.
115462306a36Sopenharmony_ci	 */
115562306a36Sopenharmony_ci	if (!of_property_read_u32_array(node, name , temp, 2)) {
115662306a36Sopenharmony_ci		if (temp[0]) {
115762306a36Sopenharmony_ci			/*
115862306a36Sopenharmony_ci			 * queue_base specified => using internal or onchip
115962306a36Sopenharmony_ci			 * link ram WARNING - we do not "reserve" this block
116062306a36Sopenharmony_ci			 */
116162306a36Sopenharmony_ci			block->dma = (dma_addr_t)temp[0];
116262306a36Sopenharmony_ci			block->virt = NULL;
116362306a36Sopenharmony_ci			block->size = temp[1];
116462306a36Sopenharmony_ci		} else {
116562306a36Sopenharmony_ci			block->size = temp[1];
116662306a36Sopenharmony_ci			/* queue_base not specific => allocate requested size */
116762306a36Sopenharmony_ci			block->virt = dmam_alloc_coherent(kdev->dev,
116862306a36Sopenharmony_ci						  8 * block->size, &block->dma,
116962306a36Sopenharmony_ci						  GFP_KERNEL);
117062306a36Sopenharmony_ci			if (!block->virt) {
117162306a36Sopenharmony_ci				dev_err(kdev->dev, "failed to alloc linkram\n");
117262306a36Sopenharmony_ci				return -ENOMEM;
117362306a36Sopenharmony_ci			}
117462306a36Sopenharmony_ci		}
117562306a36Sopenharmony_ci	} else {
117662306a36Sopenharmony_ci		return -ENODEV;
117762306a36Sopenharmony_ci	}
117862306a36Sopenharmony_ci	return 0;
117962306a36Sopenharmony_ci}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_cistatic int knav_queue_setup_link_ram(struct knav_device *kdev)
118262306a36Sopenharmony_ci{
118362306a36Sopenharmony_ci	struct knav_link_ram_block *block;
118462306a36Sopenharmony_ci	struct knav_qmgr_info *qmgr;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	for_each_qmgr(kdev, qmgr) {
118762306a36Sopenharmony_ci		block = &kdev->link_rams[0];
118862306a36Sopenharmony_ci		dev_dbg(kdev->dev, "linkram0: dma:%pad, virt:%p, size:%x\n",
118962306a36Sopenharmony_ci			&block->dma, block->virt, block->size);
119062306a36Sopenharmony_ci		writel_relaxed((u32)block->dma, &qmgr->reg_config->link_ram_base0);
119162306a36Sopenharmony_ci		if (kdev->version == QMSS_66AK2G)
119262306a36Sopenharmony_ci			writel_relaxed(block->size,
119362306a36Sopenharmony_ci				       &qmgr->reg_config->link_ram_size0);
119462306a36Sopenharmony_ci		else
119562306a36Sopenharmony_ci			writel_relaxed(block->size - 1,
119662306a36Sopenharmony_ci				       &qmgr->reg_config->link_ram_size0);
119762306a36Sopenharmony_ci		block++;
119862306a36Sopenharmony_ci		if (!block->size)
119962306a36Sopenharmony_ci			continue;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci		dev_dbg(kdev->dev, "linkram1: dma:%pad, virt:%p, size:%x\n",
120262306a36Sopenharmony_ci			&block->dma, block->virt, block->size);
120362306a36Sopenharmony_ci		writel_relaxed(block->dma, &qmgr->reg_config->link_ram_base1);
120462306a36Sopenharmony_ci	}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	return 0;
120762306a36Sopenharmony_ci}
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_cistatic int knav_setup_queue_range(struct knav_device *kdev,
121062306a36Sopenharmony_ci					struct device_node *node)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	struct device *dev = kdev->dev;
121362306a36Sopenharmony_ci	struct knav_range_info *range;
121462306a36Sopenharmony_ci	struct knav_qmgr_info *qmgr;
121562306a36Sopenharmony_ci	u32 temp[2], start, end, id, index;
121662306a36Sopenharmony_ci	int ret, i;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL);
121962306a36Sopenharmony_ci	if (!range) {
122062306a36Sopenharmony_ci		dev_err(dev, "out of memory allocating range\n");
122162306a36Sopenharmony_ci		return -ENOMEM;
122262306a36Sopenharmony_ci	}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	range->kdev = kdev;
122562306a36Sopenharmony_ci	range->name = knav_queue_find_name(node);
122662306a36Sopenharmony_ci	ret = of_property_read_u32_array(node, "qrange", temp, 2);
122762306a36Sopenharmony_ci	if (!ret) {
122862306a36Sopenharmony_ci		range->queue_base = temp[0] - kdev->base_id;
122962306a36Sopenharmony_ci		range->num_queues = temp[1];
123062306a36Sopenharmony_ci	} else {
123162306a36Sopenharmony_ci		dev_err(dev, "invalid queue range %s\n", range->name);
123262306a36Sopenharmony_ci		devm_kfree(dev, range);
123362306a36Sopenharmony_ci		return -EINVAL;
123462306a36Sopenharmony_ci	}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	for (i = 0; i < RANGE_MAX_IRQS; i++) {
123762306a36Sopenharmony_ci		struct of_phandle_args oirq;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci		if (of_irq_parse_one(node, i, &oirq))
124062306a36Sopenharmony_ci			break;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci		range->irqs[i].irq = irq_create_of_mapping(&oirq);
124362306a36Sopenharmony_ci		if (range->irqs[i].irq == IRQ_NONE)
124462306a36Sopenharmony_ci			break;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci		range->num_irqs++;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_SMP) && oirq.args_count == 3) {
124962306a36Sopenharmony_ci			unsigned long mask;
125062306a36Sopenharmony_ci			int bit;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci			range->irqs[i].cpu_mask = devm_kzalloc(dev,
125362306a36Sopenharmony_ci							       cpumask_size(), GFP_KERNEL);
125462306a36Sopenharmony_ci			if (!range->irqs[i].cpu_mask)
125562306a36Sopenharmony_ci				return -ENOMEM;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci			mask = (oirq.args[2] & 0x0000ff00) >> 8;
125862306a36Sopenharmony_ci			for_each_set_bit(bit, &mask, BITS_PER_LONG)
125962306a36Sopenharmony_ci				cpumask_set_cpu(bit, range->irqs[i].cpu_mask);
126062306a36Sopenharmony_ci		}
126162306a36Sopenharmony_ci	}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	range->num_irqs = min(range->num_irqs, range->num_queues);
126462306a36Sopenharmony_ci	if (range->num_irqs)
126562306a36Sopenharmony_ci		range->flags |= RANGE_HAS_IRQ;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	if (of_property_read_bool(node, "qalloc-by-id"))
126862306a36Sopenharmony_ci		range->flags |= RANGE_RESERVED;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	if (of_property_present(node, "accumulator")) {
127162306a36Sopenharmony_ci		ret = knav_init_acc_range(kdev, node, range);
127262306a36Sopenharmony_ci		if (ret < 0) {
127362306a36Sopenharmony_ci			devm_kfree(dev, range);
127462306a36Sopenharmony_ci			return ret;
127562306a36Sopenharmony_ci		}
127662306a36Sopenharmony_ci	} else {
127762306a36Sopenharmony_ci		range->ops = &knav_gp_range_ops;
127862306a36Sopenharmony_ci	}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	/* set threshold to 1, and flush out the queues */
128162306a36Sopenharmony_ci	for_each_qmgr(kdev, qmgr) {
128262306a36Sopenharmony_ci		start = max(qmgr->start_queue, range->queue_base);
128362306a36Sopenharmony_ci		end   = min(qmgr->start_queue + qmgr->num_queues,
128462306a36Sopenharmony_ci			    range->queue_base + range->num_queues);
128562306a36Sopenharmony_ci		for (id = start; id < end; id++) {
128662306a36Sopenharmony_ci			index = id - qmgr->start_queue;
128762306a36Sopenharmony_ci			writel_relaxed(THRESH_GTE | 1,
128862306a36Sopenharmony_ci				       &qmgr->reg_peek[index].ptr_size_thresh);
128962306a36Sopenharmony_ci			writel_relaxed(0,
129062306a36Sopenharmony_ci				       &qmgr->reg_push[index].ptr_size_thresh);
129162306a36Sopenharmony_ci		}
129262306a36Sopenharmony_ci	}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	list_add_tail(&range->list, &kdev->queue_ranges);
129562306a36Sopenharmony_ci	dev_dbg(dev, "added range %s: %d-%d, %d irqs%s%s%s\n",
129662306a36Sopenharmony_ci		range->name, range->queue_base,
129762306a36Sopenharmony_ci		range->queue_base + range->num_queues - 1,
129862306a36Sopenharmony_ci		range->num_irqs,
129962306a36Sopenharmony_ci		(range->flags & RANGE_HAS_IRQ) ? ", has irq" : "",
130062306a36Sopenharmony_ci		(range->flags & RANGE_RESERVED) ? ", reserved" : "",
130162306a36Sopenharmony_ci		(range->flags & RANGE_HAS_ACCUMULATOR) ? ", acc" : "");
130262306a36Sopenharmony_ci	kdev->num_queues_in_use += range->num_queues;
130362306a36Sopenharmony_ci	return 0;
130462306a36Sopenharmony_ci}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_cistatic int knav_setup_queue_pools(struct knav_device *kdev,
130762306a36Sopenharmony_ci				   struct device_node *queue_pools)
130862306a36Sopenharmony_ci{
130962306a36Sopenharmony_ci	struct device_node *type, *range;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	for_each_child_of_node(queue_pools, type) {
131262306a36Sopenharmony_ci		for_each_child_of_node(type, range) {
131362306a36Sopenharmony_ci			/* return value ignored, we init the rest... */
131462306a36Sopenharmony_ci			knav_setup_queue_range(kdev, range);
131562306a36Sopenharmony_ci		}
131662306a36Sopenharmony_ci	}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	/* ... and barf if they all failed! */
131962306a36Sopenharmony_ci	if (list_empty(&kdev->queue_ranges)) {
132062306a36Sopenharmony_ci		dev_err(kdev->dev, "no valid queue range found\n");
132162306a36Sopenharmony_ci		return -ENODEV;
132262306a36Sopenharmony_ci	}
132362306a36Sopenharmony_ci	return 0;
132462306a36Sopenharmony_ci}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_cistatic void knav_free_queue_range(struct knav_device *kdev,
132762306a36Sopenharmony_ci				  struct knav_range_info *range)
132862306a36Sopenharmony_ci{
132962306a36Sopenharmony_ci	if (range->ops && range->ops->free_range)
133062306a36Sopenharmony_ci		range->ops->free_range(range);
133162306a36Sopenharmony_ci	list_del(&range->list);
133262306a36Sopenharmony_ci	devm_kfree(kdev->dev, range);
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_cistatic void knav_free_queue_ranges(struct knav_device *kdev)
133662306a36Sopenharmony_ci{
133762306a36Sopenharmony_ci	struct knav_range_info *range;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	for (;;) {
134062306a36Sopenharmony_ci		range = first_queue_range(kdev);
134162306a36Sopenharmony_ci		if (!range)
134262306a36Sopenharmony_ci			break;
134362306a36Sopenharmony_ci		knav_free_queue_range(kdev, range);
134462306a36Sopenharmony_ci	}
134562306a36Sopenharmony_ci}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_cistatic void knav_queue_free_regions(struct knav_device *kdev)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	struct knav_region *region;
135062306a36Sopenharmony_ci	struct knav_pool *pool, *tmp;
135162306a36Sopenharmony_ci	unsigned size;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	for (;;) {
135462306a36Sopenharmony_ci		region = first_region(kdev);
135562306a36Sopenharmony_ci		if (!region)
135662306a36Sopenharmony_ci			break;
135762306a36Sopenharmony_ci		list_for_each_entry_safe(pool, tmp, &region->pools, region_inst)
135862306a36Sopenharmony_ci			knav_pool_destroy(pool);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci		size = region->virt_end - region->virt_start;
136162306a36Sopenharmony_ci		if (size)
136262306a36Sopenharmony_ci			free_pages_exact(region->virt_start, size);
136362306a36Sopenharmony_ci		list_del(&region->list);
136462306a36Sopenharmony_ci		devm_kfree(kdev->dev, region);
136562306a36Sopenharmony_ci	}
136662306a36Sopenharmony_ci}
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_cistatic void __iomem *knav_queue_map_reg(struct knav_device *kdev,
136962306a36Sopenharmony_ci					struct device_node *node, int index)
137062306a36Sopenharmony_ci{
137162306a36Sopenharmony_ci	struct resource res;
137262306a36Sopenharmony_ci	void __iomem *regs;
137362306a36Sopenharmony_ci	int ret;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	ret = of_address_to_resource(node, index, &res);
137662306a36Sopenharmony_ci	if (ret) {
137762306a36Sopenharmony_ci		dev_err(kdev->dev, "Can't translate of node(%pOFn) address for index(%d)\n",
137862306a36Sopenharmony_ci			node, index);
137962306a36Sopenharmony_ci		return ERR_PTR(ret);
138062306a36Sopenharmony_ci	}
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	regs = devm_ioremap_resource(kdev->dev, &res);
138362306a36Sopenharmony_ci	if (IS_ERR(regs))
138462306a36Sopenharmony_ci		dev_err(kdev->dev, "Failed to map register base for index(%d) node(%pOFn)\n",
138562306a36Sopenharmony_ci			index, node);
138662306a36Sopenharmony_ci	return regs;
138762306a36Sopenharmony_ci}
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_cistatic int knav_queue_init_qmgrs(struct knav_device *kdev,
139062306a36Sopenharmony_ci					struct device_node *qmgrs)
139162306a36Sopenharmony_ci{
139262306a36Sopenharmony_ci	struct device *dev = kdev->dev;
139362306a36Sopenharmony_ci	struct knav_qmgr_info *qmgr;
139462306a36Sopenharmony_ci	struct device_node *child;
139562306a36Sopenharmony_ci	u32 temp[2];
139662306a36Sopenharmony_ci	int ret;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	for_each_child_of_node(qmgrs, child) {
139962306a36Sopenharmony_ci		qmgr = devm_kzalloc(dev, sizeof(*qmgr), GFP_KERNEL);
140062306a36Sopenharmony_ci		if (!qmgr) {
140162306a36Sopenharmony_ci			of_node_put(child);
140262306a36Sopenharmony_ci			dev_err(dev, "out of memory allocating qmgr\n");
140362306a36Sopenharmony_ci			return -ENOMEM;
140462306a36Sopenharmony_ci		}
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci		ret = of_property_read_u32_array(child, "managed-queues",
140762306a36Sopenharmony_ci						 temp, 2);
140862306a36Sopenharmony_ci		if (!ret) {
140962306a36Sopenharmony_ci			qmgr->start_queue = temp[0];
141062306a36Sopenharmony_ci			qmgr->num_queues = temp[1];
141162306a36Sopenharmony_ci		} else {
141262306a36Sopenharmony_ci			dev_err(dev, "invalid qmgr queue range\n");
141362306a36Sopenharmony_ci			devm_kfree(dev, qmgr);
141462306a36Sopenharmony_ci			continue;
141562306a36Sopenharmony_ci		}
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci		dev_info(dev, "qmgr start queue %d, number of queues %d\n",
141862306a36Sopenharmony_ci			 qmgr->start_queue, qmgr->num_queues);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci		qmgr->reg_peek =
142162306a36Sopenharmony_ci			knav_queue_map_reg(kdev, child,
142262306a36Sopenharmony_ci					   KNAV_QUEUE_PEEK_REG_INDEX);
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci		if (kdev->version == QMSS) {
142562306a36Sopenharmony_ci			qmgr->reg_status =
142662306a36Sopenharmony_ci				knav_queue_map_reg(kdev, child,
142762306a36Sopenharmony_ci						   KNAV_QUEUE_STATUS_REG_INDEX);
142862306a36Sopenharmony_ci		}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci		qmgr->reg_config =
143162306a36Sopenharmony_ci			knav_queue_map_reg(kdev, child,
143262306a36Sopenharmony_ci					   (kdev->version == QMSS_66AK2G) ?
143362306a36Sopenharmony_ci					   KNAV_L_QUEUE_CONFIG_REG_INDEX :
143462306a36Sopenharmony_ci					   KNAV_QUEUE_CONFIG_REG_INDEX);
143562306a36Sopenharmony_ci		qmgr->reg_region =
143662306a36Sopenharmony_ci			knav_queue_map_reg(kdev, child,
143762306a36Sopenharmony_ci					   (kdev->version == QMSS_66AK2G) ?
143862306a36Sopenharmony_ci					   KNAV_L_QUEUE_REGION_REG_INDEX :
143962306a36Sopenharmony_ci					   KNAV_QUEUE_REGION_REG_INDEX);
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci		qmgr->reg_push =
144262306a36Sopenharmony_ci			knav_queue_map_reg(kdev, child,
144362306a36Sopenharmony_ci					   (kdev->version == QMSS_66AK2G) ?
144462306a36Sopenharmony_ci					    KNAV_L_QUEUE_PUSH_REG_INDEX :
144562306a36Sopenharmony_ci					    KNAV_QUEUE_PUSH_REG_INDEX);
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci		if (kdev->version == QMSS) {
144862306a36Sopenharmony_ci			qmgr->reg_pop =
144962306a36Sopenharmony_ci				knav_queue_map_reg(kdev, child,
145062306a36Sopenharmony_ci						   KNAV_QUEUE_POP_REG_INDEX);
145162306a36Sopenharmony_ci		}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci		if (IS_ERR(qmgr->reg_peek) ||
145462306a36Sopenharmony_ci		    ((kdev->version == QMSS) &&
145562306a36Sopenharmony_ci		    (IS_ERR(qmgr->reg_status) || IS_ERR(qmgr->reg_pop))) ||
145662306a36Sopenharmony_ci		    IS_ERR(qmgr->reg_config) || IS_ERR(qmgr->reg_region) ||
145762306a36Sopenharmony_ci		    IS_ERR(qmgr->reg_push)) {
145862306a36Sopenharmony_ci			dev_err(dev, "failed to map qmgr regs\n");
145962306a36Sopenharmony_ci			if (kdev->version == QMSS) {
146062306a36Sopenharmony_ci				if (!IS_ERR(qmgr->reg_status))
146162306a36Sopenharmony_ci					devm_iounmap(dev, qmgr->reg_status);
146262306a36Sopenharmony_ci				if (!IS_ERR(qmgr->reg_pop))
146362306a36Sopenharmony_ci					devm_iounmap(dev, qmgr->reg_pop);
146462306a36Sopenharmony_ci			}
146562306a36Sopenharmony_ci			if (!IS_ERR(qmgr->reg_peek))
146662306a36Sopenharmony_ci				devm_iounmap(dev, qmgr->reg_peek);
146762306a36Sopenharmony_ci			if (!IS_ERR(qmgr->reg_config))
146862306a36Sopenharmony_ci				devm_iounmap(dev, qmgr->reg_config);
146962306a36Sopenharmony_ci			if (!IS_ERR(qmgr->reg_region))
147062306a36Sopenharmony_ci				devm_iounmap(dev, qmgr->reg_region);
147162306a36Sopenharmony_ci			if (!IS_ERR(qmgr->reg_push))
147262306a36Sopenharmony_ci				devm_iounmap(dev, qmgr->reg_push);
147362306a36Sopenharmony_ci			devm_kfree(dev, qmgr);
147462306a36Sopenharmony_ci			continue;
147562306a36Sopenharmony_ci		}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci		/* Use same push register for pop as well */
147862306a36Sopenharmony_ci		if (kdev->version == QMSS_66AK2G)
147962306a36Sopenharmony_ci			qmgr->reg_pop = qmgr->reg_push;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci		list_add_tail(&qmgr->list, &kdev->qmgrs);
148262306a36Sopenharmony_ci		dev_info(dev, "added qmgr start queue %d, num of queues %d, reg_peek %p, reg_status %p, reg_config %p, reg_region %p, reg_push %p, reg_pop %p\n",
148362306a36Sopenharmony_ci			 qmgr->start_queue, qmgr->num_queues,
148462306a36Sopenharmony_ci			 qmgr->reg_peek, qmgr->reg_status,
148562306a36Sopenharmony_ci			 qmgr->reg_config, qmgr->reg_region,
148662306a36Sopenharmony_ci			 qmgr->reg_push, qmgr->reg_pop);
148762306a36Sopenharmony_ci	}
148862306a36Sopenharmony_ci	return 0;
148962306a36Sopenharmony_ci}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_cistatic int knav_queue_init_pdsps(struct knav_device *kdev,
149262306a36Sopenharmony_ci					struct device_node *pdsps)
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	struct device *dev = kdev->dev;
149562306a36Sopenharmony_ci	struct knav_pdsp_info *pdsp;
149662306a36Sopenharmony_ci	struct device_node *child;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	for_each_child_of_node(pdsps, child) {
149962306a36Sopenharmony_ci		pdsp = devm_kzalloc(dev, sizeof(*pdsp), GFP_KERNEL);
150062306a36Sopenharmony_ci		if (!pdsp) {
150162306a36Sopenharmony_ci			of_node_put(child);
150262306a36Sopenharmony_ci			dev_err(dev, "out of memory allocating pdsp\n");
150362306a36Sopenharmony_ci			return -ENOMEM;
150462306a36Sopenharmony_ci		}
150562306a36Sopenharmony_ci		pdsp->name = knav_queue_find_name(child);
150662306a36Sopenharmony_ci		pdsp->iram =
150762306a36Sopenharmony_ci			knav_queue_map_reg(kdev, child,
150862306a36Sopenharmony_ci					   KNAV_QUEUE_PDSP_IRAM_REG_INDEX);
150962306a36Sopenharmony_ci		pdsp->regs =
151062306a36Sopenharmony_ci			knav_queue_map_reg(kdev, child,
151162306a36Sopenharmony_ci					   KNAV_QUEUE_PDSP_REGS_REG_INDEX);
151262306a36Sopenharmony_ci		pdsp->intd =
151362306a36Sopenharmony_ci			knav_queue_map_reg(kdev, child,
151462306a36Sopenharmony_ci					   KNAV_QUEUE_PDSP_INTD_REG_INDEX);
151562306a36Sopenharmony_ci		pdsp->command =
151662306a36Sopenharmony_ci			knav_queue_map_reg(kdev, child,
151762306a36Sopenharmony_ci					   KNAV_QUEUE_PDSP_CMD_REG_INDEX);
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci		if (IS_ERR(pdsp->command) || IS_ERR(pdsp->iram) ||
152062306a36Sopenharmony_ci		    IS_ERR(pdsp->regs) || IS_ERR(pdsp->intd)) {
152162306a36Sopenharmony_ci			dev_err(dev, "failed to map pdsp %s regs\n",
152262306a36Sopenharmony_ci				pdsp->name);
152362306a36Sopenharmony_ci			if (!IS_ERR(pdsp->command))
152462306a36Sopenharmony_ci				devm_iounmap(dev, pdsp->command);
152562306a36Sopenharmony_ci			if (!IS_ERR(pdsp->iram))
152662306a36Sopenharmony_ci				devm_iounmap(dev, pdsp->iram);
152762306a36Sopenharmony_ci			if (!IS_ERR(pdsp->regs))
152862306a36Sopenharmony_ci				devm_iounmap(dev, pdsp->regs);
152962306a36Sopenharmony_ci			if (!IS_ERR(pdsp->intd))
153062306a36Sopenharmony_ci				devm_iounmap(dev, pdsp->intd);
153162306a36Sopenharmony_ci			devm_kfree(dev, pdsp);
153262306a36Sopenharmony_ci			continue;
153362306a36Sopenharmony_ci		}
153462306a36Sopenharmony_ci		of_property_read_u32(child, "id", &pdsp->id);
153562306a36Sopenharmony_ci		list_add_tail(&pdsp->list, &kdev->pdsps);
153662306a36Sopenharmony_ci		dev_dbg(dev, "added pdsp %s: command %p, iram %p, regs %p, intd %p\n",
153762306a36Sopenharmony_ci			pdsp->name, pdsp->command, pdsp->iram, pdsp->regs,
153862306a36Sopenharmony_ci			pdsp->intd);
153962306a36Sopenharmony_ci	}
154062306a36Sopenharmony_ci	return 0;
154162306a36Sopenharmony_ci}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_cistatic int knav_queue_stop_pdsp(struct knav_device *kdev,
154462306a36Sopenharmony_ci			  struct knav_pdsp_info *pdsp)
154562306a36Sopenharmony_ci{
154662306a36Sopenharmony_ci	u32 val, timeout = 1000;
154762306a36Sopenharmony_ci	int ret;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	val = readl_relaxed(&pdsp->regs->control) & ~PDSP_CTRL_ENABLE;
155062306a36Sopenharmony_ci	writel_relaxed(val, &pdsp->regs->control);
155162306a36Sopenharmony_ci	ret = knav_queue_pdsp_wait(&pdsp->regs->control, timeout,
155262306a36Sopenharmony_ci					PDSP_CTRL_RUNNING);
155362306a36Sopenharmony_ci	if (ret < 0) {
155462306a36Sopenharmony_ci		dev_err(kdev->dev, "timed out on pdsp %s stop\n", pdsp->name);
155562306a36Sopenharmony_ci		return ret;
155662306a36Sopenharmony_ci	}
155762306a36Sopenharmony_ci	pdsp->loaded = false;
155862306a36Sopenharmony_ci	pdsp->started = false;
155962306a36Sopenharmony_ci	return 0;
156062306a36Sopenharmony_ci}
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_cistatic int knav_queue_load_pdsp(struct knav_device *kdev,
156362306a36Sopenharmony_ci			  struct knav_pdsp_info *pdsp)
156462306a36Sopenharmony_ci{
156562306a36Sopenharmony_ci	int i, ret, fwlen;
156662306a36Sopenharmony_ci	const struct firmware *fw;
156762306a36Sopenharmony_ci	bool found = false;
156862306a36Sopenharmony_ci	u32 *fwdata;
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(knav_acc_firmwares); i++) {
157162306a36Sopenharmony_ci		if (knav_acc_firmwares[i]) {
157262306a36Sopenharmony_ci			ret = request_firmware_direct(&fw,
157362306a36Sopenharmony_ci						      knav_acc_firmwares[i],
157462306a36Sopenharmony_ci						      kdev->dev);
157562306a36Sopenharmony_ci			if (!ret) {
157662306a36Sopenharmony_ci				found = true;
157762306a36Sopenharmony_ci				break;
157862306a36Sopenharmony_ci			}
157962306a36Sopenharmony_ci		}
158062306a36Sopenharmony_ci	}
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	if (!found) {
158362306a36Sopenharmony_ci		dev_err(kdev->dev, "failed to get firmware for pdsp\n");
158462306a36Sopenharmony_ci		return -ENODEV;
158562306a36Sopenharmony_ci	}
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	dev_info(kdev->dev, "firmware file %s downloaded for PDSP\n",
158862306a36Sopenharmony_ci		 knav_acc_firmwares[i]);
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	writel_relaxed(pdsp->id + 1, pdsp->command + 0x18);
159162306a36Sopenharmony_ci	/* download the firmware */
159262306a36Sopenharmony_ci	fwdata = (u32 *)fw->data;
159362306a36Sopenharmony_ci	fwlen = (fw->size + sizeof(u32) - 1) / sizeof(u32);
159462306a36Sopenharmony_ci	for (i = 0; i < fwlen; i++)
159562306a36Sopenharmony_ci		writel_relaxed(be32_to_cpu(fwdata[i]), pdsp->iram + i);
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	release_firmware(fw);
159862306a36Sopenharmony_ci	return 0;
159962306a36Sopenharmony_ci}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_cistatic int knav_queue_start_pdsp(struct knav_device *kdev,
160262306a36Sopenharmony_ci			   struct knav_pdsp_info *pdsp)
160362306a36Sopenharmony_ci{
160462306a36Sopenharmony_ci	u32 val, timeout = 1000;
160562306a36Sopenharmony_ci	int ret;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	/* write a command for sync */
160862306a36Sopenharmony_ci	writel_relaxed(0xffffffff, pdsp->command);
160962306a36Sopenharmony_ci	while (readl_relaxed(pdsp->command) != 0xffffffff)
161062306a36Sopenharmony_ci		cpu_relax();
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	/* soft reset the PDSP */
161362306a36Sopenharmony_ci	val  = readl_relaxed(&pdsp->regs->control);
161462306a36Sopenharmony_ci	val &= ~(PDSP_CTRL_PC_MASK | PDSP_CTRL_SOFT_RESET);
161562306a36Sopenharmony_ci	writel_relaxed(val, &pdsp->regs->control);
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	/* enable pdsp */
161862306a36Sopenharmony_ci	val = readl_relaxed(&pdsp->regs->control) | PDSP_CTRL_ENABLE;
161962306a36Sopenharmony_ci	writel_relaxed(val, &pdsp->regs->control);
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	/* wait for command register to clear */
162262306a36Sopenharmony_ci	ret = knav_queue_pdsp_wait(pdsp->command, timeout, 0);
162362306a36Sopenharmony_ci	if (ret < 0) {
162462306a36Sopenharmony_ci		dev_err(kdev->dev,
162562306a36Sopenharmony_ci			"timed out on pdsp %s command register wait\n",
162662306a36Sopenharmony_ci			pdsp->name);
162762306a36Sopenharmony_ci		return ret;
162862306a36Sopenharmony_ci	}
162962306a36Sopenharmony_ci	return 0;
163062306a36Sopenharmony_ci}
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_cistatic void knav_queue_stop_pdsps(struct knav_device *kdev)
163362306a36Sopenharmony_ci{
163462306a36Sopenharmony_ci	struct knav_pdsp_info *pdsp;
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	/* disable all pdsps */
163762306a36Sopenharmony_ci	for_each_pdsp(kdev, pdsp)
163862306a36Sopenharmony_ci		knav_queue_stop_pdsp(kdev, pdsp);
163962306a36Sopenharmony_ci}
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_cistatic int knav_queue_start_pdsps(struct knav_device *kdev)
164262306a36Sopenharmony_ci{
164362306a36Sopenharmony_ci	struct knav_pdsp_info *pdsp;
164462306a36Sopenharmony_ci	int ret;
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	knav_queue_stop_pdsps(kdev);
164762306a36Sopenharmony_ci	/* now load them all. We return success even if pdsp
164862306a36Sopenharmony_ci	 * is not loaded as acc channels are optional on having
164962306a36Sopenharmony_ci	 * firmware availability in the system. We set the loaded
165062306a36Sopenharmony_ci	 * and stated flag and when initialize the acc range, check
165162306a36Sopenharmony_ci	 * it and init the range only if pdsp is started.
165262306a36Sopenharmony_ci	 */
165362306a36Sopenharmony_ci	for_each_pdsp(kdev, pdsp) {
165462306a36Sopenharmony_ci		ret = knav_queue_load_pdsp(kdev, pdsp);
165562306a36Sopenharmony_ci		if (!ret)
165662306a36Sopenharmony_ci			pdsp->loaded = true;
165762306a36Sopenharmony_ci	}
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	for_each_pdsp(kdev, pdsp) {
166062306a36Sopenharmony_ci		if (pdsp->loaded) {
166162306a36Sopenharmony_ci			ret = knav_queue_start_pdsp(kdev, pdsp);
166262306a36Sopenharmony_ci			if (!ret)
166362306a36Sopenharmony_ci				pdsp->started = true;
166462306a36Sopenharmony_ci		}
166562306a36Sopenharmony_ci	}
166662306a36Sopenharmony_ci	return 0;
166762306a36Sopenharmony_ci}
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_cistatic inline struct knav_qmgr_info *knav_find_qmgr(unsigned id)
167062306a36Sopenharmony_ci{
167162306a36Sopenharmony_ci	struct knav_qmgr_info *qmgr;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	for_each_qmgr(kdev, qmgr) {
167462306a36Sopenharmony_ci		if ((id >= qmgr->start_queue) &&
167562306a36Sopenharmony_ci		    (id < qmgr->start_queue + qmgr->num_queues))
167662306a36Sopenharmony_ci			return qmgr;
167762306a36Sopenharmony_ci	}
167862306a36Sopenharmony_ci	return NULL;
167962306a36Sopenharmony_ci}
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_cistatic int knav_queue_init_queue(struct knav_device *kdev,
168262306a36Sopenharmony_ci					struct knav_range_info *range,
168362306a36Sopenharmony_ci					struct knav_queue_inst *inst,
168462306a36Sopenharmony_ci					unsigned id)
168562306a36Sopenharmony_ci{
168662306a36Sopenharmony_ci	char irq_name[KNAV_NAME_SIZE];
168762306a36Sopenharmony_ci	inst->qmgr = knav_find_qmgr(id);
168862306a36Sopenharmony_ci	if (!inst->qmgr)
168962306a36Sopenharmony_ci		return -1;
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci	INIT_LIST_HEAD(&inst->handles);
169262306a36Sopenharmony_ci	inst->kdev = kdev;
169362306a36Sopenharmony_ci	inst->range = range;
169462306a36Sopenharmony_ci	inst->irq_num = -1;
169562306a36Sopenharmony_ci	inst->id = id;
169662306a36Sopenharmony_ci	scnprintf(irq_name, sizeof(irq_name), "hwqueue-%d", id);
169762306a36Sopenharmony_ci	inst->irq_name = kstrndup(irq_name, sizeof(irq_name), GFP_KERNEL);
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	if (range->ops && range->ops->init_queue)
170062306a36Sopenharmony_ci		return range->ops->init_queue(range, inst);
170162306a36Sopenharmony_ci	else
170262306a36Sopenharmony_ci		return 0;
170362306a36Sopenharmony_ci}
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_cistatic int knav_queue_init_queues(struct knav_device *kdev)
170662306a36Sopenharmony_ci{
170762306a36Sopenharmony_ci	struct knav_range_info *range;
170862306a36Sopenharmony_ci	int size, id, base_idx;
170962306a36Sopenharmony_ci	int idx = 0, ret = 0;
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	/* how much do we need for instance data? */
171262306a36Sopenharmony_ci	size = sizeof(struct knav_queue_inst);
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	/* round this up to a power of 2, keep the index to instance
171562306a36Sopenharmony_ci	 * arithmetic fast.
171662306a36Sopenharmony_ci	 * */
171762306a36Sopenharmony_ci	kdev->inst_shift = order_base_2(size);
171862306a36Sopenharmony_ci	size = (1 << kdev->inst_shift) * kdev->num_queues_in_use;
171962306a36Sopenharmony_ci	kdev->instances = devm_kzalloc(kdev->dev, size, GFP_KERNEL);
172062306a36Sopenharmony_ci	if (!kdev->instances)
172162306a36Sopenharmony_ci		return -ENOMEM;
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci	for_each_queue_range(kdev, range) {
172462306a36Sopenharmony_ci		if (range->ops && range->ops->init_range)
172562306a36Sopenharmony_ci			range->ops->init_range(range);
172662306a36Sopenharmony_ci		base_idx = idx;
172762306a36Sopenharmony_ci		for (id = range->queue_base;
172862306a36Sopenharmony_ci		     id < range->queue_base + range->num_queues; id++, idx++) {
172962306a36Sopenharmony_ci			ret = knav_queue_init_queue(kdev, range,
173062306a36Sopenharmony_ci					knav_queue_idx_to_inst(kdev, idx), id);
173162306a36Sopenharmony_ci			if (ret < 0)
173262306a36Sopenharmony_ci				return ret;
173362306a36Sopenharmony_ci		}
173462306a36Sopenharmony_ci		range->queue_base_inst =
173562306a36Sopenharmony_ci			knav_queue_idx_to_inst(kdev, base_idx);
173662306a36Sopenharmony_ci	}
173762306a36Sopenharmony_ci	return 0;
173862306a36Sopenharmony_ci}
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci/* Match table for of_platform binding */
174162306a36Sopenharmony_cistatic const struct of_device_id keystone_qmss_of_match[] = {
174262306a36Sopenharmony_ci	{
174362306a36Sopenharmony_ci		.compatible = "ti,keystone-navigator-qmss",
174462306a36Sopenharmony_ci	},
174562306a36Sopenharmony_ci	{
174662306a36Sopenharmony_ci		.compatible = "ti,66ak2g-navss-qm",
174762306a36Sopenharmony_ci		.data	= (void *)QMSS_66AK2G,
174862306a36Sopenharmony_ci	},
174962306a36Sopenharmony_ci	{},
175062306a36Sopenharmony_ci};
175162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, keystone_qmss_of_match);
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_cistatic int knav_queue_probe(struct platform_device *pdev)
175462306a36Sopenharmony_ci{
175562306a36Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
175662306a36Sopenharmony_ci	struct device_node *qmgrs, *queue_pools, *regions, *pdsps;
175762306a36Sopenharmony_ci	const struct of_device_id *match;
175862306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
175962306a36Sopenharmony_ci	u32 temp[2];
176062306a36Sopenharmony_ci	int ret;
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	if (!node) {
176362306a36Sopenharmony_ci		dev_err(dev, "device tree info unavailable\n");
176462306a36Sopenharmony_ci		return -ENODEV;
176562306a36Sopenharmony_ci	}
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	kdev = devm_kzalloc(dev, sizeof(struct knav_device), GFP_KERNEL);
176862306a36Sopenharmony_ci	if (!kdev) {
176962306a36Sopenharmony_ci		dev_err(dev, "memory allocation failed\n");
177062306a36Sopenharmony_ci		return -ENOMEM;
177162306a36Sopenharmony_ci	}
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	match = of_match_device(of_match_ptr(keystone_qmss_of_match), dev);
177462306a36Sopenharmony_ci	if (match && match->data)
177562306a36Sopenharmony_ci		kdev->version = QMSS_66AK2G;
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	platform_set_drvdata(pdev, kdev);
177862306a36Sopenharmony_ci	kdev->dev = dev;
177962306a36Sopenharmony_ci	INIT_LIST_HEAD(&kdev->queue_ranges);
178062306a36Sopenharmony_ci	INIT_LIST_HEAD(&kdev->qmgrs);
178162306a36Sopenharmony_ci	INIT_LIST_HEAD(&kdev->pools);
178262306a36Sopenharmony_ci	INIT_LIST_HEAD(&kdev->regions);
178362306a36Sopenharmony_ci	INIT_LIST_HEAD(&kdev->pdsps);
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
178662306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(&pdev->dev);
178762306a36Sopenharmony_ci	if (ret < 0) {
178862306a36Sopenharmony_ci		pm_runtime_disable(&pdev->dev);
178962306a36Sopenharmony_ci		dev_err(dev, "Failed to enable QMSS\n");
179062306a36Sopenharmony_ci		return ret;
179162306a36Sopenharmony_ci	}
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	if (of_property_read_u32_array(node, "queue-range", temp, 2)) {
179462306a36Sopenharmony_ci		dev_err(dev, "queue-range not specified\n");
179562306a36Sopenharmony_ci		ret = -ENODEV;
179662306a36Sopenharmony_ci		goto err;
179762306a36Sopenharmony_ci	}
179862306a36Sopenharmony_ci	kdev->base_id    = temp[0];
179962306a36Sopenharmony_ci	kdev->num_queues = temp[1];
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	/* Initialize queue managers using device tree configuration */
180262306a36Sopenharmony_ci	qmgrs =  of_get_child_by_name(node, "qmgrs");
180362306a36Sopenharmony_ci	if (!qmgrs) {
180462306a36Sopenharmony_ci		dev_err(dev, "queue manager info not specified\n");
180562306a36Sopenharmony_ci		ret = -ENODEV;
180662306a36Sopenharmony_ci		goto err;
180762306a36Sopenharmony_ci	}
180862306a36Sopenharmony_ci	ret = knav_queue_init_qmgrs(kdev, qmgrs);
180962306a36Sopenharmony_ci	of_node_put(qmgrs);
181062306a36Sopenharmony_ci	if (ret)
181162306a36Sopenharmony_ci		goto err;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	/* get pdsp configuration values from device tree */
181462306a36Sopenharmony_ci	pdsps =  of_get_child_by_name(node, "pdsps");
181562306a36Sopenharmony_ci	if (pdsps) {
181662306a36Sopenharmony_ci		ret = knav_queue_init_pdsps(kdev, pdsps);
181762306a36Sopenharmony_ci		if (ret)
181862306a36Sopenharmony_ci			goto err;
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci		ret = knav_queue_start_pdsps(kdev);
182162306a36Sopenharmony_ci		if (ret)
182262306a36Sopenharmony_ci			goto err;
182362306a36Sopenharmony_ci	}
182462306a36Sopenharmony_ci	of_node_put(pdsps);
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	/* get usable queue range values from device tree */
182762306a36Sopenharmony_ci	queue_pools = of_get_child_by_name(node, "queue-pools");
182862306a36Sopenharmony_ci	if (!queue_pools) {
182962306a36Sopenharmony_ci		dev_err(dev, "queue-pools not specified\n");
183062306a36Sopenharmony_ci		ret = -ENODEV;
183162306a36Sopenharmony_ci		goto err;
183262306a36Sopenharmony_ci	}
183362306a36Sopenharmony_ci	ret = knav_setup_queue_pools(kdev, queue_pools);
183462306a36Sopenharmony_ci	of_node_put(queue_pools);
183562306a36Sopenharmony_ci	if (ret)
183662306a36Sopenharmony_ci		goto err;
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	ret = knav_get_link_ram(kdev, "linkram0", &kdev->link_rams[0]);
183962306a36Sopenharmony_ci	if (ret) {
184062306a36Sopenharmony_ci		dev_err(kdev->dev, "could not setup linking ram\n");
184162306a36Sopenharmony_ci		goto err;
184262306a36Sopenharmony_ci	}
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	ret = knav_get_link_ram(kdev, "linkram1", &kdev->link_rams[1]);
184562306a36Sopenharmony_ci	if (ret) {
184662306a36Sopenharmony_ci		/*
184762306a36Sopenharmony_ci		 * nothing really, we have one linking ram already, so we just
184862306a36Sopenharmony_ci		 * live within our means
184962306a36Sopenharmony_ci		 */
185062306a36Sopenharmony_ci	}
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	ret = knav_queue_setup_link_ram(kdev);
185362306a36Sopenharmony_ci	if (ret)
185462306a36Sopenharmony_ci		goto err;
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	regions = of_get_child_by_name(node, "descriptor-regions");
185762306a36Sopenharmony_ci	if (!regions) {
185862306a36Sopenharmony_ci		dev_err(dev, "descriptor-regions not specified\n");
185962306a36Sopenharmony_ci		ret = -ENODEV;
186062306a36Sopenharmony_ci		goto err;
186162306a36Sopenharmony_ci	}
186262306a36Sopenharmony_ci	ret = knav_queue_setup_regions(kdev, regions);
186362306a36Sopenharmony_ci	of_node_put(regions);
186462306a36Sopenharmony_ci	if (ret)
186562306a36Sopenharmony_ci		goto err;
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	ret = knav_queue_init_queues(kdev);
186862306a36Sopenharmony_ci	if (ret < 0) {
186962306a36Sopenharmony_ci		dev_err(dev, "hwqueue initialization failed\n");
187062306a36Sopenharmony_ci		goto err;
187162306a36Sopenharmony_ci	}
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	debugfs_create_file("qmss", S_IFREG | S_IRUGO, NULL, NULL,
187462306a36Sopenharmony_ci			    &knav_queue_debug_fops);
187562306a36Sopenharmony_ci	device_ready = true;
187662306a36Sopenharmony_ci	return 0;
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_cierr:
187962306a36Sopenharmony_ci	knav_queue_stop_pdsps(kdev);
188062306a36Sopenharmony_ci	knav_queue_free_regions(kdev);
188162306a36Sopenharmony_ci	knav_free_queue_ranges(kdev);
188262306a36Sopenharmony_ci	pm_runtime_put_sync(&pdev->dev);
188362306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
188462306a36Sopenharmony_ci	return ret;
188562306a36Sopenharmony_ci}
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_cistatic int knav_queue_remove(struct platform_device *pdev)
188862306a36Sopenharmony_ci{
188962306a36Sopenharmony_ci	/* TODO: Free resources */
189062306a36Sopenharmony_ci	pm_runtime_put_sync(&pdev->dev);
189162306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
189262306a36Sopenharmony_ci	return 0;
189362306a36Sopenharmony_ci}
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_cistatic struct platform_driver keystone_qmss_driver = {
189662306a36Sopenharmony_ci	.probe		= knav_queue_probe,
189762306a36Sopenharmony_ci	.remove		= knav_queue_remove,
189862306a36Sopenharmony_ci	.driver		= {
189962306a36Sopenharmony_ci		.name	= "keystone-navigator-qmss",
190062306a36Sopenharmony_ci		.of_match_table = keystone_qmss_of_match,
190162306a36Sopenharmony_ci	},
190262306a36Sopenharmony_ci};
190362306a36Sopenharmony_cimodule_platform_driver(keystone_qmss_driver);
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
190662306a36Sopenharmony_ciMODULE_DESCRIPTION("TI QMSS driver for Keystone SOCs");
190762306a36Sopenharmony_ciMODULE_AUTHOR("Sandeep Nair <sandeep_n@ti.com>");
190862306a36Sopenharmony_ciMODULE_AUTHOR("Santosh Shilimkar <santosh.shilimkar@ti.com>");
1909