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