18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/******************************************************************************* 38c2ecf20Sopenharmony_ci AudioScience HPI driver 48c2ecf20Sopenharmony_ci Common Linux HPI ioctl and module probe/remove functions 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci*******************************************************************************/ 108c2ecf20Sopenharmony_ci#define SOURCEFILE_NAME "hpioctl.c" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "hpi_internal.h" 138c2ecf20Sopenharmony_ci#include "hpi_version.h" 148c2ecf20Sopenharmony_ci#include "hpimsginit.h" 158c2ecf20Sopenharmony_ci#include "hpidebug.h" 168c2ecf20Sopenharmony_ci#include "hpimsgx.h" 178c2ecf20Sopenharmony_ci#include "hpioctl.h" 188c2ecf20Sopenharmony_ci#include "hpicmn.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/fs.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 248c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 258c2ecf20Sopenharmony_ci#include <linux/pci.h> 268c2ecf20Sopenharmony_ci#include <linux/stringify.h> 278c2ecf20Sopenharmony_ci#include <linux/module.h> 288c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 298c2ecf20Sopenharmony_ci#include <linux/nospec.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#ifdef MODULE_FIRMWARE 328c2ecf20Sopenharmony_ciMODULE_FIRMWARE("asihpi/dsp5000.bin"); 338c2ecf20Sopenharmony_ciMODULE_FIRMWARE("asihpi/dsp6200.bin"); 348c2ecf20Sopenharmony_ciMODULE_FIRMWARE("asihpi/dsp6205.bin"); 358c2ecf20Sopenharmony_ciMODULE_FIRMWARE("asihpi/dsp6400.bin"); 368c2ecf20Sopenharmony_ciMODULE_FIRMWARE("asihpi/dsp6600.bin"); 378c2ecf20Sopenharmony_ciMODULE_FIRMWARE("asihpi/dsp8700.bin"); 388c2ecf20Sopenharmony_ciMODULE_FIRMWARE("asihpi/dsp8900.bin"); 398c2ecf20Sopenharmony_ci#endif 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int prealloc_stream_buf; 428c2ecf20Sopenharmony_cimodule_param(prealloc_stream_buf, int, 0444); 438c2ecf20Sopenharmony_ciMODULE_PARM_DESC(prealloc_stream_buf, 448c2ecf20Sopenharmony_ci "Preallocate size for per-adapter stream buffer"); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* Allow the debug level to be changed after module load. 478c2ecf20Sopenharmony_ci E.g. echo 2 > /sys/module/asihpi/parameters/hpiDebugLevel 488c2ecf20Sopenharmony_ci*/ 498c2ecf20Sopenharmony_cimodule_param(hpi_debug_level, int, 0644); 508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(hpi_debug_level, "debug verbosity 0..5"); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* List of adapters found */ 538c2ecf20Sopenharmony_cistatic struct hpi_adapter adapters[HPI_MAX_ADAPTERS]; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* Wrapper function to HPI_Message to enable dumping of the 568c2ecf20Sopenharmony_ci message and response types. 578c2ecf20Sopenharmony_ci*/ 588c2ecf20Sopenharmony_cistatic void hpi_send_recv_f(struct hpi_message *phm, struct hpi_response *phr, 598c2ecf20Sopenharmony_ci struct file *file) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci if ((phm->adapter_index >= HPI_MAX_ADAPTERS) 628c2ecf20Sopenharmony_ci && (phm->object != HPI_OBJ_SUBSYSTEM)) 638c2ecf20Sopenharmony_ci phr->error = HPI_ERROR_INVALID_OBJ_INDEX; 648c2ecf20Sopenharmony_ci else 658c2ecf20Sopenharmony_ci hpi_send_recv_ex(phm, phr, file); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* This is called from hpifunc.c functions, called by ALSA 698c2ecf20Sopenharmony_ci * (or other kernel process) In this case there is no file descriptor 708c2ecf20Sopenharmony_ci * available for the message cache code 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_civoid hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci hpi_send_recv_f(phm, phr, HOWNER_KERNEL); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hpi_send_recv); 788c2ecf20Sopenharmony_ci/* for radio-asihpi */ 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ciint asihpi_hpi_release(struct file *file) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct hpi_message hm; 838c2ecf20Sopenharmony_ci struct hpi_response hr; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* HPI_DEBUG_LOG(INFO,"hpi_release file %p, pid %d\n", file, current->pid); */ 868c2ecf20Sopenharmony_ci /* close the subsystem just in case the application forgot to. */ 878c2ecf20Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, 888c2ecf20Sopenharmony_ci HPI_SUBSYS_CLOSE); 898c2ecf20Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, file); 908c2ecf20Sopenharmony_ci return 0; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cilong asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct hpi_ioctl_linux __user *phpi_ioctl_data; 968c2ecf20Sopenharmony_ci void __user *puhm; 978c2ecf20Sopenharmony_ci void __user *puhr; 988c2ecf20Sopenharmony_ci union hpi_message_buffer_v1 *hm; 998c2ecf20Sopenharmony_ci union hpi_response_buffer_v1 *hr; 1008c2ecf20Sopenharmony_ci u16 msg_size; 1018c2ecf20Sopenharmony_ci u16 res_max_size; 1028c2ecf20Sopenharmony_ci u32 uncopied_bytes; 1038c2ecf20Sopenharmony_ci int err = 0; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (cmd != HPI_IOCTL_LINUX) 1068c2ecf20Sopenharmony_ci return -EINVAL; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci hm = kmalloc(sizeof(*hm), GFP_KERNEL); 1098c2ecf20Sopenharmony_ci hr = kzalloc(sizeof(*hr), GFP_KERNEL); 1108c2ecf20Sopenharmony_ci if (!hm || !hr) { 1118c2ecf20Sopenharmony_ci err = -ENOMEM; 1128c2ecf20Sopenharmony_ci goto out; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci phpi_ioctl_data = (struct hpi_ioctl_linux __user *)arg; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* Read the message and response pointers from user space. */ 1188c2ecf20Sopenharmony_ci if (get_user(puhm, &phpi_ioctl_data->phm) 1198c2ecf20Sopenharmony_ci || get_user(puhr, &phpi_ioctl_data->phr)) { 1208c2ecf20Sopenharmony_ci err = -EFAULT; 1218c2ecf20Sopenharmony_ci goto out; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* Now read the message size and data from user space. */ 1258c2ecf20Sopenharmony_ci if (get_user(msg_size, (u16 __user *)puhm)) { 1268c2ecf20Sopenharmony_ci err = -EFAULT; 1278c2ecf20Sopenharmony_ci goto out; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci if (msg_size > sizeof(*hm)) 1308c2ecf20Sopenharmony_ci msg_size = sizeof(*hm); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* printk(KERN_INFO "message size %d\n", hm->h.wSize); */ 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci uncopied_bytes = copy_from_user(hm, puhm, msg_size); 1358c2ecf20Sopenharmony_ci if (uncopied_bytes) { 1368c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes); 1378c2ecf20Sopenharmony_ci err = -EFAULT; 1388c2ecf20Sopenharmony_ci goto out; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Override h.size in case it is changed between two userspace fetches */ 1428c2ecf20Sopenharmony_ci hm->h.size = msg_size; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (get_user(res_max_size, (u16 __user *)puhr)) { 1458c2ecf20Sopenharmony_ci err = -EFAULT; 1468c2ecf20Sopenharmony_ci goto out; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci /* printk(KERN_INFO "user response size %d\n", res_max_size); */ 1498c2ecf20Sopenharmony_ci if (res_max_size < sizeof(struct hpi_response_header)) { 1508c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(WARNING, "small res size %d\n", res_max_size); 1518c2ecf20Sopenharmony_ci err = -EFAULT; 1528c2ecf20Sopenharmony_ci goto out; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci res_max_size = min_t(size_t, res_max_size, sizeof(*hr)); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci switch (hm->h.function) { 1588c2ecf20Sopenharmony_ci case HPI_SUBSYS_CREATE_ADAPTER: 1598c2ecf20Sopenharmony_ci case HPI_ADAPTER_DELETE: 1608c2ecf20Sopenharmony_ci /* Application must not use these functions! */ 1618c2ecf20Sopenharmony_ci hr->h.size = sizeof(hr->h); 1628c2ecf20Sopenharmony_ci hr->h.error = HPI_ERROR_INVALID_OPERATION; 1638c2ecf20Sopenharmony_ci hr->h.function = hm->h.function; 1648c2ecf20Sopenharmony_ci uncopied_bytes = copy_to_user(puhr, hr, hr->h.size); 1658c2ecf20Sopenharmony_ci if (uncopied_bytes) 1668c2ecf20Sopenharmony_ci err = -EFAULT; 1678c2ecf20Sopenharmony_ci else 1688c2ecf20Sopenharmony_ci err = 0; 1698c2ecf20Sopenharmony_ci goto out; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci hr->h.size = res_max_size; 1738c2ecf20Sopenharmony_ci if (hm->h.object == HPI_OBJ_SUBSYSTEM) { 1748c2ecf20Sopenharmony_ci hpi_send_recv_f(&hm->m0, &hr->r0, file); 1758c2ecf20Sopenharmony_ci } else { 1768c2ecf20Sopenharmony_ci u16 __user *ptr = NULL; 1778c2ecf20Sopenharmony_ci u32 size = 0; 1788c2ecf20Sopenharmony_ci /* -1=no data 0=read from user mem, 1=write to user mem */ 1798c2ecf20Sopenharmony_ci int wrflag = -1; 1808c2ecf20Sopenharmony_ci struct hpi_adapter *pa = NULL; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (hm->h.adapter_index < ARRAY_SIZE(adapters)) 1838c2ecf20Sopenharmony_ci pa = &adapters[array_index_nospec(hm->h.adapter_index, 1848c2ecf20Sopenharmony_ci ARRAY_SIZE(adapters))]; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (!pa || !pa->adapter || !pa->adapter->type) { 1878c2ecf20Sopenharmony_ci hpi_init_response(&hr->r0, hm->h.object, 1888c2ecf20Sopenharmony_ci hm->h.function, HPI_ERROR_BAD_ADAPTER_NUMBER); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci uncopied_bytes = 1918c2ecf20Sopenharmony_ci copy_to_user(puhr, hr, sizeof(hr->h)); 1928c2ecf20Sopenharmony_ci if (uncopied_bytes) 1938c2ecf20Sopenharmony_ci err = -EFAULT; 1948c2ecf20Sopenharmony_ci else 1958c2ecf20Sopenharmony_ci err = 0; 1968c2ecf20Sopenharmony_ci goto out; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&pa->mutex)) { 2008c2ecf20Sopenharmony_ci err = -EINTR; 2018c2ecf20Sopenharmony_ci goto out; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* Dig out any pointers embedded in the message. */ 2058c2ecf20Sopenharmony_ci switch (hm->h.function) { 2068c2ecf20Sopenharmony_ci case HPI_OSTREAM_WRITE: 2078c2ecf20Sopenharmony_ci case HPI_ISTREAM_READ:{ 2088c2ecf20Sopenharmony_ci /* Yes, sparse, this is correct. */ 2098c2ecf20Sopenharmony_ci ptr = (u16 __user *)hm->m0.u.d.u.data.pb_data; 2108c2ecf20Sopenharmony_ci size = hm->m0.u.d.u.data.data_size; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* Allocate buffer according to application request. 2138c2ecf20Sopenharmony_ci ?Is it better to alloc/free for the duration 2148c2ecf20Sopenharmony_ci of the transaction? 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci if (pa->buffer_size < size) { 2178c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(DEBUG, 2188c2ecf20Sopenharmony_ci "Realloc adapter %d stream " 2198c2ecf20Sopenharmony_ci "buffer from %zd to %d\n", 2208c2ecf20Sopenharmony_ci hm->h.adapter_index, 2218c2ecf20Sopenharmony_ci pa->buffer_size, size); 2228c2ecf20Sopenharmony_ci if (pa->p_buffer) { 2238c2ecf20Sopenharmony_ci pa->buffer_size = 0; 2248c2ecf20Sopenharmony_ci vfree(pa->p_buffer); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci pa->p_buffer = vmalloc(size); 2278c2ecf20Sopenharmony_ci if (pa->p_buffer) 2288c2ecf20Sopenharmony_ci pa->buffer_size = size; 2298c2ecf20Sopenharmony_ci else { 2308c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(ERROR, 2318c2ecf20Sopenharmony_ci "HPI could not allocate " 2328c2ecf20Sopenharmony_ci "stream buffer size %d\n", 2338c2ecf20Sopenharmony_ci size); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci mutex_unlock(&pa->mutex); 2368c2ecf20Sopenharmony_ci err = -EINVAL; 2378c2ecf20Sopenharmony_ci goto out; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci hm->m0.u.d.u.data.pb_data = pa->p_buffer; 2428c2ecf20Sopenharmony_ci if (hm->h.function == HPI_ISTREAM_READ) 2438c2ecf20Sopenharmony_ci /* from card, WRITE to user mem */ 2448c2ecf20Sopenharmony_ci wrflag = 1; 2458c2ecf20Sopenharmony_ci else 2468c2ecf20Sopenharmony_ci wrflag = 0; 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci default: 2518c2ecf20Sopenharmony_ci size = 0; 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (size && (wrflag == 0)) { 2568c2ecf20Sopenharmony_ci uncopied_bytes = 2578c2ecf20Sopenharmony_ci copy_from_user(pa->p_buffer, ptr, size); 2588c2ecf20Sopenharmony_ci if (uncopied_bytes) 2598c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(WARNING, 2608c2ecf20Sopenharmony_ci "Missed %d of %d " 2618c2ecf20Sopenharmony_ci "bytes from user\n", uncopied_bytes, 2628c2ecf20Sopenharmony_ci size); 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci hpi_send_recv_f(&hm->m0, &hr->r0, file); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (size && (wrflag == 1)) { 2688c2ecf20Sopenharmony_ci uncopied_bytes = 2698c2ecf20Sopenharmony_ci copy_to_user(ptr, pa->p_buffer, size); 2708c2ecf20Sopenharmony_ci if (uncopied_bytes) 2718c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(WARNING, 2728c2ecf20Sopenharmony_ci "Missed %d of %d " "bytes to user\n", 2738c2ecf20Sopenharmony_ci uncopied_bytes, size); 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci mutex_unlock(&pa->mutex); 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* on return response size must be set */ 2808c2ecf20Sopenharmony_ci /*printk(KERN_INFO "response size %d\n", hr->h.wSize); */ 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (!hr->h.size) { 2838c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(ERROR, "response zero size\n"); 2848c2ecf20Sopenharmony_ci err = -EFAULT; 2858c2ecf20Sopenharmony_ci goto out; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (hr->h.size > res_max_size) { 2898c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(ERROR, "response too big %d %d\n", hr->h.size, 2908c2ecf20Sopenharmony_ci res_max_size); 2918c2ecf20Sopenharmony_ci hr->h.error = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL; 2928c2ecf20Sopenharmony_ci hr->h.specific_error = hr->h.size; 2938c2ecf20Sopenharmony_ci hr->h.size = sizeof(hr->h); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci uncopied_bytes = copy_to_user(puhr, hr, hr->h.size); 2978c2ecf20Sopenharmony_ci if (uncopied_bytes) { 2988c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes); 2998c2ecf20Sopenharmony_ci err = -EFAULT; 3008c2ecf20Sopenharmony_ci goto out; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ciout: 3048c2ecf20Sopenharmony_ci kfree(hm); 3058c2ecf20Sopenharmony_ci kfree(hr); 3068c2ecf20Sopenharmony_ci return err; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int asihpi_irq_count; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic irqreturn_t asihpi_isr(int irq, void *dev_id) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct hpi_adapter *a = dev_id; 3148c2ecf20Sopenharmony_ci int handled; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (!a->adapter->irq_query_and_clear) { 3178c2ecf20Sopenharmony_ci pr_err("asihpi_isr ASI%04X:%d no handler\n", a->adapter->type, 3188c2ecf20Sopenharmony_ci a->adapter->index); 3198c2ecf20Sopenharmony_ci return IRQ_NONE; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci handled = a->adapter->irq_query_and_clear(a->adapter, 0); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (!handled) 3258c2ecf20Sopenharmony_ci return IRQ_NONE; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci asihpi_irq_count++; 3288c2ecf20Sopenharmony_ci /* printk(KERN_INFO "asihpi_isr %d ASI%04X:%d irq handled\n", 3298c2ecf20Sopenharmony_ci asihpi_irq_count, a->adapter->type, a->adapter->index); */ 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (a->interrupt_callback) 3328c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic irqreturn_t asihpi_isr_thread(int irq, void *dev_id) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct hpi_adapter *a = dev_id; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (a->interrupt_callback) 3428c2ecf20Sopenharmony_ci a->interrupt_callback(a); 3438c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ciint asihpi_adapter_probe(struct pci_dev *pci_dev, 3478c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci int idx, nm, low_latency_mode = 0, irq_supported = 0; 3508c2ecf20Sopenharmony_ci int adapter_index; 3518c2ecf20Sopenharmony_ci unsigned int memlen; 3528c2ecf20Sopenharmony_ci struct hpi_message hm; 3538c2ecf20Sopenharmony_ci struct hpi_response hr; 3548c2ecf20Sopenharmony_ci struct hpi_adapter adapter; 3558c2ecf20Sopenharmony_ci struct hpi_pci pci = { 0 }; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci memset(&adapter, 0, sizeof(adapter)); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &pci_dev->dev, 3608c2ecf20Sopenharmony_ci "probe %04x:%04x,%04x:%04x,%04x\n", pci_dev->vendor, 3618c2ecf20Sopenharmony_ci pci_dev->device, pci_dev->subsystem_vendor, 3628c2ecf20Sopenharmony_ci pci_dev->subsystem_device, pci_dev->devfn); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (pcim_enable_device(pci_dev) < 0) { 3658c2ecf20Sopenharmony_ci dev_err(&pci_dev->dev, 3668c2ecf20Sopenharmony_ci "pci_enable_device failed, disabling device\n"); 3678c2ecf20Sopenharmony_ci return -EIO; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci pci_set_master(pci_dev); /* also sets latency timer if < 16 */ 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, 3738c2ecf20Sopenharmony_ci HPI_SUBSYS_CREATE_ADAPTER); 3748c2ecf20Sopenharmony_ci hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CREATE_ADAPTER, 3758c2ecf20Sopenharmony_ci HPI_ERROR_PROCESSING_MESSAGE); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci hm.adapter_index = HPI_ADAPTER_INDEX_INVALID; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci nm = HPI_MAX_ADAPTER_MEM_SPACES; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci for (idx = 0; idx < nm; idx++) { 3828c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(INFO, "resource %d %pR\n", idx, 3838c2ecf20Sopenharmony_ci &pci_dev->resource[idx]); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) { 3868c2ecf20Sopenharmony_ci memlen = pci_resource_len(pci_dev, idx); 3878c2ecf20Sopenharmony_ci pci.ap_mem_base[idx] = 3888c2ecf20Sopenharmony_ci ioremap(pci_resource_start(pci_dev, idx), 3898c2ecf20Sopenharmony_ci memlen); 3908c2ecf20Sopenharmony_ci if (!pci.ap_mem_base[idx]) { 3918c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(ERROR, 3928c2ecf20Sopenharmony_ci "ioremap failed, aborting\n"); 3938c2ecf20Sopenharmony_ci /* unmap previously mapped pci mem space */ 3948c2ecf20Sopenharmony_ci goto err; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci pci.pci_dev = pci_dev; 4008c2ecf20Sopenharmony_ci hm.u.s.resource.bus_type = HPI_BUS_PCI; 4018c2ecf20Sopenharmony_ci hm.u.s.resource.r.pci = &pci; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* call CreateAdapterObject on the relevant hpi module */ 4048c2ecf20Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 4058c2ecf20Sopenharmony_ci if (hr.error) 4068c2ecf20Sopenharmony_ci goto err; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci adapter_index = hr.u.s.adapter_index; 4098c2ecf20Sopenharmony_ci adapter.adapter = hpi_find_adapter(adapter_index); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (prealloc_stream_buf) { 4128c2ecf20Sopenharmony_ci adapter.p_buffer = vmalloc(prealloc_stream_buf); 4138c2ecf20Sopenharmony_ci if (!adapter.p_buffer) { 4148c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(ERROR, 4158c2ecf20Sopenharmony_ci "HPI could not allocate " 4168c2ecf20Sopenharmony_ci "kernel buffer size %d\n", 4178c2ecf20Sopenharmony_ci prealloc_stream_buf); 4188c2ecf20Sopenharmony_ci goto err; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, 4238c2ecf20Sopenharmony_ci HPI_ADAPTER_OPEN); 4248c2ecf20Sopenharmony_ci hm.adapter_index = adapter.adapter->index; 4258c2ecf20Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (hr.error) { 4288c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(ERROR, "HPI_ADAPTER_OPEN failed, aborting\n"); 4298c2ecf20Sopenharmony_ci goto err; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* Check if current mode == Low Latency mode */ 4338c2ecf20Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, 4348c2ecf20Sopenharmony_ci HPI_ADAPTER_GET_MODE); 4358c2ecf20Sopenharmony_ci hm.adapter_index = adapter.adapter->index; 4368c2ecf20Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (!hr.error 4398c2ecf20Sopenharmony_ci && hr.u.ax.mode.adapter_mode == HPI_ADAPTER_MODE_LOW_LATENCY) 4408c2ecf20Sopenharmony_ci low_latency_mode = 1; 4418c2ecf20Sopenharmony_ci else 4428c2ecf20Sopenharmony_ci dev_info(&pci_dev->dev, 4438c2ecf20Sopenharmony_ci "Adapter at index %d is not in low latency mode\n", 4448c2ecf20Sopenharmony_ci adapter.adapter->index); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* Check if IRQs are supported */ 4478c2ecf20Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, 4488c2ecf20Sopenharmony_ci HPI_ADAPTER_GET_PROPERTY); 4498c2ecf20Sopenharmony_ci hm.adapter_index = adapter.adapter->index; 4508c2ecf20Sopenharmony_ci hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_SUPPORTS_IRQ; 4518c2ecf20Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 4528c2ecf20Sopenharmony_ci if (hr.error || !hr.u.ax.property_get.parameter1) { 4538c2ecf20Sopenharmony_ci dev_info(&pci_dev->dev, 4548c2ecf20Sopenharmony_ci "IRQs not supported by adapter at index %d\n", 4558c2ecf20Sopenharmony_ci adapter.adapter->index); 4568c2ecf20Sopenharmony_ci } else { 4578c2ecf20Sopenharmony_ci irq_supported = 1; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* WARNING can't init mutex in 'adapter' 4618c2ecf20Sopenharmony_ci * and then copy it to adapters[] ?!?! 4628c2ecf20Sopenharmony_ci */ 4638c2ecf20Sopenharmony_ci adapters[adapter_index] = adapter; 4648c2ecf20Sopenharmony_ci mutex_init(&adapters[adapter_index].mutex); 4658c2ecf20Sopenharmony_ci pci_set_drvdata(pci_dev, &adapters[adapter_index]); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (low_latency_mode && irq_supported) { 4688c2ecf20Sopenharmony_ci if (!adapter.adapter->irq_query_and_clear) { 4698c2ecf20Sopenharmony_ci dev_err(&pci_dev->dev, 4708c2ecf20Sopenharmony_ci "no IRQ handler for adapter %d, aborting\n", 4718c2ecf20Sopenharmony_ci adapter.adapter->index); 4728c2ecf20Sopenharmony_ci goto err; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* Disable IRQ generation on DSP side by setting the rate to 0 */ 4768c2ecf20Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, 4778c2ecf20Sopenharmony_ci HPI_ADAPTER_SET_PROPERTY); 4788c2ecf20Sopenharmony_ci hm.adapter_index = adapter.adapter->index; 4798c2ecf20Sopenharmony_ci hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE; 4808c2ecf20Sopenharmony_ci hm.u.ax.property_set.parameter1 = 0; 4818c2ecf20Sopenharmony_ci hm.u.ax.property_set.parameter2 = 0; 4828c2ecf20Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 4838c2ecf20Sopenharmony_ci if (hr.error) { 4848c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(ERROR, 4858c2ecf20Sopenharmony_ci "HPI_ADAPTER_GET_MODE failed, aborting\n"); 4868c2ecf20Sopenharmony_ci goto err; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* Note: request_irq calls asihpi_isr here */ 4908c2ecf20Sopenharmony_ci if (request_threaded_irq(pci_dev->irq, asihpi_isr, 4918c2ecf20Sopenharmony_ci asihpi_isr_thread, IRQF_SHARED, 4928c2ecf20Sopenharmony_ci "asihpi", &adapters[adapter_index])) { 4938c2ecf20Sopenharmony_ci dev_err(&pci_dev->dev, "request_irq(%d) failed\n", 4948c2ecf20Sopenharmony_ci pci_dev->irq); 4958c2ecf20Sopenharmony_ci goto err; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci adapters[adapter_index].interrupt_mode = 1; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci dev_info(&pci_dev->dev, "using irq %d\n", pci_dev->irq); 5018c2ecf20Sopenharmony_ci adapters[adapter_index].irq = pci_dev->irq; 5028c2ecf20Sopenharmony_ci } else { 5038c2ecf20Sopenharmony_ci dev_info(&pci_dev->dev, "using polled mode\n"); 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci dev_info(&pci_dev->dev, "probe succeeded for ASI%04X HPI index %d\n", 5078c2ecf20Sopenharmony_ci adapter.adapter->type, adapter_index); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci return 0; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cierr: 5128c2ecf20Sopenharmony_ci while (--idx >= 0) { 5138c2ecf20Sopenharmony_ci if (pci.ap_mem_base[idx]) { 5148c2ecf20Sopenharmony_ci iounmap(pci.ap_mem_base[idx]); 5158c2ecf20Sopenharmony_ci pci.ap_mem_base[idx] = NULL; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if (adapter.p_buffer) { 5208c2ecf20Sopenharmony_ci adapter.buffer_size = 0; 5218c2ecf20Sopenharmony_ci vfree(adapter.p_buffer); 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(ERROR, "adapter_probe failed\n"); 5258c2ecf20Sopenharmony_ci return -ENODEV; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_civoid asihpi_adapter_remove(struct pci_dev *pci_dev) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci int idx; 5318c2ecf20Sopenharmony_ci struct hpi_message hm; 5328c2ecf20Sopenharmony_ci struct hpi_response hr; 5338c2ecf20Sopenharmony_ci struct hpi_adapter *pa; 5348c2ecf20Sopenharmony_ci struct hpi_pci pci; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci pa = pci_get_drvdata(pci_dev); 5378c2ecf20Sopenharmony_ci pci = pa->adapter->pci; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* Disable IRQ generation on DSP side */ 5408c2ecf20Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, 5418c2ecf20Sopenharmony_ci HPI_ADAPTER_SET_PROPERTY); 5428c2ecf20Sopenharmony_ci hm.adapter_index = pa->adapter->index; 5438c2ecf20Sopenharmony_ci hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE; 5448c2ecf20Sopenharmony_ci hm.u.ax.property_set.parameter1 = 0; 5458c2ecf20Sopenharmony_ci hm.u.ax.property_set.parameter2 = 0; 5468c2ecf20Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, 5498c2ecf20Sopenharmony_ci HPI_ADAPTER_DELETE); 5508c2ecf20Sopenharmony_ci hm.adapter_index = pa->adapter->index; 5518c2ecf20Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* unmap PCI memory space, mapped during device init. */ 5548c2ecf20Sopenharmony_ci for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; ++idx) 5558c2ecf20Sopenharmony_ci iounmap(pci.ap_mem_base[idx]); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (pa->irq) 5588c2ecf20Sopenharmony_ci free_irq(pa->irq, pa); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci vfree(pa->p_buffer); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (1) 5638c2ecf20Sopenharmony_ci dev_info(&pci_dev->dev, 5648c2ecf20Sopenharmony_ci "remove %04x:%04x,%04x:%04x,%04x, HPI index %d\n", 5658c2ecf20Sopenharmony_ci pci_dev->vendor, pci_dev->device, 5668c2ecf20Sopenharmony_ci pci_dev->subsystem_vendor, pci_dev->subsystem_device, 5678c2ecf20Sopenharmony_ci pci_dev->devfn, pa->adapter->index); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci memset(pa, 0, sizeof(*pa)); 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_civoid __init asihpi_init(void) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci struct hpi_message hm; 5758c2ecf20Sopenharmony_ci struct hpi_response hr; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci memset(adapters, 0, sizeof(adapters)); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci printk(KERN_INFO "ASIHPI driver " HPI_VER_STRING "\n"); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, 5828c2ecf20Sopenharmony_ci HPI_SUBSYS_DRIVER_LOAD); 5838c2ecf20Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_civoid asihpi_exit(void) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci struct hpi_message hm; 5898c2ecf20Sopenharmony_ci struct hpi_response hr; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, 5928c2ecf20Sopenharmony_ci HPI_SUBSYS_DRIVER_UNLOAD); 5938c2ecf20Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 5948c2ecf20Sopenharmony_ci} 595