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