162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Keystone accumulator queue manager
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
662306a36Sopenharmony_ci * Author:	Sandeep Nair <sandeep_n@ti.com>
762306a36Sopenharmony_ci *		Cyril Chemparathy <cyril@ti.com>
862306a36Sopenharmony_ci *		Santosh Shilimkar <santosh.shilimkar@ti.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/of_address.h>
1662306a36Sopenharmony_ci#include <linux/soc/ti/knav_qmss.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "knav_qmss.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define knav_range_offset_to_inst(kdev, range, q)	\
2162306a36Sopenharmony_ci	(range->queue_base_inst + (q << kdev->inst_shift))
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic void __knav_acc_notify(struct knav_range_info *range,
2462306a36Sopenharmony_ci				struct knav_acc_channel *acc)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct knav_device *kdev = range->kdev;
2762306a36Sopenharmony_ci	struct knav_queue_inst *inst;
2862306a36Sopenharmony_ci	int range_base, queue;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	range_base = kdev->base_id + range->queue_base;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (range->flags & RANGE_MULTI_QUEUE) {
3362306a36Sopenharmony_ci		for (queue = 0; queue < range->num_queues; queue++) {
3462306a36Sopenharmony_ci			inst = knav_range_offset_to_inst(kdev, range,
3562306a36Sopenharmony_ci								queue);
3662306a36Sopenharmony_ci			if (inst->notify_needed) {
3762306a36Sopenharmony_ci				inst->notify_needed = 0;
3862306a36Sopenharmony_ci				dev_dbg(kdev->dev, "acc-irq: notifying %d\n",
3962306a36Sopenharmony_ci					range_base + queue);
4062306a36Sopenharmony_ci				knav_queue_notify(inst);
4162306a36Sopenharmony_ci			}
4262306a36Sopenharmony_ci		}
4362306a36Sopenharmony_ci	} else {
4462306a36Sopenharmony_ci		queue = acc->channel - range->acc_info.start_channel;
4562306a36Sopenharmony_ci		inst = knav_range_offset_to_inst(kdev, range, queue);
4662306a36Sopenharmony_ci		dev_dbg(kdev->dev, "acc-irq: notifying %d\n",
4762306a36Sopenharmony_ci			range_base + queue);
4862306a36Sopenharmony_ci		knav_queue_notify(inst);
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic int knav_acc_set_notify(struct knav_range_info *range,
5362306a36Sopenharmony_ci				struct knav_queue_inst *kq,
5462306a36Sopenharmony_ci				bool enabled)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct knav_pdsp_info *pdsp = range->acc_info.pdsp;
5762306a36Sopenharmony_ci	struct knav_device *kdev = range->kdev;
5862306a36Sopenharmony_ci	u32 mask, offset;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/*
6162306a36Sopenharmony_ci	 * when enabling, we need to re-trigger an interrupt if we
6262306a36Sopenharmony_ci	 * have descriptors pending
6362306a36Sopenharmony_ci	 */
6462306a36Sopenharmony_ci	if (!enabled || atomic_read(&kq->desc_count) <= 0)
6562306a36Sopenharmony_ci		return 0;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	kq->notify_needed = 1;
6862306a36Sopenharmony_ci	atomic_inc(&kq->acc->retrigger_count);
6962306a36Sopenharmony_ci	mask = BIT(kq->acc->channel % 32);
7062306a36Sopenharmony_ci	offset = ACC_INTD_OFFSET_STATUS(kq->acc->channel);
7162306a36Sopenharmony_ci	dev_dbg(kdev->dev, "setup-notify: re-triggering irq for %s\n",
7262306a36Sopenharmony_ci		kq->acc->name);
7362306a36Sopenharmony_ci	writel_relaxed(mask, pdsp->intd + offset);
7462306a36Sopenharmony_ci	return 0;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic irqreturn_t knav_acc_int_handler(int irq, void *_instdata)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct knav_acc_channel *acc;
8062306a36Sopenharmony_ci	struct knav_queue_inst *kq = NULL;
8162306a36Sopenharmony_ci	struct knav_range_info *range;
8262306a36Sopenharmony_ci	struct knav_pdsp_info *pdsp;
8362306a36Sopenharmony_ci	struct knav_acc_info *info;
8462306a36Sopenharmony_ci	struct knav_device *kdev;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	u32 *list, *list_cpu, val, idx, notifies;
8762306a36Sopenharmony_ci	int range_base, channel, queue = 0;
8862306a36Sopenharmony_ci	dma_addr_t list_dma;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	range = _instdata;
9162306a36Sopenharmony_ci	info  = &range->acc_info;
9262306a36Sopenharmony_ci	kdev  = range->kdev;
9362306a36Sopenharmony_ci	pdsp  = range->acc_info.pdsp;
9462306a36Sopenharmony_ci	acc   = range->acc;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	range_base = kdev->base_id + range->queue_base;
9762306a36Sopenharmony_ci	if ((range->flags & RANGE_MULTI_QUEUE) == 0) {
9862306a36Sopenharmony_ci		for (queue = 0; queue < range->num_irqs; queue++)
9962306a36Sopenharmony_ci			if (range->irqs[queue].irq == irq)
10062306a36Sopenharmony_ci				break;
10162306a36Sopenharmony_ci		kq = knav_range_offset_to_inst(kdev, range, queue);
10262306a36Sopenharmony_ci		acc += queue;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	channel = acc->channel;
10662306a36Sopenharmony_ci	list_dma = acc->list_dma[acc->list_index];
10762306a36Sopenharmony_ci	list_cpu = acc->list_cpu[acc->list_index];
10862306a36Sopenharmony_ci	dev_dbg(kdev->dev, "acc-irq: channel %d, list %d, virt %p, dma %pad\n",
10962306a36Sopenharmony_ci		channel, acc->list_index, list_cpu, &list_dma);
11062306a36Sopenharmony_ci	if (atomic_read(&acc->retrigger_count)) {
11162306a36Sopenharmony_ci		atomic_dec(&acc->retrigger_count);
11262306a36Sopenharmony_ci		__knav_acc_notify(range, acc);
11362306a36Sopenharmony_ci		writel_relaxed(1, pdsp->intd + ACC_INTD_OFFSET_COUNT(channel));
11462306a36Sopenharmony_ci		/* ack the interrupt */
11562306a36Sopenharmony_ci		writel_relaxed(ACC_CHANNEL_INT_BASE + channel,
11662306a36Sopenharmony_ci			       pdsp->intd + ACC_INTD_OFFSET_EOI);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci		return IRQ_HANDLED;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	notifies = readl_relaxed(pdsp->intd + ACC_INTD_OFFSET_COUNT(channel));
12262306a36Sopenharmony_ci	WARN_ON(!notifies);
12362306a36Sopenharmony_ci	dma_sync_single_for_cpu(kdev->dev, list_dma, info->list_size,
12462306a36Sopenharmony_ci				DMA_FROM_DEVICE);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	for (list = list_cpu; list < list_cpu + (info->list_size / sizeof(u32));
12762306a36Sopenharmony_ci	     list += ACC_LIST_ENTRY_WORDS) {
12862306a36Sopenharmony_ci		if (ACC_LIST_ENTRY_WORDS == 1) {
12962306a36Sopenharmony_ci			dev_dbg(kdev->dev,
13062306a36Sopenharmony_ci				"acc-irq: list %d, entry @%p, %08x\n",
13162306a36Sopenharmony_ci				acc->list_index, list, list[0]);
13262306a36Sopenharmony_ci		} else if (ACC_LIST_ENTRY_WORDS == 2) {
13362306a36Sopenharmony_ci			dev_dbg(kdev->dev,
13462306a36Sopenharmony_ci				"acc-irq: list %d, entry @%p, %08x %08x\n",
13562306a36Sopenharmony_ci				acc->list_index, list, list[0], list[1]);
13662306a36Sopenharmony_ci		} else if (ACC_LIST_ENTRY_WORDS == 4) {
13762306a36Sopenharmony_ci			dev_dbg(kdev->dev,
13862306a36Sopenharmony_ci				"acc-irq: list %d, entry @%p, %08x %08x %08x %08x\n",
13962306a36Sopenharmony_ci				acc->list_index, list, list[0], list[1],
14062306a36Sopenharmony_ci				list[2], list[3]);
14162306a36Sopenharmony_ci		}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		val = list[ACC_LIST_ENTRY_DESC_IDX];
14462306a36Sopenharmony_ci		if (!val)
14562306a36Sopenharmony_ci			break;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci		if (range->flags & RANGE_MULTI_QUEUE) {
14862306a36Sopenharmony_ci			queue = list[ACC_LIST_ENTRY_QUEUE_IDX] >> 16;
14962306a36Sopenharmony_ci			if (queue < range_base ||
15062306a36Sopenharmony_ci			    queue >= range_base + range->num_queues) {
15162306a36Sopenharmony_ci				dev_err(kdev->dev,
15262306a36Sopenharmony_ci					"bad queue %d, expecting %d-%d\n",
15362306a36Sopenharmony_ci					queue, range_base,
15462306a36Sopenharmony_ci					range_base + range->num_queues);
15562306a36Sopenharmony_ci				break;
15662306a36Sopenharmony_ci			}
15762306a36Sopenharmony_ci			queue -= range_base;
15862306a36Sopenharmony_ci			kq = knav_range_offset_to_inst(kdev, range,
15962306a36Sopenharmony_ci								queue);
16062306a36Sopenharmony_ci		}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		if (atomic_inc_return(&kq->desc_count) >= ACC_DESCS_MAX) {
16362306a36Sopenharmony_ci			atomic_dec(&kq->desc_count);
16462306a36Sopenharmony_ci			dev_err(kdev->dev,
16562306a36Sopenharmony_ci				"acc-irq: queue %d full, entry dropped\n",
16662306a36Sopenharmony_ci				queue + range_base);
16762306a36Sopenharmony_ci			continue;
16862306a36Sopenharmony_ci		}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		idx = atomic_inc_return(&kq->desc_tail) & ACC_DESCS_MASK;
17162306a36Sopenharmony_ci		kq->descs[idx] = val;
17262306a36Sopenharmony_ci		kq->notify_needed = 1;
17362306a36Sopenharmony_ci		dev_dbg(kdev->dev, "acc-irq: enqueue %08x at %d, queue %d\n",
17462306a36Sopenharmony_ci			val, idx, queue + range_base);
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	__knav_acc_notify(range, acc);
17862306a36Sopenharmony_ci	memset(list_cpu, 0, info->list_size);
17962306a36Sopenharmony_ci	dma_sync_single_for_device(kdev->dev, list_dma, info->list_size,
18062306a36Sopenharmony_ci				   DMA_TO_DEVICE);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* flip to the other list */
18362306a36Sopenharmony_ci	acc->list_index ^= 1;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/* reset the interrupt counter */
18662306a36Sopenharmony_ci	writel_relaxed(1, pdsp->intd + ACC_INTD_OFFSET_COUNT(channel));
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* ack the interrupt */
18962306a36Sopenharmony_ci	writel_relaxed(ACC_CHANNEL_INT_BASE + channel,
19062306a36Sopenharmony_ci		       pdsp->intd + ACC_INTD_OFFSET_EOI);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return IRQ_HANDLED;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic int knav_range_setup_acc_irq(struct knav_range_info *range,
19662306a36Sopenharmony_ci				int queue, bool enabled)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct knav_device *kdev = range->kdev;
19962306a36Sopenharmony_ci	struct knav_acc_channel *acc;
20062306a36Sopenharmony_ci	struct cpumask *cpu_mask;
20162306a36Sopenharmony_ci	int ret = 0, irq;
20262306a36Sopenharmony_ci	u32 old, new;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (range->flags & RANGE_MULTI_QUEUE) {
20562306a36Sopenharmony_ci		acc = range->acc;
20662306a36Sopenharmony_ci		irq = range->irqs[0].irq;
20762306a36Sopenharmony_ci		cpu_mask = range->irqs[0].cpu_mask;
20862306a36Sopenharmony_ci	} else {
20962306a36Sopenharmony_ci		acc = range->acc + queue;
21062306a36Sopenharmony_ci		irq = range->irqs[queue].irq;
21162306a36Sopenharmony_ci		cpu_mask = range->irqs[queue].cpu_mask;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	old = acc->open_mask;
21562306a36Sopenharmony_ci	if (enabled)
21662306a36Sopenharmony_ci		new = old | BIT(queue);
21762306a36Sopenharmony_ci	else
21862306a36Sopenharmony_ci		new = old & ~BIT(queue);
21962306a36Sopenharmony_ci	acc->open_mask = new;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	dev_dbg(kdev->dev,
22262306a36Sopenharmony_ci		"setup-acc-irq: open mask old %08x, new %08x, channel %s\n",
22362306a36Sopenharmony_ci		old, new, acc->name);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (likely(new == old))
22662306a36Sopenharmony_ci		return 0;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (new && !old) {
22962306a36Sopenharmony_ci		dev_dbg(kdev->dev,
23062306a36Sopenharmony_ci			"setup-acc-irq: requesting %s for channel %s\n",
23162306a36Sopenharmony_ci			acc->name, acc->name);
23262306a36Sopenharmony_ci		ret = request_irq(irq, knav_acc_int_handler, 0, acc->name,
23362306a36Sopenharmony_ci				  range);
23462306a36Sopenharmony_ci		if (!ret && cpu_mask) {
23562306a36Sopenharmony_ci			ret = irq_set_affinity_hint(irq, cpu_mask);
23662306a36Sopenharmony_ci			if (ret) {
23762306a36Sopenharmony_ci				dev_warn(range->kdev->dev,
23862306a36Sopenharmony_ci					 "Failed to set IRQ affinity\n");
23962306a36Sopenharmony_ci				return ret;
24062306a36Sopenharmony_ci			}
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (old && !new) {
24562306a36Sopenharmony_ci		dev_dbg(kdev->dev, "setup-acc-irq: freeing %s for channel %s\n",
24662306a36Sopenharmony_ci			acc->name, acc->name);
24762306a36Sopenharmony_ci		ret = irq_set_affinity_hint(irq, NULL);
24862306a36Sopenharmony_ci		if (ret)
24962306a36Sopenharmony_ci			dev_warn(range->kdev->dev,
25062306a36Sopenharmony_ci				 "Failed to set IRQ affinity\n");
25162306a36Sopenharmony_ci		free_irq(irq, range);
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return ret;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic const char *knav_acc_result_str(enum knav_acc_result result)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	static const char * const result_str[] = {
26062306a36Sopenharmony_ci		[ACC_RET_IDLE]			= "idle",
26162306a36Sopenharmony_ci		[ACC_RET_SUCCESS]		= "success",
26262306a36Sopenharmony_ci		[ACC_RET_INVALID_COMMAND]	= "invalid command",
26362306a36Sopenharmony_ci		[ACC_RET_INVALID_CHANNEL]	= "invalid channel",
26462306a36Sopenharmony_ci		[ACC_RET_INACTIVE_CHANNEL]	= "inactive channel",
26562306a36Sopenharmony_ci		[ACC_RET_ACTIVE_CHANNEL]	= "active channel",
26662306a36Sopenharmony_ci		[ACC_RET_INVALID_QUEUE]		= "invalid queue",
26762306a36Sopenharmony_ci		[ACC_RET_INVALID_RET]		= "invalid return code",
26862306a36Sopenharmony_ci	};
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (result >= ARRAY_SIZE(result_str))
27162306a36Sopenharmony_ci		return result_str[ACC_RET_INVALID_RET];
27262306a36Sopenharmony_ci	else
27362306a36Sopenharmony_ci		return result_str[result];
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic enum knav_acc_result
27762306a36Sopenharmony_ciknav_acc_write(struct knav_device *kdev, struct knav_pdsp_info *pdsp,
27862306a36Sopenharmony_ci		struct knav_reg_acc_command *cmd)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	u32 result;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	dev_dbg(kdev->dev, "acc command %08x %08x %08x %08x %08x\n",
28362306a36Sopenharmony_ci		cmd->command, cmd->queue_mask, cmd->list_dma,
28462306a36Sopenharmony_ci		cmd->queue_num, cmd->timer_config);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	writel_relaxed(cmd->timer_config, &pdsp->acc_command->timer_config);
28762306a36Sopenharmony_ci	writel_relaxed(cmd->queue_num, &pdsp->acc_command->queue_num);
28862306a36Sopenharmony_ci	writel_relaxed(cmd->list_dma, &pdsp->acc_command->list_dma);
28962306a36Sopenharmony_ci	writel_relaxed(cmd->queue_mask, &pdsp->acc_command->queue_mask);
29062306a36Sopenharmony_ci	writel_relaxed(cmd->command, &pdsp->acc_command->command);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* wait for the command to clear */
29362306a36Sopenharmony_ci	do {
29462306a36Sopenharmony_ci		result = readl_relaxed(&pdsp->acc_command->command);
29562306a36Sopenharmony_ci	} while ((result >> 8) & 0xff);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	return (result >> 24) & 0xff;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic void knav_acc_setup_cmd(struct knav_device *kdev,
30162306a36Sopenharmony_ci				struct knav_range_info *range,
30262306a36Sopenharmony_ci				struct knav_reg_acc_command *cmd,
30362306a36Sopenharmony_ci				int queue)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct knav_acc_info *info = &range->acc_info;
30662306a36Sopenharmony_ci	struct knav_acc_channel *acc;
30762306a36Sopenharmony_ci	int queue_base;
30862306a36Sopenharmony_ci	u32 queue_mask;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (range->flags & RANGE_MULTI_QUEUE) {
31162306a36Sopenharmony_ci		acc = range->acc;
31262306a36Sopenharmony_ci		queue_base = range->queue_base;
31362306a36Sopenharmony_ci		queue_mask = BIT(range->num_queues) - 1;
31462306a36Sopenharmony_ci	} else {
31562306a36Sopenharmony_ci		acc = range->acc + queue;
31662306a36Sopenharmony_ci		queue_base = range->queue_base + queue;
31762306a36Sopenharmony_ci		queue_mask = 0;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	memset(cmd, 0, sizeof(*cmd));
32162306a36Sopenharmony_ci	cmd->command    = acc->channel;
32262306a36Sopenharmony_ci	cmd->queue_mask = queue_mask;
32362306a36Sopenharmony_ci	cmd->list_dma   = (u32)acc->list_dma[0];
32462306a36Sopenharmony_ci	cmd->queue_num  = info->list_entries << 16;
32562306a36Sopenharmony_ci	cmd->queue_num |= queue_base;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	cmd->timer_config = ACC_LIST_ENTRY_TYPE << 18;
32862306a36Sopenharmony_ci	if (range->flags & RANGE_MULTI_QUEUE)
32962306a36Sopenharmony_ci		cmd->timer_config |= ACC_CFG_MULTI_QUEUE;
33062306a36Sopenharmony_ci	cmd->timer_config |= info->pacing_mode << 16;
33162306a36Sopenharmony_ci	cmd->timer_config |= info->timer_count;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void knav_acc_stop(struct knav_device *kdev,
33562306a36Sopenharmony_ci				struct knav_range_info *range,
33662306a36Sopenharmony_ci				int queue)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct knav_reg_acc_command cmd;
33962306a36Sopenharmony_ci	struct knav_acc_channel *acc;
34062306a36Sopenharmony_ci	enum knav_acc_result result;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	acc = range->acc + queue;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	knav_acc_setup_cmd(kdev, range, &cmd, queue);
34562306a36Sopenharmony_ci	cmd.command |= ACC_CMD_DISABLE_CHANNEL << 8;
34662306a36Sopenharmony_ci	result = knav_acc_write(kdev, range->acc_info.pdsp, &cmd);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	dev_dbg(kdev->dev, "stopped acc channel %s, result %s\n",
34962306a36Sopenharmony_ci		acc->name, knav_acc_result_str(result));
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic enum knav_acc_result knav_acc_start(struct knav_device *kdev,
35362306a36Sopenharmony_ci						struct knav_range_info *range,
35462306a36Sopenharmony_ci						int queue)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct knav_reg_acc_command cmd;
35762306a36Sopenharmony_ci	struct knav_acc_channel *acc;
35862306a36Sopenharmony_ci	enum knav_acc_result result;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	acc = range->acc + queue;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	knav_acc_setup_cmd(kdev, range, &cmd, queue);
36362306a36Sopenharmony_ci	cmd.command |= ACC_CMD_ENABLE_CHANNEL << 8;
36462306a36Sopenharmony_ci	result = knav_acc_write(kdev, range->acc_info.pdsp, &cmd);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	dev_dbg(kdev->dev, "started acc channel %s, result %s\n",
36762306a36Sopenharmony_ci		acc->name, knav_acc_result_str(result));
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	return result;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic int knav_acc_init_range(struct knav_range_info *range)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct knav_device *kdev = range->kdev;
37562306a36Sopenharmony_ci	struct knav_acc_channel *acc;
37662306a36Sopenharmony_ci	enum knav_acc_result result;
37762306a36Sopenharmony_ci	int queue;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	for (queue = 0; queue < range->num_queues; queue++) {
38062306a36Sopenharmony_ci		acc = range->acc + queue;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		knav_acc_stop(kdev, range, queue);
38362306a36Sopenharmony_ci		acc->list_index = 0;
38462306a36Sopenharmony_ci		result = knav_acc_start(kdev, range, queue);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		if (result != ACC_RET_SUCCESS)
38762306a36Sopenharmony_ci			return -EIO;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		if (range->flags & RANGE_MULTI_QUEUE)
39062306a36Sopenharmony_ci			return 0;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci	return 0;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic int knav_acc_init_queue(struct knav_range_info *range,
39662306a36Sopenharmony_ci				struct knav_queue_inst *kq)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	unsigned id = kq->id - range->queue_base;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	kq->descs = devm_kcalloc(range->kdev->dev,
40162306a36Sopenharmony_ci				 ACC_DESCS_MAX, sizeof(u32), GFP_KERNEL);
40262306a36Sopenharmony_ci	if (!kq->descs)
40362306a36Sopenharmony_ci		return -ENOMEM;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	kq->acc = range->acc;
40662306a36Sopenharmony_ci	if ((range->flags & RANGE_MULTI_QUEUE) == 0)
40762306a36Sopenharmony_ci		kq->acc += id;
40862306a36Sopenharmony_ci	return 0;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic int knav_acc_open_queue(struct knav_range_info *range,
41262306a36Sopenharmony_ci				struct knav_queue_inst *inst, unsigned flags)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	unsigned id = inst->id - range->queue_base;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	return knav_range_setup_acc_irq(range, id, true);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic int knav_acc_close_queue(struct knav_range_info *range,
42062306a36Sopenharmony_ci					struct knav_queue_inst *inst)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	unsigned id = inst->id - range->queue_base;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	return knav_range_setup_acc_irq(range, id, false);
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic int knav_acc_free_range(struct knav_range_info *range)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	struct knav_device *kdev = range->kdev;
43062306a36Sopenharmony_ci	struct knav_acc_channel *acc;
43162306a36Sopenharmony_ci	struct knav_acc_info *info;
43262306a36Sopenharmony_ci	int channel, channels;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	info = &range->acc_info;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (range->flags & RANGE_MULTI_QUEUE)
43762306a36Sopenharmony_ci		channels = 1;
43862306a36Sopenharmony_ci	else
43962306a36Sopenharmony_ci		channels = range->num_queues;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	for (channel = 0; channel < channels; channel++) {
44262306a36Sopenharmony_ci		acc = range->acc + channel;
44362306a36Sopenharmony_ci		if (!acc->list_cpu[0])
44462306a36Sopenharmony_ci			continue;
44562306a36Sopenharmony_ci		dma_unmap_single(kdev->dev, acc->list_dma[0],
44662306a36Sopenharmony_ci				 info->mem_size, DMA_BIDIRECTIONAL);
44762306a36Sopenharmony_ci		free_pages_exact(acc->list_cpu[0], info->mem_size);
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci	devm_kfree(range->kdev->dev, range->acc);
45062306a36Sopenharmony_ci	return 0;
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic struct knav_range_ops knav_acc_range_ops = {
45462306a36Sopenharmony_ci	.set_notify	= knav_acc_set_notify,
45562306a36Sopenharmony_ci	.init_queue	= knav_acc_init_queue,
45662306a36Sopenharmony_ci	.open_queue	= knav_acc_open_queue,
45762306a36Sopenharmony_ci	.close_queue	= knav_acc_close_queue,
45862306a36Sopenharmony_ci	.init_range	= knav_acc_init_range,
45962306a36Sopenharmony_ci	.free_range	= knav_acc_free_range,
46062306a36Sopenharmony_ci};
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci/**
46362306a36Sopenharmony_ci * knav_init_acc_range: Initialise accumulator ranges
46462306a36Sopenharmony_ci *
46562306a36Sopenharmony_ci * @kdev:		qmss device
46662306a36Sopenharmony_ci * @node:		device node
46762306a36Sopenharmony_ci * @range:		qmms range information
46862306a36Sopenharmony_ci *
46962306a36Sopenharmony_ci * Return 0 on success or error
47062306a36Sopenharmony_ci */
47162306a36Sopenharmony_ciint knav_init_acc_range(struct knav_device *kdev,
47262306a36Sopenharmony_ci			struct device_node *node,
47362306a36Sopenharmony_ci			struct knav_range_info *range)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct knav_acc_channel *acc;
47662306a36Sopenharmony_ci	struct knav_pdsp_info *pdsp;
47762306a36Sopenharmony_ci	struct knav_acc_info *info;
47862306a36Sopenharmony_ci	int ret, channel, channels;
47962306a36Sopenharmony_ci	int list_size, mem_size;
48062306a36Sopenharmony_ci	dma_addr_t list_dma;
48162306a36Sopenharmony_ci	void *list_mem;
48262306a36Sopenharmony_ci	u32 config[5];
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	range->flags |= RANGE_HAS_ACCUMULATOR;
48562306a36Sopenharmony_ci	info = &range->acc_info;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	ret = of_property_read_u32_array(node, "accumulator", config, 5);
48862306a36Sopenharmony_ci	if (ret)
48962306a36Sopenharmony_ci		return ret;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	info->pdsp_id		= config[0];
49262306a36Sopenharmony_ci	info->start_channel	= config[1];
49362306a36Sopenharmony_ci	info->list_entries	= config[2];
49462306a36Sopenharmony_ci	info->pacing_mode	= config[3];
49562306a36Sopenharmony_ci	info->timer_count	= config[4] / ACC_DEFAULT_PERIOD;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (info->start_channel > ACC_MAX_CHANNEL) {
49862306a36Sopenharmony_ci		dev_err(kdev->dev, "channel %d invalid for range %s\n",
49962306a36Sopenharmony_ci			info->start_channel, range->name);
50062306a36Sopenharmony_ci		return -EINVAL;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (info->pacing_mode > 3) {
50462306a36Sopenharmony_ci		dev_err(kdev->dev, "pacing mode %d invalid for range %s\n",
50562306a36Sopenharmony_ci			info->pacing_mode, range->name);
50662306a36Sopenharmony_ci		return -EINVAL;
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	pdsp = knav_find_pdsp(kdev, info->pdsp_id);
51062306a36Sopenharmony_ci	if (!pdsp) {
51162306a36Sopenharmony_ci		dev_err(kdev->dev, "pdsp id %d not found for range %s\n",
51262306a36Sopenharmony_ci			info->pdsp_id, range->name);
51362306a36Sopenharmony_ci		return -EINVAL;
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (!pdsp->started) {
51762306a36Sopenharmony_ci		dev_err(kdev->dev, "pdsp id %d not started for range %s\n",
51862306a36Sopenharmony_ci			info->pdsp_id, range->name);
51962306a36Sopenharmony_ci		return -ENODEV;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	info->pdsp = pdsp;
52362306a36Sopenharmony_ci	channels = range->num_queues;
52462306a36Sopenharmony_ci	if (of_property_read_bool(node, "multi-queue")) {
52562306a36Sopenharmony_ci		range->flags |= RANGE_MULTI_QUEUE;
52662306a36Sopenharmony_ci		channels = 1;
52762306a36Sopenharmony_ci		if (range->queue_base & (32 - 1)) {
52862306a36Sopenharmony_ci			dev_err(kdev->dev,
52962306a36Sopenharmony_ci				"misaligned multi-queue accumulator range %s\n",
53062306a36Sopenharmony_ci				range->name);
53162306a36Sopenharmony_ci			return -EINVAL;
53262306a36Sopenharmony_ci		}
53362306a36Sopenharmony_ci		if (range->num_queues > 32) {
53462306a36Sopenharmony_ci			dev_err(kdev->dev,
53562306a36Sopenharmony_ci				"too many queues in accumulator range %s\n",
53662306a36Sopenharmony_ci				range->name);
53762306a36Sopenharmony_ci			return -EINVAL;
53862306a36Sopenharmony_ci		}
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	/* figure out list size */
54262306a36Sopenharmony_ci	list_size  = info->list_entries;
54362306a36Sopenharmony_ci	list_size *= ACC_LIST_ENTRY_WORDS * sizeof(u32);
54462306a36Sopenharmony_ci	info->list_size = list_size;
54562306a36Sopenharmony_ci	mem_size   = PAGE_ALIGN(list_size * 2);
54662306a36Sopenharmony_ci	info->mem_size  = mem_size;
54762306a36Sopenharmony_ci	range->acc = devm_kcalloc(kdev->dev, channels, sizeof(*range->acc),
54862306a36Sopenharmony_ci				  GFP_KERNEL);
54962306a36Sopenharmony_ci	if (!range->acc)
55062306a36Sopenharmony_ci		return -ENOMEM;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	for (channel = 0; channel < channels; channel++) {
55362306a36Sopenharmony_ci		acc = range->acc + channel;
55462306a36Sopenharmony_ci		acc->channel = info->start_channel + channel;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		/* allocate memory for the two lists */
55762306a36Sopenharmony_ci		list_mem = alloc_pages_exact(mem_size, GFP_KERNEL | GFP_DMA);
55862306a36Sopenharmony_ci		if (!list_mem)
55962306a36Sopenharmony_ci			return -ENOMEM;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci		list_dma = dma_map_single(kdev->dev, list_mem, mem_size,
56262306a36Sopenharmony_ci					  DMA_BIDIRECTIONAL);
56362306a36Sopenharmony_ci		if (dma_mapping_error(kdev->dev, list_dma)) {
56462306a36Sopenharmony_ci			free_pages_exact(list_mem, mem_size);
56562306a36Sopenharmony_ci			return -ENOMEM;
56662306a36Sopenharmony_ci		}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		memset(list_mem, 0, mem_size);
56962306a36Sopenharmony_ci		dma_sync_single_for_device(kdev->dev, list_dma, mem_size,
57062306a36Sopenharmony_ci					   DMA_TO_DEVICE);
57162306a36Sopenharmony_ci		scnprintf(acc->name, sizeof(acc->name), "hwqueue-acc-%d",
57262306a36Sopenharmony_ci			  acc->channel);
57362306a36Sopenharmony_ci		acc->list_cpu[0] = list_mem;
57462306a36Sopenharmony_ci		acc->list_cpu[1] = list_mem + list_size;
57562306a36Sopenharmony_ci		acc->list_dma[0] = list_dma;
57662306a36Sopenharmony_ci		acc->list_dma[1] = list_dma + list_size;
57762306a36Sopenharmony_ci		dev_dbg(kdev->dev, "%s: channel %d, dma %pad, virt %8p\n",
57862306a36Sopenharmony_ci			acc->name, acc->channel, &list_dma, list_mem);
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	range->ops = &knav_acc_range_ops;
58262306a36Sopenharmony_ci	return 0;
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(knav_init_acc_range);
585