18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Keystone Queue Manager subsystem driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
68c2ecf20Sopenharmony_ci * Authors:	Sandeep Nair <sandeep_n@ti.com>
78c2ecf20Sopenharmony_ci *		Cyril Chemparathy <cyril@ti.com>
88c2ecf20Sopenharmony_ci *		Santosh Shilimkar <santosh.shilimkar@ti.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
128c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
138c2ecf20Sopenharmony_ci#include <linux/firmware.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/of_address.h>
188c2ecf20Sopenharmony_ci#include <linux/of_device.h>
198c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
208c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/soc/ti/knav_qmss.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "knav_qmss.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic struct knav_device *kdev;
278c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(knav_dev_lock);
288c2ecf20Sopenharmony_ci#define knav_dev_lock_held() \
298c2ecf20Sopenharmony_ci	lockdep_is_held(&knav_dev_lock)
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* Queue manager register indices in DTS */
328c2ecf20Sopenharmony_ci#define KNAV_QUEUE_PEEK_REG_INDEX	0
338c2ecf20Sopenharmony_ci#define KNAV_QUEUE_STATUS_REG_INDEX	1
348c2ecf20Sopenharmony_ci#define KNAV_QUEUE_CONFIG_REG_INDEX	2
358c2ecf20Sopenharmony_ci#define KNAV_QUEUE_REGION_REG_INDEX	3
368c2ecf20Sopenharmony_ci#define KNAV_QUEUE_PUSH_REG_INDEX	4
378c2ecf20Sopenharmony_ci#define KNAV_QUEUE_POP_REG_INDEX	5
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* Queue manager register indices in DTS for QMSS in K2G NAVSS.
408c2ecf20Sopenharmony_ci * There are no status and vbusm push registers on this version
418c2ecf20Sopenharmony_ci * of QMSS. Push registers are same as pop, So all indices above 1
428c2ecf20Sopenharmony_ci * are to be re-defined
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_ci#define KNAV_L_QUEUE_CONFIG_REG_INDEX	1
458c2ecf20Sopenharmony_ci#define KNAV_L_QUEUE_REGION_REG_INDEX	2
468c2ecf20Sopenharmony_ci#define KNAV_L_QUEUE_PUSH_REG_INDEX	3
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* PDSP register indices in DTS */
498c2ecf20Sopenharmony_ci#define KNAV_QUEUE_PDSP_IRAM_REG_INDEX	0
508c2ecf20Sopenharmony_ci#define KNAV_QUEUE_PDSP_REGS_REG_INDEX	1
518c2ecf20Sopenharmony_ci#define KNAV_QUEUE_PDSP_INTD_REG_INDEX	2
528c2ecf20Sopenharmony_ci#define KNAV_QUEUE_PDSP_CMD_REG_INDEX	3
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define knav_queue_idx_to_inst(kdev, idx)			\
558c2ecf20Sopenharmony_ci	(kdev->instances + (idx << kdev->inst_shift))
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define for_each_handle_rcu(qh, inst)				\
588c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(qh, &inst->handles, list,	\
598c2ecf20Sopenharmony_ci				knav_dev_lock_held())
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define for_each_instance(idx, inst, kdev)		\
628c2ecf20Sopenharmony_ci	for (idx = 0, inst = kdev->instances;		\
638c2ecf20Sopenharmony_ci	     idx < (kdev)->num_queues_in_use;			\
648c2ecf20Sopenharmony_ci	     idx++, inst = knav_queue_idx_to_inst(kdev, idx))
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* All firmware file names end up here. List the firmware file names below.
678c2ecf20Sopenharmony_ci * Newest followed by older ones. Search is done from start of the array
688c2ecf20Sopenharmony_ci * until a firmware file is found.
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_cistatic const char * const knav_acc_firmwares[] = {"ks2_qmss_pdsp_acc48.bin"};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic bool device_ready;
738c2ecf20Sopenharmony_cibool knav_qmss_device_ready(void)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	return device_ready;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_qmss_device_ready);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/**
808c2ecf20Sopenharmony_ci * knav_queue_notify: qmss queue notfier call
818c2ecf20Sopenharmony_ci *
828c2ecf20Sopenharmony_ci * @inst:		qmss queue instance like accumulator
838c2ecf20Sopenharmony_ci */
848c2ecf20Sopenharmony_civoid knav_queue_notify(struct knav_queue_inst *inst)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct knav_queue *qh;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (!inst)
898c2ecf20Sopenharmony_ci		return;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	rcu_read_lock();
928c2ecf20Sopenharmony_ci	for_each_handle_rcu(qh, inst) {
938c2ecf20Sopenharmony_ci		if (atomic_read(&qh->notifier_enabled) <= 0)
948c2ecf20Sopenharmony_ci			continue;
958c2ecf20Sopenharmony_ci		if (WARN_ON(!qh->notifier_fn))
968c2ecf20Sopenharmony_ci			continue;
978c2ecf20Sopenharmony_ci		this_cpu_inc(qh->stats->notifies);
988c2ecf20Sopenharmony_ci		qh->notifier_fn(qh->notifier_fn_arg);
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci	rcu_read_unlock();
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_queue_notify);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic irqreturn_t knav_queue_int_handler(int irq, void *_instdata)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct knav_queue_inst *inst = _instdata;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	knav_queue_notify(inst);
1098c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int knav_queue_setup_irq(struct knav_range_info *range,
1138c2ecf20Sopenharmony_ci			  struct knav_queue_inst *inst)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	unsigned queue = inst->id - range->queue_base;
1168c2ecf20Sopenharmony_ci	int ret = 0, irq;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (range->flags & RANGE_HAS_IRQ) {
1198c2ecf20Sopenharmony_ci		irq = range->irqs[queue].irq;
1208c2ecf20Sopenharmony_ci		ret = request_irq(irq, knav_queue_int_handler, 0,
1218c2ecf20Sopenharmony_ci					inst->irq_name, inst);
1228c2ecf20Sopenharmony_ci		if (ret)
1238c2ecf20Sopenharmony_ci			return ret;
1248c2ecf20Sopenharmony_ci		disable_irq(irq);
1258c2ecf20Sopenharmony_ci		if (range->irqs[queue].cpu_mask) {
1268c2ecf20Sopenharmony_ci			ret = irq_set_affinity_hint(irq, range->irqs[queue].cpu_mask);
1278c2ecf20Sopenharmony_ci			if (ret) {
1288c2ecf20Sopenharmony_ci				dev_warn(range->kdev->dev,
1298c2ecf20Sopenharmony_ci					 "Failed to set IRQ affinity\n");
1308c2ecf20Sopenharmony_ci				return ret;
1318c2ecf20Sopenharmony_ci			}
1328c2ecf20Sopenharmony_ci		}
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci	return ret;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic void knav_queue_free_irq(struct knav_queue_inst *inst)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct knav_range_info *range = inst->range;
1408c2ecf20Sopenharmony_ci	unsigned queue = inst->id - inst->range->queue_base;
1418c2ecf20Sopenharmony_ci	int irq;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (range->flags & RANGE_HAS_IRQ) {
1448c2ecf20Sopenharmony_ci		irq = range->irqs[queue].irq;
1458c2ecf20Sopenharmony_ci		irq_set_affinity_hint(irq, NULL);
1468c2ecf20Sopenharmony_ci		free_irq(irq, inst);
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic inline bool knav_queue_is_busy(struct knav_queue_inst *inst)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	return !list_empty(&inst->handles);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic inline bool knav_queue_is_reserved(struct knav_queue_inst *inst)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	return inst->range->flags & RANGE_RESERVED;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic inline bool knav_queue_is_shared(struct knav_queue_inst *inst)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct knav_queue *tmp;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	rcu_read_lock();
1658c2ecf20Sopenharmony_ci	for_each_handle_rcu(tmp, inst) {
1668c2ecf20Sopenharmony_ci		if (tmp->flags & KNAV_QUEUE_SHARED) {
1678c2ecf20Sopenharmony_ci			rcu_read_unlock();
1688c2ecf20Sopenharmony_ci			return true;
1698c2ecf20Sopenharmony_ci		}
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci	rcu_read_unlock();
1728c2ecf20Sopenharmony_ci	return false;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic inline bool knav_queue_match_type(struct knav_queue_inst *inst,
1768c2ecf20Sopenharmony_ci						unsigned type)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	if ((type == KNAV_QUEUE_QPEND) &&
1798c2ecf20Sopenharmony_ci	    (inst->range->flags & RANGE_HAS_IRQ)) {
1808c2ecf20Sopenharmony_ci		return true;
1818c2ecf20Sopenharmony_ci	} else if ((type == KNAV_QUEUE_ACC) &&
1828c2ecf20Sopenharmony_ci		(inst->range->flags & RANGE_HAS_ACCUMULATOR)) {
1838c2ecf20Sopenharmony_ci		return true;
1848c2ecf20Sopenharmony_ci	} else if ((type == KNAV_QUEUE_GP) &&
1858c2ecf20Sopenharmony_ci		!(inst->range->flags &
1868c2ecf20Sopenharmony_ci			(RANGE_HAS_ACCUMULATOR | RANGE_HAS_IRQ))) {
1878c2ecf20Sopenharmony_ci		return true;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci	return false;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic inline struct knav_queue_inst *
1938c2ecf20Sopenharmony_ciknav_queue_match_id_to_inst(struct knav_device *kdev, unsigned id)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct knav_queue_inst *inst;
1968c2ecf20Sopenharmony_ci	int idx;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	for_each_instance(idx, inst, kdev) {
1998c2ecf20Sopenharmony_ci		if (inst->id == id)
2008c2ecf20Sopenharmony_ci			return inst;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	return NULL;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic inline struct knav_queue_inst *knav_queue_find_by_id(int id)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	if (kdev->base_id <= id &&
2088c2ecf20Sopenharmony_ci	    kdev->base_id + kdev->num_queues > id) {
2098c2ecf20Sopenharmony_ci		id -= kdev->base_id;
2108c2ecf20Sopenharmony_ci		return knav_queue_match_id_to_inst(kdev, id);
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci	return NULL;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic struct knav_queue *__knav_queue_open(struct knav_queue_inst *inst,
2168c2ecf20Sopenharmony_ci				      const char *name, unsigned flags)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	struct knav_queue *qh;
2198c2ecf20Sopenharmony_ci	unsigned id;
2208c2ecf20Sopenharmony_ci	int ret = 0;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	qh = devm_kzalloc(inst->kdev->dev, sizeof(*qh), GFP_KERNEL);
2238c2ecf20Sopenharmony_ci	if (!qh)
2248c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	qh->stats = alloc_percpu(struct knav_queue_stats);
2278c2ecf20Sopenharmony_ci	if (!qh->stats) {
2288c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2298c2ecf20Sopenharmony_ci		goto err;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	qh->flags = flags;
2338c2ecf20Sopenharmony_ci	qh->inst = inst;
2348c2ecf20Sopenharmony_ci	id = inst->id - inst->qmgr->start_queue;
2358c2ecf20Sopenharmony_ci	qh->reg_push = &inst->qmgr->reg_push[id];
2368c2ecf20Sopenharmony_ci	qh->reg_pop = &inst->qmgr->reg_pop[id];
2378c2ecf20Sopenharmony_ci	qh->reg_peek = &inst->qmgr->reg_peek[id];
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* first opener? */
2408c2ecf20Sopenharmony_ci	if (!knav_queue_is_busy(inst)) {
2418c2ecf20Sopenharmony_ci		struct knav_range_info *range = inst->range;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		inst->name = kstrndup(name, KNAV_NAME_SIZE - 1, GFP_KERNEL);
2448c2ecf20Sopenharmony_ci		if (range->ops && range->ops->open_queue)
2458c2ecf20Sopenharmony_ci			ret = range->ops->open_queue(range, inst, flags);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		if (ret)
2488c2ecf20Sopenharmony_ci			goto err;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci	list_add_tail_rcu(&qh->list, &inst->handles);
2518c2ecf20Sopenharmony_ci	return qh;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cierr:
2548c2ecf20Sopenharmony_ci	if (qh->stats)
2558c2ecf20Sopenharmony_ci		free_percpu(qh->stats);
2568c2ecf20Sopenharmony_ci	devm_kfree(inst->kdev->dev, qh);
2578c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic struct knav_queue *
2618c2ecf20Sopenharmony_ciknav_queue_open_by_id(const char *name, unsigned id, unsigned flags)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	struct knav_queue_inst *inst;
2648c2ecf20Sopenharmony_ci	struct knav_queue *qh;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	mutex_lock(&knav_dev_lock);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	qh = ERR_PTR(-ENODEV);
2698c2ecf20Sopenharmony_ci	inst = knav_queue_find_by_id(id);
2708c2ecf20Sopenharmony_ci	if (!inst)
2718c2ecf20Sopenharmony_ci		goto unlock_ret;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	qh = ERR_PTR(-EEXIST);
2748c2ecf20Sopenharmony_ci	if (!(flags & KNAV_QUEUE_SHARED) && knav_queue_is_busy(inst))
2758c2ecf20Sopenharmony_ci		goto unlock_ret;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	qh = ERR_PTR(-EBUSY);
2788c2ecf20Sopenharmony_ci	if ((flags & KNAV_QUEUE_SHARED) &&
2798c2ecf20Sopenharmony_ci	    (knav_queue_is_busy(inst) && !knav_queue_is_shared(inst)))
2808c2ecf20Sopenharmony_ci		goto unlock_ret;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	qh = __knav_queue_open(inst, name, flags);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ciunlock_ret:
2858c2ecf20Sopenharmony_ci	mutex_unlock(&knav_dev_lock);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return qh;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic struct knav_queue *knav_queue_open_by_type(const char *name,
2918c2ecf20Sopenharmony_ci						unsigned type, unsigned flags)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct knav_queue_inst *inst;
2948c2ecf20Sopenharmony_ci	struct knav_queue *qh = ERR_PTR(-EINVAL);
2958c2ecf20Sopenharmony_ci	int idx;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	mutex_lock(&knav_dev_lock);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	for_each_instance(idx, inst, kdev) {
3008c2ecf20Sopenharmony_ci		if (knav_queue_is_reserved(inst))
3018c2ecf20Sopenharmony_ci			continue;
3028c2ecf20Sopenharmony_ci		if (!knav_queue_match_type(inst, type))
3038c2ecf20Sopenharmony_ci			continue;
3048c2ecf20Sopenharmony_ci		if (knav_queue_is_busy(inst))
3058c2ecf20Sopenharmony_ci			continue;
3068c2ecf20Sopenharmony_ci		qh = __knav_queue_open(inst, name, flags);
3078c2ecf20Sopenharmony_ci		goto unlock_ret;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ciunlock_ret:
3118c2ecf20Sopenharmony_ci	mutex_unlock(&knav_dev_lock);
3128c2ecf20Sopenharmony_ci	return qh;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic void knav_queue_set_notify(struct knav_queue_inst *inst, bool enabled)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	struct knav_range_info *range = inst->range;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (range->ops && range->ops->set_notify)
3208c2ecf20Sopenharmony_ci		range->ops->set_notify(range, inst, enabled);
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic int knav_queue_enable_notifier(struct knav_queue *qh)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct knav_queue_inst *inst = qh->inst;
3268c2ecf20Sopenharmony_ci	bool first;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if (WARN_ON(!qh->notifier_fn))
3298c2ecf20Sopenharmony_ci		return -EINVAL;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/* Adjust the per handle notifier count */
3328c2ecf20Sopenharmony_ci	first = (atomic_inc_return(&qh->notifier_enabled) == 1);
3338c2ecf20Sopenharmony_ci	if (!first)
3348c2ecf20Sopenharmony_ci		return 0; /* nothing to do */
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* Now adjust the per instance notifier count */
3378c2ecf20Sopenharmony_ci	first = (atomic_inc_return(&inst->num_notifiers) == 1);
3388c2ecf20Sopenharmony_ci	if (first)
3398c2ecf20Sopenharmony_ci		knav_queue_set_notify(inst, true);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	return 0;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic int knav_queue_disable_notifier(struct knav_queue *qh)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct knav_queue_inst *inst = qh->inst;
3478c2ecf20Sopenharmony_ci	bool last;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	last = (atomic_dec_return(&qh->notifier_enabled) == 0);
3508c2ecf20Sopenharmony_ci	if (!last)
3518c2ecf20Sopenharmony_ci		return 0; /* nothing to do */
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	last = (atomic_dec_return(&inst->num_notifiers) == 0);
3548c2ecf20Sopenharmony_ci	if (last)
3558c2ecf20Sopenharmony_ci		knav_queue_set_notify(inst, false);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	return 0;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int knav_queue_set_notifier(struct knav_queue *qh,
3618c2ecf20Sopenharmony_ci				struct knav_queue_notify_config *cfg)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	knav_queue_notify_fn old_fn = qh->notifier_fn;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (!cfg)
3668c2ecf20Sopenharmony_ci		return -EINVAL;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (!(qh->inst->range->flags & (RANGE_HAS_ACCUMULATOR | RANGE_HAS_IRQ)))
3698c2ecf20Sopenharmony_ci		return -ENOTSUPP;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (!cfg->fn && old_fn)
3728c2ecf20Sopenharmony_ci		knav_queue_disable_notifier(qh);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	qh->notifier_fn = cfg->fn;
3758c2ecf20Sopenharmony_ci	qh->notifier_fn_arg = cfg->fn_arg;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (cfg->fn && !old_fn)
3788c2ecf20Sopenharmony_ci		knav_queue_enable_notifier(qh);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return 0;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic int knav_gp_set_notify(struct knav_range_info *range,
3848c2ecf20Sopenharmony_ci			       struct knav_queue_inst *inst,
3858c2ecf20Sopenharmony_ci			       bool enabled)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	unsigned queue;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	if (range->flags & RANGE_HAS_IRQ) {
3908c2ecf20Sopenharmony_ci		queue = inst->id - range->queue_base;
3918c2ecf20Sopenharmony_ci		if (enabled)
3928c2ecf20Sopenharmony_ci			enable_irq(range->irqs[queue].irq);
3938c2ecf20Sopenharmony_ci		else
3948c2ecf20Sopenharmony_ci			disable_irq_nosync(range->irqs[queue].irq);
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci	return 0;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic int knav_gp_open_queue(struct knav_range_info *range,
4008c2ecf20Sopenharmony_ci				struct knav_queue_inst *inst, unsigned flags)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	return knav_queue_setup_irq(range, inst);
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic int knav_gp_close_queue(struct knav_range_info *range,
4068c2ecf20Sopenharmony_ci				struct knav_queue_inst *inst)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	knav_queue_free_irq(inst);
4098c2ecf20Sopenharmony_ci	return 0;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic struct knav_range_ops knav_gp_range_ops = {
4138c2ecf20Sopenharmony_ci	.set_notify	= knav_gp_set_notify,
4148c2ecf20Sopenharmony_ci	.open_queue	= knav_gp_open_queue,
4158c2ecf20Sopenharmony_ci	.close_queue	= knav_gp_close_queue,
4168c2ecf20Sopenharmony_ci};
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic int knav_queue_get_count(void *qhandle)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	struct knav_queue *qh = qhandle;
4228c2ecf20Sopenharmony_ci	struct knav_queue_inst *inst = qh->inst;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	return readl_relaxed(&qh->reg_peek[0].entry_count) +
4258c2ecf20Sopenharmony_ci		atomic_read(&inst->desc_count);
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic void knav_queue_debug_show_instance(struct seq_file *s,
4298c2ecf20Sopenharmony_ci					struct knav_queue_inst *inst)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	struct knav_device *kdev = inst->kdev;
4328c2ecf20Sopenharmony_ci	struct knav_queue *qh;
4338c2ecf20Sopenharmony_ci	int cpu = 0;
4348c2ecf20Sopenharmony_ci	int pushes = 0;
4358c2ecf20Sopenharmony_ci	int pops = 0;
4368c2ecf20Sopenharmony_ci	int push_errors = 0;
4378c2ecf20Sopenharmony_ci	int pop_errors = 0;
4388c2ecf20Sopenharmony_ci	int notifies = 0;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if (!knav_queue_is_busy(inst))
4418c2ecf20Sopenharmony_ci		return;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	seq_printf(s, "\tqueue id %d (%s)\n",
4448c2ecf20Sopenharmony_ci		   kdev->base_id + inst->id, inst->name);
4458c2ecf20Sopenharmony_ci	for_each_handle_rcu(qh, inst) {
4468c2ecf20Sopenharmony_ci		for_each_possible_cpu(cpu) {
4478c2ecf20Sopenharmony_ci			pushes += per_cpu_ptr(qh->stats, cpu)->pushes;
4488c2ecf20Sopenharmony_ci			pops += per_cpu_ptr(qh->stats, cpu)->pops;
4498c2ecf20Sopenharmony_ci			push_errors += per_cpu_ptr(qh->stats, cpu)->push_errors;
4508c2ecf20Sopenharmony_ci			pop_errors += per_cpu_ptr(qh->stats, cpu)->pop_errors;
4518c2ecf20Sopenharmony_ci			notifies += per_cpu_ptr(qh->stats, cpu)->notifies;
4528c2ecf20Sopenharmony_ci		}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci		seq_printf(s, "\t\thandle %p: pushes %8d, pops %8d, count %8d, notifies %8d, push errors %8d, pop errors %8d\n",
4558c2ecf20Sopenharmony_ci				qh,
4568c2ecf20Sopenharmony_ci				pushes,
4578c2ecf20Sopenharmony_ci				pops,
4588c2ecf20Sopenharmony_ci				knav_queue_get_count(qh),
4598c2ecf20Sopenharmony_ci				notifies,
4608c2ecf20Sopenharmony_ci				push_errors,
4618c2ecf20Sopenharmony_ci				pop_errors);
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic int knav_queue_debug_show(struct seq_file *s, void *v)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	struct knav_queue_inst *inst;
4688c2ecf20Sopenharmony_ci	int idx;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	mutex_lock(&knav_dev_lock);
4718c2ecf20Sopenharmony_ci	seq_printf(s, "%s: %u-%u\n",
4728c2ecf20Sopenharmony_ci		   dev_name(kdev->dev), kdev->base_id,
4738c2ecf20Sopenharmony_ci		   kdev->base_id + kdev->num_queues - 1);
4748c2ecf20Sopenharmony_ci	for_each_instance(idx, inst, kdev)
4758c2ecf20Sopenharmony_ci		knav_queue_debug_show_instance(s, inst);
4768c2ecf20Sopenharmony_ci	mutex_unlock(&knav_dev_lock);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	return 0;
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(knav_queue_debug);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic inline int knav_queue_pdsp_wait(u32 * __iomem addr, unsigned timeout,
4848c2ecf20Sopenharmony_ci					u32 flags)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	unsigned long end;
4878c2ecf20Sopenharmony_ci	u32 val = 0;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	end = jiffies + msecs_to_jiffies(timeout);
4908c2ecf20Sopenharmony_ci	while (time_after(end, jiffies)) {
4918c2ecf20Sopenharmony_ci		val = readl_relaxed(addr);
4928c2ecf20Sopenharmony_ci		if (flags)
4938c2ecf20Sopenharmony_ci			val &= flags;
4948c2ecf20Sopenharmony_ci		if (!val)
4958c2ecf20Sopenharmony_ci			break;
4968c2ecf20Sopenharmony_ci		cpu_relax();
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci	return val ? -ETIMEDOUT : 0;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic int knav_queue_flush(struct knav_queue *qh)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	struct knav_queue_inst *inst = qh->inst;
5058c2ecf20Sopenharmony_ci	unsigned id = inst->id - inst->qmgr->start_queue;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	atomic_set(&inst->desc_count, 0);
5088c2ecf20Sopenharmony_ci	writel_relaxed(0, &inst->qmgr->reg_push[id].ptr_size_thresh);
5098c2ecf20Sopenharmony_ci	return 0;
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci/**
5138c2ecf20Sopenharmony_ci * knav_queue_open()	- open a hardware queue
5148c2ecf20Sopenharmony_ci * @name		- name to give the queue handle
5158c2ecf20Sopenharmony_ci * @id			- desired queue number if any or specifes the type
5168c2ecf20Sopenharmony_ci *			  of queue
5178c2ecf20Sopenharmony_ci * @flags		- the following flags are applicable to queues:
5188c2ecf20Sopenharmony_ci *	KNAV_QUEUE_SHARED - allow the queue to be shared. Queues are
5198c2ecf20Sopenharmony_ci *			     exclusive by default.
5208c2ecf20Sopenharmony_ci *			     Subsequent attempts to open a shared queue should
5218c2ecf20Sopenharmony_ci *			     also have this flag.
5228c2ecf20Sopenharmony_ci *
5238c2ecf20Sopenharmony_ci * Returns a handle to the open hardware queue if successful. Use IS_ERR()
5248c2ecf20Sopenharmony_ci * to check the returned value for error codes.
5258c2ecf20Sopenharmony_ci */
5268c2ecf20Sopenharmony_civoid *knav_queue_open(const char *name, unsigned id,
5278c2ecf20Sopenharmony_ci					unsigned flags)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	struct knav_queue *qh = ERR_PTR(-EINVAL);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	switch (id) {
5328c2ecf20Sopenharmony_ci	case KNAV_QUEUE_QPEND:
5338c2ecf20Sopenharmony_ci	case KNAV_QUEUE_ACC:
5348c2ecf20Sopenharmony_ci	case KNAV_QUEUE_GP:
5358c2ecf20Sopenharmony_ci		qh = knav_queue_open_by_type(name, id, flags);
5368c2ecf20Sopenharmony_ci		break;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	default:
5398c2ecf20Sopenharmony_ci		qh = knav_queue_open_by_id(name, id, flags);
5408c2ecf20Sopenharmony_ci		break;
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci	return qh;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_queue_open);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci/**
5478c2ecf20Sopenharmony_ci * knav_queue_close()	- close a hardware queue handle
5488c2ecf20Sopenharmony_ci * @qh			- handle to close
5498c2ecf20Sopenharmony_ci */
5508c2ecf20Sopenharmony_civoid knav_queue_close(void *qhandle)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	struct knav_queue *qh = qhandle;
5538c2ecf20Sopenharmony_ci	struct knav_queue_inst *inst = qh->inst;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	while (atomic_read(&qh->notifier_enabled) > 0)
5568c2ecf20Sopenharmony_ci		knav_queue_disable_notifier(qh);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	mutex_lock(&knav_dev_lock);
5598c2ecf20Sopenharmony_ci	list_del_rcu(&qh->list);
5608c2ecf20Sopenharmony_ci	mutex_unlock(&knav_dev_lock);
5618c2ecf20Sopenharmony_ci	synchronize_rcu();
5628c2ecf20Sopenharmony_ci	if (!knav_queue_is_busy(inst)) {
5638c2ecf20Sopenharmony_ci		struct knav_range_info *range = inst->range;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci		if (range->ops && range->ops->close_queue)
5668c2ecf20Sopenharmony_ci			range->ops->close_queue(range, inst);
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci	free_percpu(qh->stats);
5698c2ecf20Sopenharmony_ci	devm_kfree(inst->kdev->dev, qh);
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_queue_close);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci/**
5748c2ecf20Sopenharmony_ci * knav_queue_device_control()	- Perform control operations on a queue
5758c2ecf20Sopenharmony_ci * @qh				- queue handle
5768c2ecf20Sopenharmony_ci * @cmd				- control commands
5778c2ecf20Sopenharmony_ci * @arg				- command argument
5788c2ecf20Sopenharmony_ci *
5798c2ecf20Sopenharmony_ci * Returns 0 on success, errno otherwise.
5808c2ecf20Sopenharmony_ci */
5818c2ecf20Sopenharmony_ciint knav_queue_device_control(void *qhandle, enum knav_queue_ctrl_cmd cmd,
5828c2ecf20Sopenharmony_ci				unsigned long arg)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	struct knav_queue *qh = qhandle;
5858c2ecf20Sopenharmony_ci	struct knav_queue_notify_config *cfg;
5868c2ecf20Sopenharmony_ci	int ret;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	switch ((int)cmd) {
5898c2ecf20Sopenharmony_ci	case KNAV_QUEUE_GET_ID:
5908c2ecf20Sopenharmony_ci		ret = qh->inst->kdev->base_id + qh->inst->id;
5918c2ecf20Sopenharmony_ci		break;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	case KNAV_QUEUE_FLUSH:
5948c2ecf20Sopenharmony_ci		ret = knav_queue_flush(qh);
5958c2ecf20Sopenharmony_ci		break;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	case KNAV_QUEUE_SET_NOTIFIER:
5988c2ecf20Sopenharmony_ci		cfg = (void *)arg;
5998c2ecf20Sopenharmony_ci		ret = knav_queue_set_notifier(qh, cfg);
6008c2ecf20Sopenharmony_ci		break;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	case KNAV_QUEUE_ENABLE_NOTIFY:
6038c2ecf20Sopenharmony_ci		ret = knav_queue_enable_notifier(qh);
6048c2ecf20Sopenharmony_ci		break;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	case KNAV_QUEUE_DISABLE_NOTIFY:
6078c2ecf20Sopenharmony_ci		ret = knav_queue_disable_notifier(qh);
6088c2ecf20Sopenharmony_ci		break;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	case KNAV_QUEUE_GET_COUNT:
6118c2ecf20Sopenharmony_ci		ret = knav_queue_get_count(qh);
6128c2ecf20Sopenharmony_ci		break;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	default:
6158c2ecf20Sopenharmony_ci		ret = -ENOTSUPP;
6168c2ecf20Sopenharmony_ci		break;
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci	return ret;
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_queue_device_control);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci/**
6258c2ecf20Sopenharmony_ci * knav_queue_push()	- push data (or descriptor) to the tail of a queue
6268c2ecf20Sopenharmony_ci * @qh			- hardware queue handle
6278c2ecf20Sopenharmony_ci * @data		- data to push
6288c2ecf20Sopenharmony_ci * @size		- size of data to push
6298c2ecf20Sopenharmony_ci * @flags		- can be used to pass additional information
6308c2ecf20Sopenharmony_ci *
6318c2ecf20Sopenharmony_ci * Returns 0 on success, errno otherwise.
6328c2ecf20Sopenharmony_ci */
6338c2ecf20Sopenharmony_ciint knav_queue_push(void *qhandle, dma_addr_t dma,
6348c2ecf20Sopenharmony_ci					unsigned size, unsigned flags)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	struct knav_queue *qh = qhandle;
6378c2ecf20Sopenharmony_ci	u32 val;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	val = (u32)dma | ((size / 16) - 1);
6408c2ecf20Sopenharmony_ci	writel_relaxed(val, &qh->reg_push[0].ptr_size_thresh);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	this_cpu_inc(qh->stats->pushes);
6438c2ecf20Sopenharmony_ci	return 0;
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_queue_push);
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci/**
6488c2ecf20Sopenharmony_ci * knav_queue_pop()	- pop data (or descriptor) from the head of a queue
6498c2ecf20Sopenharmony_ci * @qh			- hardware queue handle
6508c2ecf20Sopenharmony_ci * @size		- (optional) size of the data pop'ed.
6518c2ecf20Sopenharmony_ci *
6528c2ecf20Sopenharmony_ci * Returns a DMA address on success, 0 on failure.
6538c2ecf20Sopenharmony_ci */
6548c2ecf20Sopenharmony_cidma_addr_t knav_queue_pop(void *qhandle, unsigned *size)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	struct knav_queue *qh = qhandle;
6578c2ecf20Sopenharmony_ci	struct knav_queue_inst *inst = qh->inst;
6588c2ecf20Sopenharmony_ci	dma_addr_t dma;
6598c2ecf20Sopenharmony_ci	u32 val, idx;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	/* are we accumulated? */
6628c2ecf20Sopenharmony_ci	if (inst->descs) {
6638c2ecf20Sopenharmony_ci		if (unlikely(atomic_dec_return(&inst->desc_count) < 0)) {
6648c2ecf20Sopenharmony_ci			atomic_inc(&inst->desc_count);
6658c2ecf20Sopenharmony_ci			return 0;
6668c2ecf20Sopenharmony_ci		}
6678c2ecf20Sopenharmony_ci		idx  = atomic_inc_return(&inst->desc_head);
6688c2ecf20Sopenharmony_ci		idx &= ACC_DESCS_MASK;
6698c2ecf20Sopenharmony_ci		val = inst->descs[idx];
6708c2ecf20Sopenharmony_ci	} else {
6718c2ecf20Sopenharmony_ci		val = readl_relaxed(&qh->reg_pop[0].ptr_size_thresh);
6728c2ecf20Sopenharmony_ci		if (unlikely(!val))
6738c2ecf20Sopenharmony_ci			return 0;
6748c2ecf20Sopenharmony_ci	}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	dma = val & DESC_PTR_MASK;
6778c2ecf20Sopenharmony_ci	if (size)
6788c2ecf20Sopenharmony_ci		*size = ((val & DESC_SIZE_MASK) + 1) * 16;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	this_cpu_inc(qh->stats->pops);
6818c2ecf20Sopenharmony_ci	return dma;
6828c2ecf20Sopenharmony_ci}
6838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_queue_pop);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci/* carve out descriptors and push into queue */
6868c2ecf20Sopenharmony_cistatic void kdesc_fill_pool(struct knav_pool *pool)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	struct knav_region *region;
6898c2ecf20Sopenharmony_ci	int i;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	region = pool->region;
6928c2ecf20Sopenharmony_ci	pool->desc_size = region->desc_size;
6938c2ecf20Sopenharmony_ci	for (i = 0; i < pool->num_desc; i++) {
6948c2ecf20Sopenharmony_ci		int index = pool->region_offset + i;
6958c2ecf20Sopenharmony_ci		dma_addr_t dma_addr;
6968c2ecf20Sopenharmony_ci		unsigned dma_size;
6978c2ecf20Sopenharmony_ci		dma_addr = region->dma_start + (region->desc_size * index);
6988c2ecf20Sopenharmony_ci		dma_size = ALIGN(pool->desc_size, SMP_CACHE_BYTES);
6998c2ecf20Sopenharmony_ci		dma_sync_single_for_device(pool->dev, dma_addr, dma_size,
7008c2ecf20Sopenharmony_ci					   DMA_TO_DEVICE);
7018c2ecf20Sopenharmony_ci		knav_queue_push(pool->queue, dma_addr, dma_size, 0);
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci/* pop out descriptors and close the queue */
7068c2ecf20Sopenharmony_cistatic void kdesc_empty_pool(struct knav_pool *pool)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	dma_addr_t dma;
7098c2ecf20Sopenharmony_ci	unsigned size;
7108c2ecf20Sopenharmony_ci	void *desc;
7118c2ecf20Sopenharmony_ci	int i;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	if (!pool->queue)
7148c2ecf20Sopenharmony_ci		return;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	for (i = 0;; i++) {
7178c2ecf20Sopenharmony_ci		dma = knav_queue_pop(pool->queue, &size);
7188c2ecf20Sopenharmony_ci		if (!dma)
7198c2ecf20Sopenharmony_ci			break;
7208c2ecf20Sopenharmony_ci		desc = knav_pool_desc_dma_to_virt(pool, dma);
7218c2ecf20Sopenharmony_ci		if (!desc) {
7228c2ecf20Sopenharmony_ci			dev_dbg(pool->kdev->dev,
7238c2ecf20Sopenharmony_ci				"couldn't unmap desc, continuing\n");
7248c2ecf20Sopenharmony_ci			continue;
7258c2ecf20Sopenharmony_ci		}
7268c2ecf20Sopenharmony_ci	}
7278c2ecf20Sopenharmony_ci	WARN_ON(i != pool->num_desc);
7288c2ecf20Sopenharmony_ci	knav_queue_close(pool->queue);
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci/* Get the DMA address of a descriptor */
7338c2ecf20Sopenharmony_cidma_addr_t knav_pool_desc_virt_to_dma(void *ph, void *virt)
7348c2ecf20Sopenharmony_ci{
7358c2ecf20Sopenharmony_ci	struct knav_pool *pool = ph;
7368c2ecf20Sopenharmony_ci	return pool->region->dma_start + (virt - pool->region->virt_start);
7378c2ecf20Sopenharmony_ci}
7388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_desc_virt_to_dma);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_civoid *knav_pool_desc_dma_to_virt(void *ph, dma_addr_t dma)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	struct knav_pool *pool = ph;
7438c2ecf20Sopenharmony_ci	return pool->region->virt_start + (dma - pool->region->dma_start);
7448c2ecf20Sopenharmony_ci}
7458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_desc_dma_to_virt);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci/**
7488c2ecf20Sopenharmony_ci * knav_pool_create()	- Create a pool of descriptors
7498c2ecf20Sopenharmony_ci * @name		- name to give the pool handle
7508c2ecf20Sopenharmony_ci * @num_desc		- numbers of descriptors in the pool
7518c2ecf20Sopenharmony_ci * @region_id		- QMSS region id from which the descriptors are to be
7528c2ecf20Sopenharmony_ci *			  allocated.
7538c2ecf20Sopenharmony_ci *
7548c2ecf20Sopenharmony_ci * Returns a pool handle on success.
7558c2ecf20Sopenharmony_ci * Use IS_ERR_OR_NULL() to identify error values on return.
7568c2ecf20Sopenharmony_ci */
7578c2ecf20Sopenharmony_civoid *knav_pool_create(const char *name,
7588c2ecf20Sopenharmony_ci					int num_desc, int region_id)
7598c2ecf20Sopenharmony_ci{
7608c2ecf20Sopenharmony_ci	struct knav_region *reg_itr, *region = NULL;
7618c2ecf20Sopenharmony_ci	struct knav_pool *pool, *pi;
7628c2ecf20Sopenharmony_ci	struct list_head *node;
7638c2ecf20Sopenharmony_ci	unsigned last_offset;
7648c2ecf20Sopenharmony_ci	bool slot_found;
7658c2ecf20Sopenharmony_ci	int ret;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	if (!kdev)
7688c2ecf20Sopenharmony_ci		return ERR_PTR(-EPROBE_DEFER);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	if (!kdev->dev)
7718c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	pool = devm_kzalloc(kdev->dev, sizeof(*pool), GFP_KERNEL);
7748c2ecf20Sopenharmony_ci	if (!pool) {
7758c2ecf20Sopenharmony_ci		dev_err(kdev->dev, "out of memory allocating pool\n");
7768c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
7778c2ecf20Sopenharmony_ci	}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	for_each_region(kdev, reg_itr) {
7808c2ecf20Sopenharmony_ci		if (reg_itr->id != region_id)
7818c2ecf20Sopenharmony_ci			continue;
7828c2ecf20Sopenharmony_ci		region = reg_itr;
7838c2ecf20Sopenharmony_ci		break;
7848c2ecf20Sopenharmony_ci	}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	if (!region) {
7878c2ecf20Sopenharmony_ci		dev_err(kdev->dev, "region-id(%d) not found\n", region_id);
7888c2ecf20Sopenharmony_ci		ret = -EINVAL;
7898c2ecf20Sopenharmony_ci		goto err;
7908c2ecf20Sopenharmony_ci	}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	pool->queue = knav_queue_open(name, KNAV_QUEUE_GP, 0);
7938c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(pool->queue)) {
7948c2ecf20Sopenharmony_ci		dev_err(kdev->dev,
7958c2ecf20Sopenharmony_ci			"failed to open queue for pool(%s), error %ld\n",
7968c2ecf20Sopenharmony_ci			name, PTR_ERR(pool->queue));
7978c2ecf20Sopenharmony_ci		ret = PTR_ERR(pool->queue);
7988c2ecf20Sopenharmony_ci		goto err;
7998c2ecf20Sopenharmony_ci	}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	pool->name = kstrndup(name, KNAV_NAME_SIZE - 1, GFP_KERNEL);
8028c2ecf20Sopenharmony_ci	pool->kdev = kdev;
8038c2ecf20Sopenharmony_ci	pool->dev = kdev->dev;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	mutex_lock(&knav_dev_lock);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	if (num_desc > (region->num_desc - region->used_desc)) {
8088c2ecf20Sopenharmony_ci		dev_err(kdev->dev, "out of descs in region(%d) for pool(%s)\n",
8098c2ecf20Sopenharmony_ci			region_id, name);
8108c2ecf20Sopenharmony_ci		ret = -ENOMEM;
8118c2ecf20Sopenharmony_ci		goto err_unlock;
8128c2ecf20Sopenharmony_ci	}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	/* Region maintains a sorted (by region offset) list of pools
8158c2ecf20Sopenharmony_ci	 * use the first free slot which is large enough to accomodate
8168c2ecf20Sopenharmony_ci	 * the request
8178c2ecf20Sopenharmony_ci	 */
8188c2ecf20Sopenharmony_ci	last_offset = 0;
8198c2ecf20Sopenharmony_ci	slot_found = false;
8208c2ecf20Sopenharmony_ci	node = &region->pools;
8218c2ecf20Sopenharmony_ci	list_for_each_entry(pi, &region->pools, region_inst) {
8228c2ecf20Sopenharmony_ci		if ((pi->region_offset - last_offset) >= num_desc) {
8238c2ecf20Sopenharmony_ci			slot_found = true;
8248c2ecf20Sopenharmony_ci			break;
8258c2ecf20Sopenharmony_ci		}
8268c2ecf20Sopenharmony_ci		last_offset = pi->region_offset + pi->num_desc;
8278c2ecf20Sopenharmony_ci	}
8288c2ecf20Sopenharmony_ci	node = &pi->region_inst;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (slot_found) {
8318c2ecf20Sopenharmony_ci		pool->region = region;
8328c2ecf20Sopenharmony_ci		pool->num_desc = num_desc;
8338c2ecf20Sopenharmony_ci		pool->region_offset = last_offset;
8348c2ecf20Sopenharmony_ci		region->used_desc += num_desc;
8358c2ecf20Sopenharmony_ci		list_add_tail(&pool->list, &kdev->pools);
8368c2ecf20Sopenharmony_ci		list_add_tail(&pool->region_inst, node);
8378c2ecf20Sopenharmony_ci	} else {
8388c2ecf20Sopenharmony_ci		dev_err(kdev->dev, "pool(%s) create failed: fragmented desc pool in region(%d)\n",
8398c2ecf20Sopenharmony_ci			name, region_id);
8408c2ecf20Sopenharmony_ci		ret = -ENOMEM;
8418c2ecf20Sopenharmony_ci		goto err_unlock;
8428c2ecf20Sopenharmony_ci	}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	mutex_unlock(&knav_dev_lock);
8458c2ecf20Sopenharmony_ci	kdesc_fill_pool(pool);
8468c2ecf20Sopenharmony_ci	return pool;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_cierr_unlock:
8498c2ecf20Sopenharmony_ci	mutex_unlock(&knav_dev_lock);
8508c2ecf20Sopenharmony_cierr:
8518c2ecf20Sopenharmony_ci	kfree(pool->name);
8528c2ecf20Sopenharmony_ci	devm_kfree(kdev->dev, pool);
8538c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_create);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci/**
8588c2ecf20Sopenharmony_ci * knav_pool_destroy()	- Free a pool of descriptors
8598c2ecf20Sopenharmony_ci * @pool		- pool handle
8608c2ecf20Sopenharmony_ci */
8618c2ecf20Sopenharmony_civoid knav_pool_destroy(void *ph)
8628c2ecf20Sopenharmony_ci{
8638c2ecf20Sopenharmony_ci	struct knav_pool *pool = ph;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	if (!pool)
8668c2ecf20Sopenharmony_ci		return;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	if (!pool->region)
8698c2ecf20Sopenharmony_ci		return;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	kdesc_empty_pool(pool);
8728c2ecf20Sopenharmony_ci	mutex_lock(&knav_dev_lock);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	pool->region->used_desc -= pool->num_desc;
8758c2ecf20Sopenharmony_ci	list_del(&pool->region_inst);
8768c2ecf20Sopenharmony_ci	list_del(&pool->list);
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	mutex_unlock(&knav_dev_lock);
8798c2ecf20Sopenharmony_ci	kfree(pool->name);
8808c2ecf20Sopenharmony_ci	devm_kfree(kdev->dev, pool);
8818c2ecf20Sopenharmony_ci}
8828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_destroy);
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci/**
8868c2ecf20Sopenharmony_ci * knav_pool_desc_get()	- Get a descriptor from the pool
8878c2ecf20Sopenharmony_ci * @pool			- pool handle
8888c2ecf20Sopenharmony_ci *
8898c2ecf20Sopenharmony_ci * Returns descriptor from the pool.
8908c2ecf20Sopenharmony_ci */
8918c2ecf20Sopenharmony_civoid *knav_pool_desc_get(void *ph)
8928c2ecf20Sopenharmony_ci{
8938c2ecf20Sopenharmony_ci	struct knav_pool *pool = ph;
8948c2ecf20Sopenharmony_ci	dma_addr_t dma;
8958c2ecf20Sopenharmony_ci	unsigned size;
8968c2ecf20Sopenharmony_ci	void *data;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	dma = knav_queue_pop(pool->queue, &size);
8998c2ecf20Sopenharmony_ci	if (unlikely(!dma))
9008c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
9018c2ecf20Sopenharmony_ci	data = knav_pool_desc_dma_to_virt(pool, dma);
9028c2ecf20Sopenharmony_ci	return data;
9038c2ecf20Sopenharmony_ci}
9048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_desc_get);
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci/**
9078c2ecf20Sopenharmony_ci * knav_pool_desc_put()	- return a descriptor to the pool
9088c2ecf20Sopenharmony_ci * @pool			- pool handle
9098c2ecf20Sopenharmony_ci */
9108c2ecf20Sopenharmony_civoid knav_pool_desc_put(void *ph, void *desc)
9118c2ecf20Sopenharmony_ci{
9128c2ecf20Sopenharmony_ci	struct knav_pool *pool = ph;
9138c2ecf20Sopenharmony_ci	dma_addr_t dma;
9148c2ecf20Sopenharmony_ci	dma = knav_pool_desc_virt_to_dma(pool, desc);
9158c2ecf20Sopenharmony_ci	knav_queue_push(pool->queue, dma, pool->region->desc_size, 0);
9168c2ecf20Sopenharmony_ci}
9178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_desc_put);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci/**
9208c2ecf20Sopenharmony_ci * knav_pool_desc_map()	- Map descriptor for DMA transfer
9218c2ecf20Sopenharmony_ci * @pool			- pool handle
9228c2ecf20Sopenharmony_ci * @desc			- address of descriptor to map
9238c2ecf20Sopenharmony_ci * @size			- size of descriptor to map
9248c2ecf20Sopenharmony_ci * @dma				- DMA address return pointer
9258c2ecf20Sopenharmony_ci * @dma_sz			- adjusted return pointer
9268c2ecf20Sopenharmony_ci *
9278c2ecf20Sopenharmony_ci * Returns 0 on success, errno otherwise.
9288c2ecf20Sopenharmony_ci */
9298c2ecf20Sopenharmony_ciint knav_pool_desc_map(void *ph, void *desc, unsigned size,
9308c2ecf20Sopenharmony_ci					dma_addr_t *dma, unsigned *dma_sz)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	struct knav_pool *pool = ph;
9338c2ecf20Sopenharmony_ci	*dma = knav_pool_desc_virt_to_dma(pool, desc);
9348c2ecf20Sopenharmony_ci	size = min(size, pool->region->desc_size);
9358c2ecf20Sopenharmony_ci	size = ALIGN(size, SMP_CACHE_BYTES);
9368c2ecf20Sopenharmony_ci	*dma_sz = size;
9378c2ecf20Sopenharmony_ci	dma_sync_single_for_device(pool->dev, *dma, size, DMA_TO_DEVICE);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	/* Ensure the descriptor reaches to the memory */
9408c2ecf20Sopenharmony_ci	__iowmb();
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	return 0;
9438c2ecf20Sopenharmony_ci}
9448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_desc_map);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci/**
9478c2ecf20Sopenharmony_ci * knav_pool_desc_unmap()	- Unmap descriptor after DMA transfer
9488c2ecf20Sopenharmony_ci * @pool			- pool handle
9498c2ecf20Sopenharmony_ci * @dma				- DMA address of descriptor to unmap
9508c2ecf20Sopenharmony_ci * @dma_sz			- size of descriptor to unmap
9518c2ecf20Sopenharmony_ci *
9528c2ecf20Sopenharmony_ci * Returns descriptor address on success, Use IS_ERR_OR_NULL() to identify
9538c2ecf20Sopenharmony_ci * error values on return.
9548c2ecf20Sopenharmony_ci */
9558c2ecf20Sopenharmony_civoid *knav_pool_desc_unmap(void *ph, dma_addr_t dma, unsigned dma_sz)
9568c2ecf20Sopenharmony_ci{
9578c2ecf20Sopenharmony_ci	struct knav_pool *pool = ph;
9588c2ecf20Sopenharmony_ci	unsigned desc_sz;
9598c2ecf20Sopenharmony_ci	void *desc;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	desc_sz = min(dma_sz, pool->region->desc_size);
9628c2ecf20Sopenharmony_ci	desc = knav_pool_desc_dma_to_virt(pool, dma);
9638c2ecf20Sopenharmony_ci	dma_sync_single_for_cpu(pool->dev, dma, desc_sz, DMA_FROM_DEVICE);
9648c2ecf20Sopenharmony_ci	prefetch(desc);
9658c2ecf20Sopenharmony_ci	return desc;
9668c2ecf20Sopenharmony_ci}
9678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_desc_unmap);
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci/**
9708c2ecf20Sopenharmony_ci * knav_pool_count()	- Get the number of descriptors in pool.
9718c2ecf20Sopenharmony_ci * @pool		- pool handle
9728c2ecf20Sopenharmony_ci * Returns number of elements in the pool.
9738c2ecf20Sopenharmony_ci */
9748c2ecf20Sopenharmony_ciint knav_pool_count(void *ph)
9758c2ecf20Sopenharmony_ci{
9768c2ecf20Sopenharmony_ci	struct knav_pool *pool = ph;
9778c2ecf20Sopenharmony_ci	return knav_queue_get_count(pool->queue);
9788c2ecf20Sopenharmony_ci}
9798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_pool_count);
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_cistatic void knav_queue_setup_region(struct knav_device *kdev,
9828c2ecf20Sopenharmony_ci					struct knav_region *region)
9838c2ecf20Sopenharmony_ci{
9848c2ecf20Sopenharmony_ci	unsigned hw_num_desc, hw_desc_size, size;
9858c2ecf20Sopenharmony_ci	struct knav_reg_region __iomem  *regs;
9868c2ecf20Sopenharmony_ci	struct knav_qmgr_info *qmgr;
9878c2ecf20Sopenharmony_ci	struct knav_pool *pool;
9888c2ecf20Sopenharmony_ci	int id = region->id;
9898c2ecf20Sopenharmony_ci	struct page *page;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	/* unused region? */
9928c2ecf20Sopenharmony_ci	if (!region->num_desc) {
9938c2ecf20Sopenharmony_ci		dev_warn(kdev->dev, "unused region %s\n", region->name);
9948c2ecf20Sopenharmony_ci		return;
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	/* get hardware descriptor value */
9988c2ecf20Sopenharmony_ci	hw_num_desc = ilog2(region->num_desc - 1) + 1;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	/* did we force fit ourselves into nothingness? */
10018c2ecf20Sopenharmony_ci	if (region->num_desc < 32) {
10028c2ecf20Sopenharmony_ci		region->num_desc = 0;
10038c2ecf20Sopenharmony_ci		dev_warn(kdev->dev, "too few descriptors in region %s\n",
10048c2ecf20Sopenharmony_ci			 region->name);
10058c2ecf20Sopenharmony_ci		return;
10068c2ecf20Sopenharmony_ci	}
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	size = region->num_desc * region->desc_size;
10098c2ecf20Sopenharmony_ci	region->virt_start = alloc_pages_exact(size, GFP_KERNEL | GFP_DMA |
10108c2ecf20Sopenharmony_ci						GFP_DMA32);
10118c2ecf20Sopenharmony_ci	if (!region->virt_start) {
10128c2ecf20Sopenharmony_ci		region->num_desc = 0;
10138c2ecf20Sopenharmony_ci		dev_err(kdev->dev, "memory alloc failed for region %s\n",
10148c2ecf20Sopenharmony_ci			region->name);
10158c2ecf20Sopenharmony_ci		return;
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci	region->virt_end = region->virt_start + size;
10188c2ecf20Sopenharmony_ci	page = virt_to_page(region->virt_start);
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	region->dma_start = dma_map_page(kdev->dev, page, 0, size,
10218c2ecf20Sopenharmony_ci					 DMA_BIDIRECTIONAL);
10228c2ecf20Sopenharmony_ci	if (dma_mapping_error(kdev->dev, region->dma_start)) {
10238c2ecf20Sopenharmony_ci		dev_err(kdev->dev, "dma map failed for region %s\n",
10248c2ecf20Sopenharmony_ci			region->name);
10258c2ecf20Sopenharmony_ci		goto fail;
10268c2ecf20Sopenharmony_ci	}
10278c2ecf20Sopenharmony_ci	region->dma_end = region->dma_start + size;
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	pool = devm_kzalloc(kdev->dev, sizeof(*pool), GFP_KERNEL);
10308c2ecf20Sopenharmony_ci	if (!pool) {
10318c2ecf20Sopenharmony_ci		dev_err(kdev->dev, "out of memory allocating dummy pool\n");
10328c2ecf20Sopenharmony_ci		goto fail;
10338c2ecf20Sopenharmony_ci	}
10348c2ecf20Sopenharmony_ci	pool->num_desc = 0;
10358c2ecf20Sopenharmony_ci	pool->region_offset = region->num_desc;
10368c2ecf20Sopenharmony_ci	list_add(&pool->region_inst, &region->pools);
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	dev_dbg(kdev->dev,
10398c2ecf20Sopenharmony_ci		"region %s (%d): size:%d, link:%d@%d, dma:%pad-%pad, virt:%p-%p\n",
10408c2ecf20Sopenharmony_ci		region->name, id, region->desc_size, region->num_desc,
10418c2ecf20Sopenharmony_ci		region->link_index, &region->dma_start, &region->dma_end,
10428c2ecf20Sopenharmony_ci		region->virt_start, region->virt_end);
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	hw_desc_size = (region->desc_size / 16) - 1;
10458c2ecf20Sopenharmony_ci	hw_num_desc -= 5;
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	for_each_qmgr(kdev, qmgr) {
10488c2ecf20Sopenharmony_ci		regs = qmgr->reg_region + id;
10498c2ecf20Sopenharmony_ci		writel_relaxed((u32)region->dma_start, &regs->base);
10508c2ecf20Sopenharmony_ci		writel_relaxed(region->link_index, &regs->start_index);
10518c2ecf20Sopenharmony_ci		writel_relaxed(hw_desc_size << 16 | hw_num_desc,
10528c2ecf20Sopenharmony_ci			       &regs->size_count);
10538c2ecf20Sopenharmony_ci	}
10548c2ecf20Sopenharmony_ci	return;
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_cifail:
10578c2ecf20Sopenharmony_ci	if (region->dma_start)
10588c2ecf20Sopenharmony_ci		dma_unmap_page(kdev->dev, region->dma_start, size,
10598c2ecf20Sopenharmony_ci				DMA_BIDIRECTIONAL);
10608c2ecf20Sopenharmony_ci	if (region->virt_start)
10618c2ecf20Sopenharmony_ci		free_pages_exact(region->virt_start, size);
10628c2ecf20Sopenharmony_ci	region->num_desc = 0;
10638c2ecf20Sopenharmony_ci	return;
10648c2ecf20Sopenharmony_ci}
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_cistatic const char *knav_queue_find_name(struct device_node *node)
10678c2ecf20Sopenharmony_ci{
10688c2ecf20Sopenharmony_ci	const char *name;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	if (of_property_read_string(node, "label", &name) < 0)
10718c2ecf20Sopenharmony_ci		name = node->name;
10728c2ecf20Sopenharmony_ci	if (!name)
10738c2ecf20Sopenharmony_ci		name = "unknown";
10748c2ecf20Sopenharmony_ci	return name;
10758c2ecf20Sopenharmony_ci}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_cistatic int knav_queue_setup_regions(struct knav_device *kdev,
10788c2ecf20Sopenharmony_ci					struct device_node *regions)
10798c2ecf20Sopenharmony_ci{
10808c2ecf20Sopenharmony_ci	struct device *dev = kdev->dev;
10818c2ecf20Sopenharmony_ci	struct knav_region *region;
10828c2ecf20Sopenharmony_ci	struct device_node *child;
10838c2ecf20Sopenharmony_ci	u32 temp[2];
10848c2ecf20Sopenharmony_ci	int ret;
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	for_each_child_of_node(regions, child) {
10878c2ecf20Sopenharmony_ci		region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL);
10888c2ecf20Sopenharmony_ci		if (!region) {
10898c2ecf20Sopenharmony_ci			dev_err(dev, "out of memory allocating region\n");
10908c2ecf20Sopenharmony_ci			return -ENOMEM;
10918c2ecf20Sopenharmony_ci		}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci		region->name = knav_queue_find_name(child);
10948c2ecf20Sopenharmony_ci		of_property_read_u32(child, "id", &region->id);
10958c2ecf20Sopenharmony_ci		ret = of_property_read_u32_array(child, "region-spec", temp, 2);
10968c2ecf20Sopenharmony_ci		if (!ret) {
10978c2ecf20Sopenharmony_ci			region->num_desc  = temp[0];
10988c2ecf20Sopenharmony_ci			region->desc_size = temp[1];
10998c2ecf20Sopenharmony_ci		} else {
11008c2ecf20Sopenharmony_ci			dev_err(dev, "invalid region info %s\n", region->name);
11018c2ecf20Sopenharmony_ci			devm_kfree(dev, region);
11028c2ecf20Sopenharmony_ci			continue;
11038c2ecf20Sopenharmony_ci		}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci		if (!of_get_property(child, "link-index", NULL)) {
11068c2ecf20Sopenharmony_ci			dev_err(dev, "No link info for %s\n", region->name);
11078c2ecf20Sopenharmony_ci			devm_kfree(dev, region);
11088c2ecf20Sopenharmony_ci			continue;
11098c2ecf20Sopenharmony_ci		}
11108c2ecf20Sopenharmony_ci		ret = of_property_read_u32(child, "link-index",
11118c2ecf20Sopenharmony_ci					   &region->link_index);
11128c2ecf20Sopenharmony_ci		if (ret) {
11138c2ecf20Sopenharmony_ci			dev_err(dev, "link index not found for %s\n",
11148c2ecf20Sopenharmony_ci				region->name);
11158c2ecf20Sopenharmony_ci			devm_kfree(dev, region);
11168c2ecf20Sopenharmony_ci			continue;
11178c2ecf20Sopenharmony_ci		}
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&region->pools);
11208c2ecf20Sopenharmony_ci		list_add_tail(&region->list, &kdev->regions);
11218c2ecf20Sopenharmony_ci	}
11228c2ecf20Sopenharmony_ci	if (list_empty(&kdev->regions)) {
11238c2ecf20Sopenharmony_ci		dev_err(dev, "no valid region information found\n");
11248c2ecf20Sopenharmony_ci		return -ENODEV;
11258c2ecf20Sopenharmony_ci	}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	/* Next, we run through the regions and set things up */
11288c2ecf20Sopenharmony_ci	for_each_region(kdev, region)
11298c2ecf20Sopenharmony_ci		knav_queue_setup_region(kdev, region);
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	return 0;
11328c2ecf20Sopenharmony_ci}
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_cistatic int knav_get_link_ram(struct knav_device *kdev,
11358c2ecf20Sopenharmony_ci				       const char *name,
11368c2ecf20Sopenharmony_ci				       struct knav_link_ram_block *block)
11378c2ecf20Sopenharmony_ci{
11388c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(kdev->dev);
11398c2ecf20Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
11408c2ecf20Sopenharmony_ci	u32 temp[2];
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	/*
11438c2ecf20Sopenharmony_ci	 * Note: link ram resources are specified in "entry" sized units. In
11448c2ecf20Sopenharmony_ci	 * reality, although entries are ~40bits in hardware, we treat them as
11458c2ecf20Sopenharmony_ci	 * 64-bit entities here.
11468c2ecf20Sopenharmony_ci	 *
11478c2ecf20Sopenharmony_ci	 * For example, to specify the internal link ram for Keystone-I class
11488c2ecf20Sopenharmony_ci	 * devices, we would set the linkram0 resource to 0x80000-0x83fff.
11498c2ecf20Sopenharmony_ci	 *
11508c2ecf20Sopenharmony_ci	 * This gets a bit weird when other link rams are used.  For example,
11518c2ecf20Sopenharmony_ci	 * if the range specified is 0x0c000000-0x0c003fff (i.e., 16K entries
11528c2ecf20Sopenharmony_ci	 * in MSMC SRAM), the actual memory used is 0x0c000000-0x0c020000,
11538c2ecf20Sopenharmony_ci	 * which accounts for 64-bits per entry, for 16K entries.
11548c2ecf20Sopenharmony_ci	 */
11558c2ecf20Sopenharmony_ci	if (!of_property_read_u32_array(node, name , temp, 2)) {
11568c2ecf20Sopenharmony_ci		if (temp[0]) {
11578c2ecf20Sopenharmony_ci			/*
11588c2ecf20Sopenharmony_ci			 * queue_base specified => using internal or onchip
11598c2ecf20Sopenharmony_ci			 * link ram WARNING - we do not "reserve" this block
11608c2ecf20Sopenharmony_ci			 */
11618c2ecf20Sopenharmony_ci			block->dma = (dma_addr_t)temp[0];
11628c2ecf20Sopenharmony_ci			block->virt = NULL;
11638c2ecf20Sopenharmony_ci			block->size = temp[1];
11648c2ecf20Sopenharmony_ci		} else {
11658c2ecf20Sopenharmony_ci			block->size = temp[1];
11668c2ecf20Sopenharmony_ci			/* queue_base not specific => allocate requested size */
11678c2ecf20Sopenharmony_ci			block->virt = dmam_alloc_coherent(kdev->dev,
11688c2ecf20Sopenharmony_ci						  8 * block->size, &block->dma,
11698c2ecf20Sopenharmony_ci						  GFP_KERNEL);
11708c2ecf20Sopenharmony_ci			if (!block->virt) {
11718c2ecf20Sopenharmony_ci				dev_err(kdev->dev, "failed to alloc linkram\n");
11728c2ecf20Sopenharmony_ci				return -ENOMEM;
11738c2ecf20Sopenharmony_ci			}
11748c2ecf20Sopenharmony_ci		}
11758c2ecf20Sopenharmony_ci	} else {
11768c2ecf20Sopenharmony_ci		return -ENODEV;
11778c2ecf20Sopenharmony_ci	}
11788c2ecf20Sopenharmony_ci	return 0;
11798c2ecf20Sopenharmony_ci}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_cistatic int knav_queue_setup_link_ram(struct knav_device *kdev)
11828c2ecf20Sopenharmony_ci{
11838c2ecf20Sopenharmony_ci	struct knav_link_ram_block *block;
11848c2ecf20Sopenharmony_ci	struct knav_qmgr_info *qmgr;
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	for_each_qmgr(kdev, qmgr) {
11878c2ecf20Sopenharmony_ci		block = &kdev->link_rams[0];
11888c2ecf20Sopenharmony_ci		dev_dbg(kdev->dev, "linkram0: dma:%pad, virt:%p, size:%x\n",
11898c2ecf20Sopenharmony_ci			&block->dma, block->virt, block->size);
11908c2ecf20Sopenharmony_ci		writel_relaxed((u32)block->dma, &qmgr->reg_config->link_ram_base0);
11918c2ecf20Sopenharmony_ci		if (kdev->version == QMSS_66AK2G)
11928c2ecf20Sopenharmony_ci			writel_relaxed(block->size,
11938c2ecf20Sopenharmony_ci				       &qmgr->reg_config->link_ram_size0);
11948c2ecf20Sopenharmony_ci		else
11958c2ecf20Sopenharmony_ci			writel_relaxed(block->size - 1,
11968c2ecf20Sopenharmony_ci				       &qmgr->reg_config->link_ram_size0);
11978c2ecf20Sopenharmony_ci		block++;
11988c2ecf20Sopenharmony_ci		if (!block->size)
11998c2ecf20Sopenharmony_ci			continue;
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci		dev_dbg(kdev->dev, "linkram1: dma:%pad, virt:%p, size:%x\n",
12028c2ecf20Sopenharmony_ci			&block->dma, block->virt, block->size);
12038c2ecf20Sopenharmony_ci		writel_relaxed(block->dma, &qmgr->reg_config->link_ram_base1);
12048c2ecf20Sopenharmony_ci	}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	return 0;
12078c2ecf20Sopenharmony_ci}
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_cistatic int knav_setup_queue_range(struct knav_device *kdev,
12108c2ecf20Sopenharmony_ci					struct device_node *node)
12118c2ecf20Sopenharmony_ci{
12128c2ecf20Sopenharmony_ci	struct device *dev = kdev->dev;
12138c2ecf20Sopenharmony_ci	struct knav_range_info *range;
12148c2ecf20Sopenharmony_ci	struct knav_qmgr_info *qmgr;
12158c2ecf20Sopenharmony_ci	u32 temp[2], start, end, id, index;
12168c2ecf20Sopenharmony_ci	int ret, i;
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL);
12198c2ecf20Sopenharmony_ci	if (!range) {
12208c2ecf20Sopenharmony_ci		dev_err(dev, "out of memory allocating range\n");
12218c2ecf20Sopenharmony_ci		return -ENOMEM;
12228c2ecf20Sopenharmony_ci	}
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	range->kdev = kdev;
12258c2ecf20Sopenharmony_ci	range->name = knav_queue_find_name(node);
12268c2ecf20Sopenharmony_ci	ret = of_property_read_u32_array(node, "qrange", temp, 2);
12278c2ecf20Sopenharmony_ci	if (!ret) {
12288c2ecf20Sopenharmony_ci		range->queue_base = temp[0] - kdev->base_id;
12298c2ecf20Sopenharmony_ci		range->num_queues = temp[1];
12308c2ecf20Sopenharmony_ci	} else {
12318c2ecf20Sopenharmony_ci		dev_err(dev, "invalid queue range %s\n", range->name);
12328c2ecf20Sopenharmony_ci		devm_kfree(dev, range);
12338c2ecf20Sopenharmony_ci		return -EINVAL;
12348c2ecf20Sopenharmony_ci	}
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	for (i = 0; i < RANGE_MAX_IRQS; i++) {
12378c2ecf20Sopenharmony_ci		struct of_phandle_args oirq;
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci		if (of_irq_parse_one(node, i, &oirq))
12408c2ecf20Sopenharmony_ci			break;
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci		range->irqs[i].irq = irq_create_of_mapping(&oirq);
12438c2ecf20Sopenharmony_ci		if (range->irqs[i].irq == IRQ_NONE)
12448c2ecf20Sopenharmony_ci			break;
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci		range->num_irqs++;
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci		if (IS_ENABLED(CONFIG_SMP) && oirq.args_count == 3) {
12498c2ecf20Sopenharmony_ci			unsigned long mask;
12508c2ecf20Sopenharmony_ci			int bit;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci			range->irqs[i].cpu_mask = devm_kzalloc(dev,
12538c2ecf20Sopenharmony_ci							       cpumask_size(), GFP_KERNEL);
12548c2ecf20Sopenharmony_ci			if (!range->irqs[i].cpu_mask)
12558c2ecf20Sopenharmony_ci				return -ENOMEM;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci			mask = (oirq.args[2] & 0x0000ff00) >> 8;
12588c2ecf20Sopenharmony_ci			for_each_set_bit(bit, &mask, BITS_PER_LONG)
12598c2ecf20Sopenharmony_ci				cpumask_set_cpu(bit, range->irqs[i].cpu_mask);
12608c2ecf20Sopenharmony_ci		}
12618c2ecf20Sopenharmony_ci	}
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	range->num_irqs = min(range->num_irqs, range->num_queues);
12648c2ecf20Sopenharmony_ci	if (range->num_irqs)
12658c2ecf20Sopenharmony_ci		range->flags |= RANGE_HAS_IRQ;
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	if (of_get_property(node, "qalloc-by-id", NULL))
12688c2ecf20Sopenharmony_ci		range->flags |= RANGE_RESERVED;
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	if (of_get_property(node, "accumulator", NULL)) {
12718c2ecf20Sopenharmony_ci		ret = knav_init_acc_range(kdev, node, range);
12728c2ecf20Sopenharmony_ci		if (ret < 0) {
12738c2ecf20Sopenharmony_ci			devm_kfree(dev, range);
12748c2ecf20Sopenharmony_ci			return ret;
12758c2ecf20Sopenharmony_ci		}
12768c2ecf20Sopenharmony_ci	} else {
12778c2ecf20Sopenharmony_ci		range->ops = &knav_gp_range_ops;
12788c2ecf20Sopenharmony_ci	}
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	/* set threshold to 1, and flush out the queues */
12818c2ecf20Sopenharmony_ci	for_each_qmgr(kdev, qmgr) {
12828c2ecf20Sopenharmony_ci		start = max(qmgr->start_queue, range->queue_base);
12838c2ecf20Sopenharmony_ci		end   = min(qmgr->start_queue + qmgr->num_queues,
12848c2ecf20Sopenharmony_ci			    range->queue_base + range->num_queues);
12858c2ecf20Sopenharmony_ci		for (id = start; id < end; id++) {
12868c2ecf20Sopenharmony_ci			index = id - qmgr->start_queue;
12878c2ecf20Sopenharmony_ci			writel_relaxed(THRESH_GTE | 1,
12888c2ecf20Sopenharmony_ci				       &qmgr->reg_peek[index].ptr_size_thresh);
12898c2ecf20Sopenharmony_ci			writel_relaxed(0,
12908c2ecf20Sopenharmony_ci				       &qmgr->reg_push[index].ptr_size_thresh);
12918c2ecf20Sopenharmony_ci		}
12928c2ecf20Sopenharmony_ci	}
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	list_add_tail(&range->list, &kdev->queue_ranges);
12958c2ecf20Sopenharmony_ci	dev_dbg(dev, "added range %s: %d-%d, %d irqs%s%s%s\n",
12968c2ecf20Sopenharmony_ci		range->name, range->queue_base,
12978c2ecf20Sopenharmony_ci		range->queue_base + range->num_queues - 1,
12988c2ecf20Sopenharmony_ci		range->num_irqs,
12998c2ecf20Sopenharmony_ci		(range->flags & RANGE_HAS_IRQ) ? ", has irq" : "",
13008c2ecf20Sopenharmony_ci		(range->flags & RANGE_RESERVED) ? ", reserved" : "",
13018c2ecf20Sopenharmony_ci		(range->flags & RANGE_HAS_ACCUMULATOR) ? ", acc" : "");
13028c2ecf20Sopenharmony_ci	kdev->num_queues_in_use += range->num_queues;
13038c2ecf20Sopenharmony_ci	return 0;
13048c2ecf20Sopenharmony_ci}
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_cistatic int knav_setup_queue_pools(struct knav_device *kdev,
13078c2ecf20Sopenharmony_ci				   struct device_node *queue_pools)
13088c2ecf20Sopenharmony_ci{
13098c2ecf20Sopenharmony_ci	struct device_node *type, *range;
13108c2ecf20Sopenharmony_ci	int ret;
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	for_each_child_of_node(queue_pools, type) {
13138c2ecf20Sopenharmony_ci		for_each_child_of_node(type, range) {
13148c2ecf20Sopenharmony_ci			ret = knav_setup_queue_range(kdev, range);
13158c2ecf20Sopenharmony_ci			/* return value ignored, we init the rest... */
13168c2ecf20Sopenharmony_ci		}
13178c2ecf20Sopenharmony_ci	}
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	/* ... and barf if they all failed! */
13208c2ecf20Sopenharmony_ci	if (list_empty(&kdev->queue_ranges)) {
13218c2ecf20Sopenharmony_ci		dev_err(kdev->dev, "no valid queue range found\n");
13228c2ecf20Sopenharmony_ci		return -ENODEV;
13238c2ecf20Sopenharmony_ci	}
13248c2ecf20Sopenharmony_ci	return 0;
13258c2ecf20Sopenharmony_ci}
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_cistatic void knav_free_queue_range(struct knav_device *kdev,
13288c2ecf20Sopenharmony_ci				  struct knav_range_info *range)
13298c2ecf20Sopenharmony_ci{
13308c2ecf20Sopenharmony_ci	if (range->ops && range->ops->free_range)
13318c2ecf20Sopenharmony_ci		range->ops->free_range(range);
13328c2ecf20Sopenharmony_ci	list_del(&range->list);
13338c2ecf20Sopenharmony_ci	devm_kfree(kdev->dev, range);
13348c2ecf20Sopenharmony_ci}
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_cistatic void knav_free_queue_ranges(struct knav_device *kdev)
13378c2ecf20Sopenharmony_ci{
13388c2ecf20Sopenharmony_ci	struct knav_range_info *range;
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	for (;;) {
13418c2ecf20Sopenharmony_ci		range = first_queue_range(kdev);
13428c2ecf20Sopenharmony_ci		if (!range)
13438c2ecf20Sopenharmony_ci			break;
13448c2ecf20Sopenharmony_ci		knav_free_queue_range(kdev, range);
13458c2ecf20Sopenharmony_ci	}
13468c2ecf20Sopenharmony_ci}
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_cistatic void knav_queue_free_regions(struct knav_device *kdev)
13498c2ecf20Sopenharmony_ci{
13508c2ecf20Sopenharmony_ci	struct knav_region *region;
13518c2ecf20Sopenharmony_ci	struct knav_pool *pool, *tmp;
13528c2ecf20Sopenharmony_ci	unsigned size;
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	for (;;) {
13558c2ecf20Sopenharmony_ci		region = first_region(kdev);
13568c2ecf20Sopenharmony_ci		if (!region)
13578c2ecf20Sopenharmony_ci			break;
13588c2ecf20Sopenharmony_ci		list_for_each_entry_safe(pool, tmp, &region->pools, region_inst)
13598c2ecf20Sopenharmony_ci			knav_pool_destroy(pool);
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci		size = region->virt_end - region->virt_start;
13628c2ecf20Sopenharmony_ci		if (size)
13638c2ecf20Sopenharmony_ci			free_pages_exact(region->virt_start, size);
13648c2ecf20Sopenharmony_ci		list_del(&region->list);
13658c2ecf20Sopenharmony_ci		devm_kfree(kdev->dev, region);
13668c2ecf20Sopenharmony_ci	}
13678c2ecf20Sopenharmony_ci}
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_cistatic void __iomem *knav_queue_map_reg(struct knav_device *kdev,
13708c2ecf20Sopenharmony_ci					struct device_node *node, int index)
13718c2ecf20Sopenharmony_ci{
13728c2ecf20Sopenharmony_ci	struct resource res;
13738c2ecf20Sopenharmony_ci	void __iomem *regs;
13748c2ecf20Sopenharmony_ci	int ret;
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	ret = of_address_to_resource(node, index, &res);
13778c2ecf20Sopenharmony_ci	if (ret) {
13788c2ecf20Sopenharmony_ci		dev_err(kdev->dev, "Can't translate of node(%pOFn) address for index(%d)\n",
13798c2ecf20Sopenharmony_ci			node, index);
13808c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
13818c2ecf20Sopenharmony_ci	}
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	regs = devm_ioremap_resource(kdev->dev, &res);
13848c2ecf20Sopenharmony_ci	if (IS_ERR(regs))
13858c2ecf20Sopenharmony_ci		dev_err(kdev->dev, "Failed to map register base for index(%d) node(%pOFn)\n",
13868c2ecf20Sopenharmony_ci			index, node);
13878c2ecf20Sopenharmony_ci	return regs;
13888c2ecf20Sopenharmony_ci}
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_cistatic int knav_queue_init_qmgrs(struct knav_device *kdev,
13918c2ecf20Sopenharmony_ci					struct device_node *qmgrs)
13928c2ecf20Sopenharmony_ci{
13938c2ecf20Sopenharmony_ci	struct device *dev = kdev->dev;
13948c2ecf20Sopenharmony_ci	struct knav_qmgr_info *qmgr;
13958c2ecf20Sopenharmony_ci	struct device_node *child;
13968c2ecf20Sopenharmony_ci	u32 temp[2];
13978c2ecf20Sopenharmony_ci	int ret;
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	for_each_child_of_node(qmgrs, child) {
14008c2ecf20Sopenharmony_ci		qmgr = devm_kzalloc(dev, sizeof(*qmgr), GFP_KERNEL);
14018c2ecf20Sopenharmony_ci		if (!qmgr) {
14028c2ecf20Sopenharmony_ci			dev_err(dev, "out of memory allocating qmgr\n");
14038c2ecf20Sopenharmony_ci			return -ENOMEM;
14048c2ecf20Sopenharmony_ci		}
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci		ret = of_property_read_u32_array(child, "managed-queues",
14078c2ecf20Sopenharmony_ci						 temp, 2);
14088c2ecf20Sopenharmony_ci		if (!ret) {
14098c2ecf20Sopenharmony_ci			qmgr->start_queue = temp[0];
14108c2ecf20Sopenharmony_ci			qmgr->num_queues = temp[1];
14118c2ecf20Sopenharmony_ci		} else {
14128c2ecf20Sopenharmony_ci			dev_err(dev, "invalid qmgr queue range\n");
14138c2ecf20Sopenharmony_ci			devm_kfree(dev, qmgr);
14148c2ecf20Sopenharmony_ci			continue;
14158c2ecf20Sopenharmony_ci		}
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci		dev_info(dev, "qmgr start queue %d, number of queues %d\n",
14188c2ecf20Sopenharmony_ci			 qmgr->start_queue, qmgr->num_queues);
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci		qmgr->reg_peek =
14218c2ecf20Sopenharmony_ci			knav_queue_map_reg(kdev, child,
14228c2ecf20Sopenharmony_ci					   KNAV_QUEUE_PEEK_REG_INDEX);
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci		if (kdev->version == QMSS) {
14258c2ecf20Sopenharmony_ci			qmgr->reg_status =
14268c2ecf20Sopenharmony_ci				knav_queue_map_reg(kdev, child,
14278c2ecf20Sopenharmony_ci						   KNAV_QUEUE_STATUS_REG_INDEX);
14288c2ecf20Sopenharmony_ci		}
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci		qmgr->reg_config =
14318c2ecf20Sopenharmony_ci			knav_queue_map_reg(kdev, child,
14328c2ecf20Sopenharmony_ci					   (kdev->version == QMSS_66AK2G) ?
14338c2ecf20Sopenharmony_ci					   KNAV_L_QUEUE_CONFIG_REG_INDEX :
14348c2ecf20Sopenharmony_ci					   KNAV_QUEUE_CONFIG_REG_INDEX);
14358c2ecf20Sopenharmony_ci		qmgr->reg_region =
14368c2ecf20Sopenharmony_ci			knav_queue_map_reg(kdev, child,
14378c2ecf20Sopenharmony_ci					   (kdev->version == QMSS_66AK2G) ?
14388c2ecf20Sopenharmony_ci					   KNAV_L_QUEUE_REGION_REG_INDEX :
14398c2ecf20Sopenharmony_ci					   KNAV_QUEUE_REGION_REG_INDEX);
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci		qmgr->reg_push =
14428c2ecf20Sopenharmony_ci			knav_queue_map_reg(kdev, child,
14438c2ecf20Sopenharmony_ci					   (kdev->version == QMSS_66AK2G) ?
14448c2ecf20Sopenharmony_ci					    KNAV_L_QUEUE_PUSH_REG_INDEX :
14458c2ecf20Sopenharmony_ci					    KNAV_QUEUE_PUSH_REG_INDEX);
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci		if (kdev->version == QMSS) {
14488c2ecf20Sopenharmony_ci			qmgr->reg_pop =
14498c2ecf20Sopenharmony_ci				knav_queue_map_reg(kdev, child,
14508c2ecf20Sopenharmony_ci						   KNAV_QUEUE_POP_REG_INDEX);
14518c2ecf20Sopenharmony_ci		}
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci		if (IS_ERR(qmgr->reg_peek) ||
14548c2ecf20Sopenharmony_ci		    ((kdev->version == QMSS) &&
14558c2ecf20Sopenharmony_ci		    (IS_ERR(qmgr->reg_status) || IS_ERR(qmgr->reg_pop))) ||
14568c2ecf20Sopenharmony_ci		    IS_ERR(qmgr->reg_config) || IS_ERR(qmgr->reg_region) ||
14578c2ecf20Sopenharmony_ci		    IS_ERR(qmgr->reg_push)) {
14588c2ecf20Sopenharmony_ci			dev_err(dev, "failed to map qmgr regs\n");
14598c2ecf20Sopenharmony_ci			if (kdev->version == QMSS) {
14608c2ecf20Sopenharmony_ci				if (!IS_ERR(qmgr->reg_status))
14618c2ecf20Sopenharmony_ci					devm_iounmap(dev, qmgr->reg_status);
14628c2ecf20Sopenharmony_ci				if (!IS_ERR(qmgr->reg_pop))
14638c2ecf20Sopenharmony_ci					devm_iounmap(dev, qmgr->reg_pop);
14648c2ecf20Sopenharmony_ci			}
14658c2ecf20Sopenharmony_ci			if (!IS_ERR(qmgr->reg_peek))
14668c2ecf20Sopenharmony_ci				devm_iounmap(dev, qmgr->reg_peek);
14678c2ecf20Sopenharmony_ci			if (!IS_ERR(qmgr->reg_config))
14688c2ecf20Sopenharmony_ci				devm_iounmap(dev, qmgr->reg_config);
14698c2ecf20Sopenharmony_ci			if (!IS_ERR(qmgr->reg_region))
14708c2ecf20Sopenharmony_ci				devm_iounmap(dev, qmgr->reg_region);
14718c2ecf20Sopenharmony_ci			if (!IS_ERR(qmgr->reg_push))
14728c2ecf20Sopenharmony_ci				devm_iounmap(dev, qmgr->reg_push);
14738c2ecf20Sopenharmony_ci			devm_kfree(dev, qmgr);
14748c2ecf20Sopenharmony_ci			continue;
14758c2ecf20Sopenharmony_ci		}
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci		/* Use same push register for pop as well */
14788c2ecf20Sopenharmony_ci		if (kdev->version == QMSS_66AK2G)
14798c2ecf20Sopenharmony_ci			qmgr->reg_pop = qmgr->reg_push;
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci		list_add_tail(&qmgr->list, &kdev->qmgrs);
14828c2ecf20Sopenharmony_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",
14838c2ecf20Sopenharmony_ci			 qmgr->start_queue, qmgr->num_queues,
14848c2ecf20Sopenharmony_ci			 qmgr->reg_peek, qmgr->reg_status,
14858c2ecf20Sopenharmony_ci			 qmgr->reg_config, qmgr->reg_region,
14868c2ecf20Sopenharmony_ci			 qmgr->reg_push, qmgr->reg_pop);
14878c2ecf20Sopenharmony_ci	}
14888c2ecf20Sopenharmony_ci	return 0;
14898c2ecf20Sopenharmony_ci}
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_cistatic int knav_queue_init_pdsps(struct knav_device *kdev,
14928c2ecf20Sopenharmony_ci					struct device_node *pdsps)
14938c2ecf20Sopenharmony_ci{
14948c2ecf20Sopenharmony_ci	struct device *dev = kdev->dev;
14958c2ecf20Sopenharmony_ci	struct knav_pdsp_info *pdsp;
14968c2ecf20Sopenharmony_ci	struct device_node *child;
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci	for_each_child_of_node(pdsps, child) {
14998c2ecf20Sopenharmony_ci		pdsp = devm_kzalloc(dev, sizeof(*pdsp), GFP_KERNEL);
15008c2ecf20Sopenharmony_ci		if (!pdsp) {
15018c2ecf20Sopenharmony_ci			dev_err(dev, "out of memory allocating pdsp\n");
15028c2ecf20Sopenharmony_ci			return -ENOMEM;
15038c2ecf20Sopenharmony_ci		}
15048c2ecf20Sopenharmony_ci		pdsp->name = knav_queue_find_name(child);
15058c2ecf20Sopenharmony_ci		pdsp->iram =
15068c2ecf20Sopenharmony_ci			knav_queue_map_reg(kdev, child,
15078c2ecf20Sopenharmony_ci					   KNAV_QUEUE_PDSP_IRAM_REG_INDEX);
15088c2ecf20Sopenharmony_ci		pdsp->regs =
15098c2ecf20Sopenharmony_ci			knav_queue_map_reg(kdev, child,
15108c2ecf20Sopenharmony_ci					   KNAV_QUEUE_PDSP_REGS_REG_INDEX);
15118c2ecf20Sopenharmony_ci		pdsp->intd =
15128c2ecf20Sopenharmony_ci			knav_queue_map_reg(kdev, child,
15138c2ecf20Sopenharmony_ci					   KNAV_QUEUE_PDSP_INTD_REG_INDEX);
15148c2ecf20Sopenharmony_ci		pdsp->command =
15158c2ecf20Sopenharmony_ci			knav_queue_map_reg(kdev, child,
15168c2ecf20Sopenharmony_ci					   KNAV_QUEUE_PDSP_CMD_REG_INDEX);
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci		if (IS_ERR(pdsp->command) || IS_ERR(pdsp->iram) ||
15198c2ecf20Sopenharmony_ci		    IS_ERR(pdsp->regs) || IS_ERR(pdsp->intd)) {
15208c2ecf20Sopenharmony_ci			dev_err(dev, "failed to map pdsp %s regs\n",
15218c2ecf20Sopenharmony_ci				pdsp->name);
15228c2ecf20Sopenharmony_ci			if (!IS_ERR(pdsp->command))
15238c2ecf20Sopenharmony_ci				devm_iounmap(dev, pdsp->command);
15248c2ecf20Sopenharmony_ci			if (!IS_ERR(pdsp->iram))
15258c2ecf20Sopenharmony_ci				devm_iounmap(dev, pdsp->iram);
15268c2ecf20Sopenharmony_ci			if (!IS_ERR(pdsp->regs))
15278c2ecf20Sopenharmony_ci				devm_iounmap(dev, pdsp->regs);
15288c2ecf20Sopenharmony_ci			if (!IS_ERR(pdsp->intd))
15298c2ecf20Sopenharmony_ci				devm_iounmap(dev, pdsp->intd);
15308c2ecf20Sopenharmony_ci			devm_kfree(dev, pdsp);
15318c2ecf20Sopenharmony_ci			continue;
15328c2ecf20Sopenharmony_ci		}
15338c2ecf20Sopenharmony_ci		of_property_read_u32(child, "id", &pdsp->id);
15348c2ecf20Sopenharmony_ci		list_add_tail(&pdsp->list, &kdev->pdsps);
15358c2ecf20Sopenharmony_ci		dev_dbg(dev, "added pdsp %s: command %p, iram %p, regs %p, intd %p\n",
15368c2ecf20Sopenharmony_ci			pdsp->name, pdsp->command, pdsp->iram, pdsp->regs,
15378c2ecf20Sopenharmony_ci			pdsp->intd);
15388c2ecf20Sopenharmony_ci	}
15398c2ecf20Sopenharmony_ci	return 0;
15408c2ecf20Sopenharmony_ci}
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_cistatic int knav_queue_stop_pdsp(struct knav_device *kdev,
15438c2ecf20Sopenharmony_ci			  struct knav_pdsp_info *pdsp)
15448c2ecf20Sopenharmony_ci{
15458c2ecf20Sopenharmony_ci	u32 val, timeout = 1000;
15468c2ecf20Sopenharmony_ci	int ret;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	val = readl_relaxed(&pdsp->regs->control) & ~PDSP_CTRL_ENABLE;
15498c2ecf20Sopenharmony_ci	writel_relaxed(val, &pdsp->regs->control);
15508c2ecf20Sopenharmony_ci	ret = knav_queue_pdsp_wait(&pdsp->regs->control, timeout,
15518c2ecf20Sopenharmony_ci					PDSP_CTRL_RUNNING);
15528c2ecf20Sopenharmony_ci	if (ret < 0) {
15538c2ecf20Sopenharmony_ci		dev_err(kdev->dev, "timed out on pdsp %s stop\n", pdsp->name);
15548c2ecf20Sopenharmony_ci		return ret;
15558c2ecf20Sopenharmony_ci	}
15568c2ecf20Sopenharmony_ci	pdsp->loaded = false;
15578c2ecf20Sopenharmony_ci	pdsp->started = false;
15588c2ecf20Sopenharmony_ci	return 0;
15598c2ecf20Sopenharmony_ci}
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_cistatic int knav_queue_load_pdsp(struct knav_device *kdev,
15628c2ecf20Sopenharmony_ci			  struct knav_pdsp_info *pdsp)
15638c2ecf20Sopenharmony_ci{
15648c2ecf20Sopenharmony_ci	int i, ret, fwlen;
15658c2ecf20Sopenharmony_ci	const struct firmware *fw;
15668c2ecf20Sopenharmony_ci	bool found = false;
15678c2ecf20Sopenharmony_ci	u32 *fwdata;
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(knav_acc_firmwares); i++) {
15708c2ecf20Sopenharmony_ci		if (knav_acc_firmwares[i]) {
15718c2ecf20Sopenharmony_ci			ret = request_firmware_direct(&fw,
15728c2ecf20Sopenharmony_ci						      knav_acc_firmwares[i],
15738c2ecf20Sopenharmony_ci						      kdev->dev);
15748c2ecf20Sopenharmony_ci			if (!ret) {
15758c2ecf20Sopenharmony_ci				found = true;
15768c2ecf20Sopenharmony_ci				break;
15778c2ecf20Sopenharmony_ci			}
15788c2ecf20Sopenharmony_ci		}
15798c2ecf20Sopenharmony_ci	}
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	if (!found) {
15828c2ecf20Sopenharmony_ci		dev_err(kdev->dev, "failed to get firmware for pdsp\n");
15838c2ecf20Sopenharmony_ci		return -ENODEV;
15848c2ecf20Sopenharmony_ci	}
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci	dev_info(kdev->dev, "firmware file %s downloaded for PDSP\n",
15878c2ecf20Sopenharmony_ci		 knav_acc_firmwares[i]);
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci	writel_relaxed(pdsp->id + 1, pdsp->command + 0x18);
15908c2ecf20Sopenharmony_ci	/* download the firmware */
15918c2ecf20Sopenharmony_ci	fwdata = (u32 *)fw->data;
15928c2ecf20Sopenharmony_ci	fwlen = (fw->size + sizeof(u32) - 1) / sizeof(u32);
15938c2ecf20Sopenharmony_ci	for (i = 0; i < fwlen; i++)
15948c2ecf20Sopenharmony_ci		writel_relaxed(be32_to_cpu(fwdata[i]), pdsp->iram + i);
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	release_firmware(fw);
15978c2ecf20Sopenharmony_ci	return 0;
15988c2ecf20Sopenharmony_ci}
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_cistatic int knav_queue_start_pdsp(struct knav_device *kdev,
16018c2ecf20Sopenharmony_ci			   struct knav_pdsp_info *pdsp)
16028c2ecf20Sopenharmony_ci{
16038c2ecf20Sopenharmony_ci	u32 val, timeout = 1000;
16048c2ecf20Sopenharmony_ci	int ret;
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	/* write a command for sync */
16078c2ecf20Sopenharmony_ci	writel_relaxed(0xffffffff, pdsp->command);
16088c2ecf20Sopenharmony_ci	while (readl_relaxed(pdsp->command) != 0xffffffff)
16098c2ecf20Sopenharmony_ci		cpu_relax();
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	/* soft reset the PDSP */
16128c2ecf20Sopenharmony_ci	val  = readl_relaxed(&pdsp->regs->control);
16138c2ecf20Sopenharmony_ci	val &= ~(PDSP_CTRL_PC_MASK | PDSP_CTRL_SOFT_RESET);
16148c2ecf20Sopenharmony_ci	writel_relaxed(val, &pdsp->regs->control);
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci	/* enable pdsp */
16178c2ecf20Sopenharmony_ci	val = readl_relaxed(&pdsp->regs->control) | PDSP_CTRL_ENABLE;
16188c2ecf20Sopenharmony_ci	writel_relaxed(val, &pdsp->regs->control);
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	/* wait for command register to clear */
16218c2ecf20Sopenharmony_ci	ret = knav_queue_pdsp_wait(pdsp->command, timeout, 0);
16228c2ecf20Sopenharmony_ci	if (ret < 0) {
16238c2ecf20Sopenharmony_ci		dev_err(kdev->dev,
16248c2ecf20Sopenharmony_ci			"timed out on pdsp %s command register wait\n",
16258c2ecf20Sopenharmony_ci			pdsp->name);
16268c2ecf20Sopenharmony_ci		return ret;
16278c2ecf20Sopenharmony_ci	}
16288c2ecf20Sopenharmony_ci	return 0;
16298c2ecf20Sopenharmony_ci}
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_cistatic void knav_queue_stop_pdsps(struct knav_device *kdev)
16328c2ecf20Sopenharmony_ci{
16338c2ecf20Sopenharmony_ci	struct knav_pdsp_info *pdsp;
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci	/* disable all pdsps */
16368c2ecf20Sopenharmony_ci	for_each_pdsp(kdev, pdsp)
16378c2ecf20Sopenharmony_ci		knav_queue_stop_pdsp(kdev, pdsp);
16388c2ecf20Sopenharmony_ci}
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_cistatic int knav_queue_start_pdsps(struct knav_device *kdev)
16418c2ecf20Sopenharmony_ci{
16428c2ecf20Sopenharmony_ci	struct knav_pdsp_info *pdsp;
16438c2ecf20Sopenharmony_ci	int ret;
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci	knav_queue_stop_pdsps(kdev);
16468c2ecf20Sopenharmony_ci	/* now load them all. We return success even if pdsp
16478c2ecf20Sopenharmony_ci	 * is not loaded as acc channels are optional on having
16488c2ecf20Sopenharmony_ci	 * firmware availability in the system. We set the loaded
16498c2ecf20Sopenharmony_ci	 * and stated flag and when initialize the acc range, check
16508c2ecf20Sopenharmony_ci	 * it and init the range only if pdsp is started.
16518c2ecf20Sopenharmony_ci	 */
16528c2ecf20Sopenharmony_ci	for_each_pdsp(kdev, pdsp) {
16538c2ecf20Sopenharmony_ci		ret = knav_queue_load_pdsp(kdev, pdsp);
16548c2ecf20Sopenharmony_ci		if (!ret)
16558c2ecf20Sopenharmony_ci			pdsp->loaded = true;
16568c2ecf20Sopenharmony_ci	}
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	for_each_pdsp(kdev, pdsp) {
16598c2ecf20Sopenharmony_ci		if (pdsp->loaded) {
16608c2ecf20Sopenharmony_ci			ret = knav_queue_start_pdsp(kdev, pdsp);
16618c2ecf20Sopenharmony_ci			if (!ret)
16628c2ecf20Sopenharmony_ci				pdsp->started = true;
16638c2ecf20Sopenharmony_ci		}
16648c2ecf20Sopenharmony_ci	}
16658c2ecf20Sopenharmony_ci	return 0;
16668c2ecf20Sopenharmony_ci}
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_cistatic inline struct knav_qmgr_info *knav_find_qmgr(unsigned id)
16698c2ecf20Sopenharmony_ci{
16708c2ecf20Sopenharmony_ci	struct knav_qmgr_info *qmgr;
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci	for_each_qmgr(kdev, qmgr) {
16738c2ecf20Sopenharmony_ci		if ((id >= qmgr->start_queue) &&
16748c2ecf20Sopenharmony_ci		    (id < qmgr->start_queue + qmgr->num_queues))
16758c2ecf20Sopenharmony_ci			return qmgr;
16768c2ecf20Sopenharmony_ci	}
16778c2ecf20Sopenharmony_ci	return NULL;
16788c2ecf20Sopenharmony_ci}
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_cistatic int knav_queue_init_queue(struct knav_device *kdev,
16818c2ecf20Sopenharmony_ci					struct knav_range_info *range,
16828c2ecf20Sopenharmony_ci					struct knav_queue_inst *inst,
16838c2ecf20Sopenharmony_ci					unsigned id)
16848c2ecf20Sopenharmony_ci{
16858c2ecf20Sopenharmony_ci	char irq_name[KNAV_NAME_SIZE];
16868c2ecf20Sopenharmony_ci	inst->qmgr = knav_find_qmgr(id);
16878c2ecf20Sopenharmony_ci	if (!inst->qmgr)
16888c2ecf20Sopenharmony_ci		return -1;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&inst->handles);
16918c2ecf20Sopenharmony_ci	inst->kdev = kdev;
16928c2ecf20Sopenharmony_ci	inst->range = range;
16938c2ecf20Sopenharmony_ci	inst->irq_num = -1;
16948c2ecf20Sopenharmony_ci	inst->id = id;
16958c2ecf20Sopenharmony_ci	scnprintf(irq_name, sizeof(irq_name), "hwqueue-%d", id);
16968c2ecf20Sopenharmony_ci	inst->irq_name = kstrndup(irq_name, sizeof(irq_name), GFP_KERNEL);
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	if (range->ops && range->ops->init_queue)
16998c2ecf20Sopenharmony_ci		return range->ops->init_queue(range, inst);
17008c2ecf20Sopenharmony_ci	else
17018c2ecf20Sopenharmony_ci		return 0;
17028c2ecf20Sopenharmony_ci}
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_cistatic int knav_queue_init_queues(struct knav_device *kdev)
17058c2ecf20Sopenharmony_ci{
17068c2ecf20Sopenharmony_ci	struct knav_range_info *range;
17078c2ecf20Sopenharmony_ci	int size, id, base_idx;
17088c2ecf20Sopenharmony_ci	int idx = 0, ret = 0;
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci	/* how much do we need for instance data? */
17118c2ecf20Sopenharmony_ci	size = sizeof(struct knav_queue_inst);
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_ci	/* round this up to a power of 2, keep the index to instance
17148c2ecf20Sopenharmony_ci	 * arithmetic fast.
17158c2ecf20Sopenharmony_ci	 * */
17168c2ecf20Sopenharmony_ci	kdev->inst_shift = order_base_2(size);
17178c2ecf20Sopenharmony_ci	size = (1 << kdev->inst_shift) * kdev->num_queues_in_use;
17188c2ecf20Sopenharmony_ci	kdev->instances = devm_kzalloc(kdev->dev, size, GFP_KERNEL);
17198c2ecf20Sopenharmony_ci	if (!kdev->instances)
17208c2ecf20Sopenharmony_ci		return -ENOMEM;
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	for_each_queue_range(kdev, range) {
17238c2ecf20Sopenharmony_ci		if (range->ops && range->ops->init_range)
17248c2ecf20Sopenharmony_ci			range->ops->init_range(range);
17258c2ecf20Sopenharmony_ci		base_idx = idx;
17268c2ecf20Sopenharmony_ci		for (id = range->queue_base;
17278c2ecf20Sopenharmony_ci		     id < range->queue_base + range->num_queues; id++, idx++) {
17288c2ecf20Sopenharmony_ci			ret = knav_queue_init_queue(kdev, range,
17298c2ecf20Sopenharmony_ci					knav_queue_idx_to_inst(kdev, idx), id);
17308c2ecf20Sopenharmony_ci			if (ret < 0)
17318c2ecf20Sopenharmony_ci				return ret;
17328c2ecf20Sopenharmony_ci		}
17338c2ecf20Sopenharmony_ci		range->queue_base_inst =
17348c2ecf20Sopenharmony_ci			knav_queue_idx_to_inst(kdev, base_idx);
17358c2ecf20Sopenharmony_ci	}
17368c2ecf20Sopenharmony_ci	return 0;
17378c2ecf20Sopenharmony_ci}
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci/* Match table for of_platform binding */
17408c2ecf20Sopenharmony_cistatic const struct of_device_id keystone_qmss_of_match[] = {
17418c2ecf20Sopenharmony_ci	{
17428c2ecf20Sopenharmony_ci		.compatible = "ti,keystone-navigator-qmss",
17438c2ecf20Sopenharmony_ci	},
17448c2ecf20Sopenharmony_ci	{
17458c2ecf20Sopenharmony_ci		.compatible = "ti,66ak2g-navss-qm",
17468c2ecf20Sopenharmony_ci		.data	= (void *)QMSS_66AK2G,
17478c2ecf20Sopenharmony_ci	},
17488c2ecf20Sopenharmony_ci	{},
17498c2ecf20Sopenharmony_ci};
17508c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, keystone_qmss_of_match);
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_cistatic int knav_queue_probe(struct platform_device *pdev)
17538c2ecf20Sopenharmony_ci{
17548c2ecf20Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
17558c2ecf20Sopenharmony_ci	struct device_node *qmgrs, *queue_pools, *regions, *pdsps;
17568c2ecf20Sopenharmony_ci	const struct of_device_id *match;
17578c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
17588c2ecf20Sopenharmony_ci	u32 temp[2];
17598c2ecf20Sopenharmony_ci	int ret;
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	if (!node) {
17628c2ecf20Sopenharmony_ci		dev_err(dev, "device tree info unavailable\n");
17638c2ecf20Sopenharmony_ci		return -ENODEV;
17648c2ecf20Sopenharmony_ci	}
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	kdev = devm_kzalloc(dev, sizeof(struct knav_device), GFP_KERNEL);
17678c2ecf20Sopenharmony_ci	if (!kdev) {
17688c2ecf20Sopenharmony_ci		dev_err(dev, "memory allocation failed\n");
17698c2ecf20Sopenharmony_ci		return -ENOMEM;
17708c2ecf20Sopenharmony_ci	}
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	match = of_match_device(of_match_ptr(keystone_qmss_of_match), dev);
17738c2ecf20Sopenharmony_ci	if (match && match->data)
17748c2ecf20Sopenharmony_ci		kdev->version = QMSS_66AK2G;
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, kdev);
17778c2ecf20Sopenharmony_ci	kdev->dev = dev;
17788c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&kdev->queue_ranges);
17798c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&kdev->qmgrs);
17808c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&kdev->pools);
17818c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&kdev->regions);
17828c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&kdev->pdsps);
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
17858c2ecf20Sopenharmony_ci	ret = pm_runtime_resume_and_get(&pdev->dev);
17868c2ecf20Sopenharmony_ci	if (ret < 0) {
17878c2ecf20Sopenharmony_ci		pm_runtime_disable(&pdev->dev);
17888c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to enable QMSS\n");
17898c2ecf20Sopenharmony_ci		return ret;
17908c2ecf20Sopenharmony_ci	}
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_ci	if (of_property_read_u32_array(node, "queue-range", temp, 2)) {
17938c2ecf20Sopenharmony_ci		dev_err(dev, "queue-range not specified\n");
17948c2ecf20Sopenharmony_ci		ret = -ENODEV;
17958c2ecf20Sopenharmony_ci		goto err;
17968c2ecf20Sopenharmony_ci	}
17978c2ecf20Sopenharmony_ci	kdev->base_id    = temp[0];
17988c2ecf20Sopenharmony_ci	kdev->num_queues = temp[1];
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci	/* Initialize queue managers using device tree configuration */
18018c2ecf20Sopenharmony_ci	qmgrs =  of_get_child_by_name(node, "qmgrs");
18028c2ecf20Sopenharmony_ci	if (!qmgrs) {
18038c2ecf20Sopenharmony_ci		dev_err(dev, "queue manager info not specified\n");
18048c2ecf20Sopenharmony_ci		ret = -ENODEV;
18058c2ecf20Sopenharmony_ci		goto err;
18068c2ecf20Sopenharmony_ci	}
18078c2ecf20Sopenharmony_ci	ret = knav_queue_init_qmgrs(kdev, qmgrs);
18088c2ecf20Sopenharmony_ci	of_node_put(qmgrs);
18098c2ecf20Sopenharmony_ci	if (ret)
18108c2ecf20Sopenharmony_ci		goto err;
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	/* get pdsp configuration values from device tree */
18138c2ecf20Sopenharmony_ci	pdsps =  of_get_child_by_name(node, "pdsps");
18148c2ecf20Sopenharmony_ci	if (pdsps) {
18158c2ecf20Sopenharmony_ci		ret = knav_queue_init_pdsps(kdev, pdsps);
18168c2ecf20Sopenharmony_ci		if (ret)
18178c2ecf20Sopenharmony_ci			goto err;
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci		ret = knav_queue_start_pdsps(kdev);
18208c2ecf20Sopenharmony_ci		if (ret)
18218c2ecf20Sopenharmony_ci			goto err;
18228c2ecf20Sopenharmony_ci	}
18238c2ecf20Sopenharmony_ci	of_node_put(pdsps);
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_ci	/* get usable queue range values from device tree */
18268c2ecf20Sopenharmony_ci	queue_pools = of_get_child_by_name(node, "queue-pools");
18278c2ecf20Sopenharmony_ci	if (!queue_pools) {
18288c2ecf20Sopenharmony_ci		dev_err(dev, "queue-pools not specified\n");
18298c2ecf20Sopenharmony_ci		ret = -ENODEV;
18308c2ecf20Sopenharmony_ci		goto err;
18318c2ecf20Sopenharmony_ci	}
18328c2ecf20Sopenharmony_ci	ret = knav_setup_queue_pools(kdev, queue_pools);
18338c2ecf20Sopenharmony_ci	of_node_put(queue_pools);
18348c2ecf20Sopenharmony_ci	if (ret)
18358c2ecf20Sopenharmony_ci		goto err;
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci	ret = knav_get_link_ram(kdev, "linkram0", &kdev->link_rams[0]);
18388c2ecf20Sopenharmony_ci	if (ret) {
18398c2ecf20Sopenharmony_ci		dev_err(kdev->dev, "could not setup linking ram\n");
18408c2ecf20Sopenharmony_ci		goto err;
18418c2ecf20Sopenharmony_ci	}
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci	ret = knav_get_link_ram(kdev, "linkram1", &kdev->link_rams[1]);
18448c2ecf20Sopenharmony_ci	if (ret) {
18458c2ecf20Sopenharmony_ci		/*
18468c2ecf20Sopenharmony_ci		 * nothing really, we have one linking ram already, so we just
18478c2ecf20Sopenharmony_ci		 * live within our means
18488c2ecf20Sopenharmony_ci		 */
18498c2ecf20Sopenharmony_ci	}
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	ret = knav_queue_setup_link_ram(kdev);
18528c2ecf20Sopenharmony_ci	if (ret)
18538c2ecf20Sopenharmony_ci		goto err;
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ci	regions = of_get_child_by_name(node, "descriptor-regions");
18568c2ecf20Sopenharmony_ci	if (!regions) {
18578c2ecf20Sopenharmony_ci		dev_err(dev, "descriptor-regions not specified\n");
18588c2ecf20Sopenharmony_ci		ret = -ENODEV;
18598c2ecf20Sopenharmony_ci		goto err;
18608c2ecf20Sopenharmony_ci	}
18618c2ecf20Sopenharmony_ci	ret = knav_queue_setup_regions(kdev, regions);
18628c2ecf20Sopenharmony_ci	of_node_put(regions);
18638c2ecf20Sopenharmony_ci	if (ret)
18648c2ecf20Sopenharmony_ci		goto err;
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci	ret = knav_queue_init_queues(kdev);
18678c2ecf20Sopenharmony_ci	if (ret < 0) {
18688c2ecf20Sopenharmony_ci		dev_err(dev, "hwqueue initialization failed\n");
18698c2ecf20Sopenharmony_ci		goto err;
18708c2ecf20Sopenharmony_ci	}
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci	debugfs_create_file("qmss", S_IFREG | S_IRUGO, NULL, NULL,
18738c2ecf20Sopenharmony_ci			    &knav_queue_debug_fops);
18748c2ecf20Sopenharmony_ci	device_ready = true;
18758c2ecf20Sopenharmony_ci	return 0;
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_cierr:
18788c2ecf20Sopenharmony_ci	knav_queue_stop_pdsps(kdev);
18798c2ecf20Sopenharmony_ci	knav_queue_free_regions(kdev);
18808c2ecf20Sopenharmony_ci	knav_free_queue_ranges(kdev);
18818c2ecf20Sopenharmony_ci	pm_runtime_put_sync(&pdev->dev);
18828c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
18838c2ecf20Sopenharmony_ci	return ret;
18848c2ecf20Sopenharmony_ci}
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_cistatic int knav_queue_remove(struct platform_device *pdev)
18878c2ecf20Sopenharmony_ci{
18888c2ecf20Sopenharmony_ci	/* TODO: Free resources */
18898c2ecf20Sopenharmony_ci	pm_runtime_put_sync(&pdev->dev);
18908c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
18918c2ecf20Sopenharmony_ci	return 0;
18928c2ecf20Sopenharmony_ci}
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_cistatic struct platform_driver keystone_qmss_driver = {
18958c2ecf20Sopenharmony_ci	.probe		= knav_queue_probe,
18968c2ecf20Sopenharmony_ci	.remove		= knav_queue_remove,
18978c2ecf20Sopenharmony_ci	.driver		= {
18988c2ecf20Sopenharmony_ci		.name	= "keystone-navigator-qmss",
18998c2ecf20Sopenharmony_ci		.of_match_table = keystone_qmss_of_match,
19008c2ecf20Sopenharmony_ci	},
19018c2ecf20Sopenharmony_ci};
19028c2ecf20Sopenharmony_cimodule_platform_driver(keystone_qmss_driver);
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
19058c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TI QMSS driver for Keystone SOCs");
19068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sandeep Nair <sandeep_n@ti.com>");
19078c2ecf20Sopenharmony_ciMODULE_AUTHOR("Santosh Shilimkar <santosh.shilimkar@ti.com>");
1908