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