162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/******************************************************************************* 362306a36Sopenharmony_ci AudioScience HPI driver 462306a36Sopenharmony_ci Common Linux HPI ioctl and module probe/remove functions 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci*******************************************************************************/ 1062306a36Sopenharmony_ci#define SOURCEFILE_NAME "hpioctl.c" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "hpi_internal.h" 1362306a36Sopenharmony_ci#include "hpi_version.h" 1462306a36Sopenharmony_ci#include "hpimsginit.h" 1562306a36Sopenharmony_ci#include "hpidebug.h" 1662306a36Sopenharmony_ci#include "hpimsgx.h" 1762306a36Sopenharmony_ci#include "hpioctl.h" 1862306a36Sopenharmony_ci#include "hpicmn.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/fs.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/moduleparam.h> 2462306a36Sopenharmony_ci#include <linux/uaccess.h> 2562306a36Sopenharmony_ci#include <linux/pci.h> 2662306a36Sopenharmony_ci#include <linux/stringify.h> 2762306a36Sopenharmony_ci#include <linux/module.h> 2862306a36Sopenharmony_ci#include <linux/vmalloc.h> 2962306a36Sopenharmony_ci#include <linux/nospec.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#ifdef MODULE_FIRMWARE 3262306a36Sopenharmony_ciMODULE_FIRMWARE("asihpi/dsp5000.bin"); 3362306a36Sopenharmony_ciMODULE_FIRMWARE("asihpi/dsp6200.bin"); 3462306a36Sopenharmony_ciMODULE_FIRMWARE("asihpi/dsp6205.bin"); 3562306a36Sopenharmony_ciMODULE_FIRMWARE("asihpi/dsp6400.bin"); 3662306a36Sopenharmony_ciMODULE_FIRMWARE("asihpi/dsp6600.bin"); 3762306a36Sopenharmony_ciMODULE_FIRMWARE("asihpi/dsp8700.bin"); 3862306a36Sopenharmony_ciMODULE_FIRMWARE("asihpi/dsp8900.bin"); 3962306a36Sopenharmony_ci#endif 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int prealloc_stream_buf; 4262306a36Sopenharmony_cimodule_param(prealloc_stream_buf, int, 0444); 4362306a36Sopenharmony_ciMODULE_PARM_DESC(prealloc_stream_buf, 4462306a36Sopenharmony_ci "Preallocate size for per-adapter stream buffer"); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Allow the debug level to be changed after module load. 4762306a36Sopenharmony_ci E.g. echo 2 > /sys/module/asihpi/parameters/hpiDebugLevel 4862306a36Sopenharmony_ci*/ 4962306a36Sopenharmony_cimodule_param(hpi_debug_level, int, 0644); 5062306a36Sopenharmony_ciMODULE_PARM_DESC(hpi_debug_level, "debug verbosity 0..5"); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* List of adapters found */ 5362306a36Sopenharmony_cistatic struct hpi_adapter adapters[HPI_MAX_ADAPTERS]; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* Wrapper function to HPI_Message to enable dumping of the 5662306a36Sopenharmony_ci message and response types. 5762306a36Sopenharmony_ci*/ 5862306a36Sopenharmony_cistatic void hpi_send_recv_f(struct hpi_message *phm, struct hpi_response *phr, 5962306a36Sopenharmony_ci struct file *file) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci if ((phm->adapter_index >= HPI_MAX_ADAPTERS) 6262306a36Sopenharmony_ci && (phm->object != HPI_OBJ_SUBSYSTEM)) 6362306a36Sopenharmony_ci phr->error = HPI_ERROR_INVALID_OBJ_INDEX; 6462306a36Sopenharmony_ci else 6562306a36Sopenharmony_ci hpi_send_recv_ex(phm, phr, file); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* This is called from hpifunc.c functions, called by ALSA 6962306a36Sopenharmony_ci * (or other kernel process) In this case there is no file descriptor 7062306a36Sopenharmony_ci * available for the message cache code 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_civoid hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci hpi_send_recv_f(phm, phr, HOWNER_KERNEL); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ciEXPORT_SYMBOL(hpi_send_recv); 7862306a36Sopenharmony_ci/* for radio-asihpi */ 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ciint asihpi_hpi_release(struct file *file) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct hpi_message hm; 8362306a36Sopenharmony_ci struct hpi_response hr; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* HPI_DEBUG_LOG(INFO,"hpi_release file %p, pid %d\n", file, current->pid); */ 8662306a36Sopenharmony_ci /* close the subsystem just in case the application forgot to. */ 8762306a36Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, 8862306a36Sopenharmony_ci HPI_SUBSYS_CLOSE); 8962306a36Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, file); 9062306a36Sopenharmony_ci return 0; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cilong asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct hpi_ioctl_linux __user *phpi_ioctl_data; 9662306a36Sopenharmony_ci void __user *puhm; 9762306a36Sopenharmony_ci void __user *puhr; 9862306a36Sopenharmony_ci union hpi_message_buffer_v1 *hm; 9962306a36Sopenharmony_ci union hpi_response_buffer_v1 *hr; 10062306a36Sopenharmony_ci u16 msg_size; 10162306a36Sopenharmony_ci u16 res_max_size; 10262306a36Sopenharmony_ci u32 uncopied_bytes; 10362306a36Sopenharmony_ci int err = 0; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (cmd != HPI_IOCTL_LINUX) 10662306a36Sopenharmony_ci return -EINVAL; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci hm = kmalloc(sizeof(*hm), GFP_KERNEL); 10962306a36Sopenharmony_ci hr = kzalloc(sizeof(*hr), GFP_KERNEL); 11062306a36Sopenharmony_ci if (!hm || !hr) { 11162306a36Sopenharmony_ci err = -ENOMEM; 11262306a36Sopenharmony_ci goto out; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci phpi_ioctl_data = (struct hpi_ioctl_linux __user *)arg; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* Read the message and response pointers from user space. */ 11862306a36Sopenharmony_ci if (get_user(puhm, &phpi_ioctl_data->phm) 11962306a36Sopenharmony_ci || get_user(puhr, &phpi_ioctl_data->phr)) { 12062306a36Sopenharmony_ci err = -EFAULT; 12162306a36Sopenharmony_ci goto out; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Now read the message size and data from user space. */ 12562306a36Sopenharmony_ci if (get_user(msg_size, (u16 __user *)puhm)) { 12662306a36Sopenharmony_ci err = -EFAULT; 12762306a36Sopenharmony_ci goto out; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci if (msg_size > sizeof(*hm)) 13062306a36Sopenharmony_ci msg_size = sizeof(*hm); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* printk(KERN_INFO "message size %d\n", hm->h.wSize); */ 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci uncopied_bytes = copy_from_user(hm, puhm, msg_size); 13562306a36Sopenharmony_ci if (uncopied_bytes) { 13662306a36Sopenharmony_ci HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes); 13762306a36Sopenharmony_ci err = -EFAULT; 13862306a36Sopenharmony_ci goto out; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* Override h.size in case it is changed between two userspace fetches */ 14262306a36Sopenharmony_ci hm->h.size = msg_size; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (get_user(res_max_size, (u16 __user *)puhr)) { 14562306a36Sopenharmony_ci err = -EFAULT; 14662306a36Sopenharmony_ci goto out; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci /* printk(KERN_INFO "user response size %d\n", res_max_size); */ 14962306a36Sopenharmony_ci if (res_max_size < sizeof(struct hpi_response_header)) { 15062306a36Sopenharmony_ci HPI_DEBUG_LOG(WARNING, "small res size %d\n", res_max_size); 15162306a36Sopenharmony_ci err = -EFAULT; 15262306a36Sopenharmony_ci goto out; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci res_max_size = min_t(size_t, res_max_size, sizeof(*hr)); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci switch (hm->h.function) { 15862306a36Sopenharmony_ci case HPI_SUBSYS_CREATE_ADAPTER: 15962306a36Sopenharmony_ci case HPI_ADAPTER_DELETE: 16062306a36Sopenharmony_ci /* Application must not use these functions! */ 16162306a36Sopenharmony_ci hr->h.size = sizeof(hr->h); 16262306a36Sopenharmony_ci hr->h.error = HPI_ERROR_INVALID_OPERATION; 16362306a36Sopenharmony_ci hr->h.function = hm->h.function; 16462306a36Sopenharmony_ci uncopied_bytes = copy_to_user(puhr, hr, hr->h.size); 16562306a36Sopenharmony_ci if (uncopied_bytes) 16662306a36Sopenharmony_ci err = -EFAULT; 16762306a36Sopenharmony_ci else 16862306a36Sopenharmony_ci err = 0; 16962306a36Sopenharmony_ci goto out; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci hr->h.size = res_max_size; 17362306a36Sopenharmony_ci if (hm->h.object == HPI_OBJ_SUBSYSTEM) { 17462306a36Sopenharmony_ci hpi_send_recv_f(&hm->m0, &hr->r0, file); 17562306a36Sopenharmony_ci } else { 17662306a36Sopenharmony_ci u16 __user *ptr = NULL; 17762306a36Sopenharmony_ci u32 size = 0; 17862306a36Sopenharmony_ci /* -1=no data 0=read from user mem, 1=write to user mem */ 17962306a36Sopenharmony_ci int wrflag = -1; 18062306a36Sopenharmony_ci struct hpi_adapter *pa = NULL; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (hm->h.adapter_index < ARRAY_SIZE(adapters)) 18362306a36Sopenharmony_ci pa = &adapters[array_index_nospec(hm->h.adapter_index, 18462306a36Sopenharmony_ci ARRAY_SIZE(adapters))]; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (!pa || !pa->adapter || !pa->adapter->type) { 18762306a36Sopenharmony_ci hpi_init_response(&hr->r0, hm->h.object, 18862306a36Sopenharmony_ci hm->h.function, HPI_ERROR_BAD_ADAPTER_NUMBER); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci uncopied_bytes = 19162306a36Sopenharmony_ci copy_to_user(puhr, hr, sizeof(hr->h)); 19262306a36Sopenharmony_ci if (uncopied_bytes) 19362306a36Sopenharmony_ci err = -EFAULT; 19462306a36Sopenharmony_ci else 19562306a36Sopenharmony_ci err = 0; 19662306a36Sopenharmony_ci goto out; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (mutex_lock_interruptible(&pa->mutex)) { 20062306a36Sopenharmony_ci err = -EINTR; 20162306a36Sopenharmony_ci goto out; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Dig out any pointers embedded in the message. */ 20562306a36Sopenharmony_ci switch (hm->h.function) { 20662306a36Sopenharmony_ci case HPI_OSTREAM_WRITE: 20762306a36Sopenharmony_ci case HPI_ISTREAM_READ:{ 20862306a36Sopenharmony_ci /* Yes, sparse, this is correct. */ 20962306a36Sopenharmony_ci ptr = (u16 __user *)hm->m0.u.d.u.data.pb_data; 21062306a36Sopenharmony_ci size = hm->m0.u.d.u.data.data_size; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Allocate buffer according to application request. 21362306a36Sopenharmony_ci ?Is it better to alloc/free for the duration 21462306a36Sopenharmony_ci of the transaction? 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci if (pa->buffer_size < size) { 21762306a36Sopenharmony_ci HPI_DEBUG_LOG(DEBUG, 21862306a36Sopenharmony_ci "Realloc adapter %d stream " 21962306a36Sopenharmony_ci "buffer from %zd to %d\n", 22062306a36Sopenharmony_ci hm->h.adapter_index, 22162306a36Sopenharmony_ci pa->buffer_size, size); 22262306a36Sopenharmony_ci if (pa->p_buffer) { 22362306a36Sopenharmony_ci pa->buffer_size = 0; 22462306a36Sopenharmony_ci vfree(pa->p_buffer); 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci pa->p_buffer = vmalloc(size); 22762306a36Sopenharmony_ci if (pa->p_buffer) 22862306a36Sopenharmony_ci pa->buffer_size = size; 22962306a36Sopenharmony_ci else { 23062306a36Sopenharmony_ci HPI_DEBUG_LOG(ERROR, 23162306a36Sopenharmony_ci "HPI could not allocate " 23262306a36Sopenharmony_ci "stream buffer size %d\n", 23362306a36Sopenharmony_ci size); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci mutex_unlock(&pa->mutex); 23662306a36Sopenharmony_ci err = -EINVAL; 23762306a36Sopenharmony_ci goto out; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci hm->m0.u.d.u.data.pb_data = pa->p_buffer; 24262306a36Sopenharmony_ci if (hm->h.function == HPI_ISTREAM_READ) 24362306a36Sopenharmony_ci /* from card, WRITE to user mem */ 24462306a36Sopenharmony_ci wrflag = 1; 24562306a36Sopenharmony_ci else 24662306a36Sopenharmony_ci wrflag = 0; 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci default: 25162306a36Sopenharmony_ci size = 0; 25262306a36Sopenharmony_ci break; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (size && (wrflag == 0)) { 25662306a36Sopenharmony_ci uncopied_bytes = 25762306a36Sopenharmony_ci copy_from_user(pa->p_buffer, ptr, size); 25862306a36Sopenharmony_ci if (uncopied_bytes) 25962306a36Sopenharmony_ci HPI_DEBUG_LOG(WARNING, 26062306a36Sopenharmony_ci "Missed %d of %d " 26162306a36Sopenharmony_ci "bytes from user\n", uncopied_bytes, 26262306a36Sopenharmony_ci size); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci hpi_send_recv_f(&hm->m0, &hr->r0, file); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (size && (wrflag == 1)) { 26862306a36Sopenharmony_ci uncopied_bytes = 26962306a36Sopenharmony_ci copy_to_user(ptr, pa->p_buffer, size); 27062306a36Sopenharmony_ci if (uncopied_bytes) 27162306a36Sopenharmony_ci HPI_DEBUG_LOG(WARNING, 27262306a36Sopenharmony_ci "Missed %d of %d " "bytes to user\n", 27362306a36Sopenharmony_ci uncopied_bytes, size); 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci mutex_unlock(&pa->mutex); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* on return response size must be set */ 28062306a36Sopenharmony_ci /*printk(KERN_INFO "response size %d\n", hr->h.wSize); */ 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (!hr->h.size) { 28362306a36Sopenharmony_ci HPI_DEBUG_LOG(ERROR, "response zero size\n"); 28462306a36Sopenharmony_ci err = -EFAULT; 28562306a36Sopenharmony_ci goto out; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (hr->h.size > res_max_size) { 28962306a36Sopenharmony_ci HPI_DEBUG_LOG(ERROR, "response too big %d %d\n", hr->h.size, 29062306a36Sopenharmony_ci res_max_size); 29162306a36Sopenharmony_ci hr->h.error = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL; 29262306a36Sopenharmony_ci hr->h.specific_error = hr->h.size; 29362306a36Sopenharmony_ci hr->h.size = sizeof(hr->h); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci uncopied_bytes = copy_to_user(puhr, hr, hr->h.size); 29762306a36Sopenharmony_ci if (uncopied_bytes) { 29862306a36Sopenharmony_ci HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes); 29962306a36Sopenharmony_ci err = -EFAULT; 30062306a36Sopenharmony_ci goto out; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ciout: 30462306a36Sopenharmony_ci kfree(hm); 30562306a36Sopenharmony_ci kfree(hr); 30662306a36Sopenharmony_ci return err; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int asihpi_irq_count; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic irqreturn_t asihpi_isr(int irq, void *dev_id) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct hpi_adapter *a = dev_id; 31462306a36Sopenharmony_ci int handled; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (!a->adapter->irq_query_and_clear) { 31762306a36Sopenharmony_ci pr_err("asihpi_isr ASI%04X:%d no handler\n", a->adapter->type, 31862306a36Sopenharmony_ci a->adapter->index); 31962306a36Sopenharmony_ci return IRQ_NONE; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci handled = a->adapter->irq_query_and_clear(a->adapter, 0); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (!handled) 32562306a36Sopenharmony_ci return IRQ_NONE; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci asihpi_irq_count++; 32862306a36Sopenharmony_ci /* printk(KERN_INFO "asihpi_isr %d ASI%04X:%d irq handled\n", 32962306a36Sopenharmony_ci asihpi_irq_count, a->adapter->type, a->adapter->index); */ 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (a->interrupt_callback) 33262306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return IRQ_HANDLED; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic irqreturn_t asihpi_isr_thread(int irq, void *dev_id) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct hpi_adapter *a = dev_id; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (a->interrupt_callback) 34262306a36Sopenharmony_ci a->interrupt_callback(a); 34362306a36Sopenharmony_ci return IRQ_HANDLED; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ciint asihpi_adapter_probe(struct pci_dev *pci_dev, 34762306a36Sopenharmony_ci const struct pci_device_id *pci_id) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci int idx, nm, low_latency_mode = 0, irq_supported = 0; 35062306a36Sopenharmony_ci int adapter_index; 35162306a36Sopenharmony_ci unsigned int memlen; 35262306a36Sopenharmony_ci struct hpi_message hm; 35362306a36Sopenharmony_ci struct hpi_response hr; 35462306a36Sopenharmony_ci struct hpi_adapter adapter; 35562306a36Sopenharmony_ci struct hpi_pci pci = { 0 }; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci memset(&adapter, 0, sizeof(adapter)); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &pci_dev->dev, 36062306a36Sopenharmony_ci "probe %04x:%04x,%04x:%04x,%04x\n", pci_dev->vendor, 36162306a36Sopenharmony_ci pci_dev->device, pci_dev->subsystem_vendor, 36262306a36Sopenharmony_ci pci_dev->subsystem_device, pci_dev->devfn); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (pcim_enable_device(pci_dev) < 0) { 36562306a36Sopenharmony_ci dev_err(&pci_dev->dev, 36662306a36Sopenharmony_ci "pci_enable_device failed, disabling device\n"); 36762306a36Sopenharmony_ci return -EIO; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci pci_set_master(pci_dev); /* also sets latency timer if < 16 */ 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, 37362306a36Sopenharmony_ci HPI_SUBSYS_CREATE_ADAPTER); 37462306a36Sopenharmony_ci hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CREATE_ADAPTER, 37562306a36Sopenharmony_ci HPI_ERROR_PROCESSING_MESSAGE); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci hm.adapter_index = HPI_ADAPTER_INDEX_INVALID; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci nm = HPI_MAX_ADAPTER_MEM_SPACES; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci for (idx = 0; idx < nm; idx++) { 38262306a36Sopenharmony_ci HPI_DEBUG_LOG(INFO, "resource %d %pR\n", idx, 38362306a36Sopenharmony_ci &pci_dev->resource[idx]); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) { 38662306a36Sopenharmony_ci memlen = pci_resource_len(pci_dev, idx); 38762306a36Sopenharmony_ci pci.ap_mem_base[idx] = 38862306a36Sopenharmony_ci ioremap(pci_resource_start(pci_dev, idx), 38962306a36Sopenharmony_ci memlen); 39062306a36Sopenharmony_ci if (!pci.ap_mem_base[idx]) { 39162306a36Sopenharmony_ci HPI_DEBUG_LOG(ERROR, 39262306a36Sopenharmony_ci "ioremap failed, aborting\n"); 39362306a36Sopenharmony_ci /* unmap previously mapped pci mem space */ 39462306a36Sopenharmony_ci goto err; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci pci.pci_dev = pci_dev; 40062306a36Sopenharmony_ci hm.u.s.resource.bus_type = HPI_BUS_PCI; 40162306a36Sopenharmony_ci hm.u.s.resource.r.pci = &pci; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* call CreateAdapterObject on the relevant hpi module */ 40462306a36Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 40562306a36Sopenharmony_ci if (hr.error) 40662306a36Sopenharmony_ci goto err; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci adapter_index = hr.u.s.adapter_index; 40962306a36Sopenharmony_ci adapter.adapter = hpi_find_adapter(adapter_index); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (prealloc_stream_buf) { 41262306a36Sopenharmony_ci adapter.p_buffer = vmalloc(prealloc_stream_buf); 41362306a36Sopenharmony_ci if (!adapter.p_buffer) { 41462306a36Sopenharmony_ci HPI_DEBUG_LOG(ERROR, 41562306a36Sopenharmony_ci "HPI could not allocate " 41662306a36Sopenharmony_ci "kernel buffer size %d\n", 41762306a36Sopenharmony_ci prealloc_stream_buf); 41862306a36Sopenharmony_ci goto err; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, 42362306a36Sopenharmony_ci HPI_ADAPTER_OPEN); 42462306a36Sopenharmony_ci hm.adapter_index = adapter.adapter->index; 42562306a36Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (hr.error) { 42862306a36Sopenharmony_ci HPI_DEBUG_LOG(ERROR, "HPI_ADAPTER_OPEN failed, aborting\n"); 42962306a36Sopenharmony_ci goto err; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* Check if current mode == Low Latency mode */ 43362306a36Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, 43462306a36Sopenharmony_ci HPI_ADAPTER_GET_MODE); 43562306a36Sopenharmony_ci hm.adapter_index = adapter.adapter->index; 43662306a36Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (!hr.error 43962306a36Sopenharmony_ci && hr.u.ax.mode.adapter_mode == HPI_ADAPTER_MODE_LOW_LATENCY) 44062306a36Sopenharmony_ci low_latency_mode = 1; 44162306a36Sopenharmony_ci else 44262306a36Sopenharmony_ci dev_info(&pci_dev->dev, 44362306a36Sopenharmony_ci "Adapter at index %d is not in low latency mode\n", 44462306a36Sopenharmony_ci adapter.adapter->index); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* Check if IRQs are supported */ 44762306a36Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, 44862306a36Sopenharmony_ci HPI_ADAPTER_GET_PROPERTY); 44962306a36Sopenharmony_ci hm.adapter_index = adapter.adapter->index; 45062306a36Sopenharmony_ci hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_SUPPORTS_IRQ; 45162306a36Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 45262306a36Sopenharmony_ci if (hr.error || !hr.u.ax.property_get.parameter1) { 45362306a36Sopenharmony_ci dev_info(&pci_dev->dev, 45462306a36Sopenharmony_ci "IRQs not supported by adapter at index %d\n", 45562306a36Sopenharmony_ci adapter.adapter->index); 45662306a36Sopenharmony_ci } else { 45762306a36Sopenharmony_ci irq_supported = 1; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* WARNING can't init mutex in 'adapter' 46162306a36Sopenharmony_ci * and then copy it to adapters[] ?!?! 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ci adapters[adapter_index] = adapter; 46462306a36Sopenharmony_ci mutex_init(&adapters[adapter_index].mutex); 46562306a36Sopenharmony_ci pci_set_drvdata(pci_dev, &adapters[adapter_index]); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (low_latency_mode && irq_supported) { 46862306a36Sopenharmony_ci if (!adapter.adapter->irq_query_and_clear) { 46962306a36Sopenharmony_ci dev_err(&pci_dev->dev, 47062306a36Sopenharmony_ci "no IRQ handler for adapter %d, aborting\n", 47162306a36Sopenharmony_ci adapter.adapter->index); 47262306a36Sopenharmony_ci goto err; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* Disable IRQ generation on DSP side by setting the rate to 0 */ 47662306a36Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, 47762306a36Sopenharmony_ci HPI_ADAPTER_SET_PROPERTY); 47862306a36Sopenharmony_ci hm.adapter_index = adapter.adapter->index; 47962306a36Sopenharmony_ci hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE; 48062306a36Sopenharmony_ci hm.u.ax.property_set.parameter1 = 0; 48162306a36Sopenharmony_ci hm.u.ax.property_set.parameter2 = 0; 48262306a36Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 48362306a36Sopenharmony_ci if (hr.error) { 48462306a36Sopenharmony_ci HPI_DEBUG_LOG(ERROR, 48562306a36Sopenharmony_ci "HPI_ADAPTER_GET_MODE failed, aborting\n"); 48662306a36Sopenharmony_ci goto err; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* Note: request_irq calls asihpi_isr here */ 49062306a36Sopenharmony_ci if (request_threaded_irq(pci_dev->irq, asihpi_isr, 49162306a36Sopenharmony_ci asihpi_isr_thread, IRQF_SHARED, 49262306a36Sopenharmony_ci "asihpi", &adapters[adapter_index])) { 49362306a36Sopenharmony_ci dev_err(&pci_dev->dev, "request_irq(%d) failed\n", 49462306a36Sopenharmony_ci pci_dev->irq); 49562306a36Sopenharmony_ci goto err; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci adapters[adapter_index].interrupt_mode = 1; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci dev_info(&pci_dev->dev, "using irq %d\n", pci_dev->irq); 50162306a36Sopenharmony_ci adapters[adapter_index].irq = pci_dev->irq; 50262306a36Sopenharmony_ci } else { 50362306a36Sopenharmony_ci dev_info(&pci_dev->dev, "using polled mode\n"); 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci dev_info(&pci_dev->dev, "probe succeeded for ASI%04X HPI index %d\n", 50762306a36Sopenharmony_ci adapter.adapter->type, adapter_index); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return 0; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cierr: 51262306a36Sopenharmony_ci while (--idx >= 0) { 51362306a36Sopenharmony_ci if (pci.ap_mem_base[idx]) { 51462306a36Sopenharmony_ci iounmap(pci.ap_mem_base[idx]); 51562306a36Sopenharmony_ci pci.ap_mem_base[idx] = NULL; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (adapter.p_buffer) { 52062306a36Sopenharmony_ci adapter.buffer_size = 0; 52162306a36Sopenharmony_ci vfree(adapter.p_buffer); 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci HPI_DEBUG_LOG(ERROR, "adapter_probe failed\n"); 52562306a36Sopenharmony_ci return -ENODEV; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_civoid asihpi_adapter_remove(struct pci_dev *pci_dev) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci int idx; 53162306a36Sopenharmony_ci struct hpi_message hm; 53262306a36Sopenharmony_ci struct hpi_response hr; 53362306a36Sopenharmony_ci struct hpi_adapter *pa; 53462306a36Sopenharmony_ci struct hpi_pci pci; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci pa = pci_get_drvdata(pci_dev); 53762306a36Sopenharmony_ci pci = pa->adapter->pci; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* Disable IRQ generation on DSP side */ 54062306a36Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, 54162306a36Sopenharmony_ci HPI_ADAPTER_SET_PROPERTY); 54262306a36Sopenharmony_ci hm.adapter_index = pa->adapter->index; 54362306a36Sopenharmony_ci hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE; 54462306a36Sopenharmony_ci hm.u.ax.property_set.parameter1 = 0; 54562306a36Sopenharmony_ci hm.u.ax.property_set.parameter2 = 0; 54662306a36Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, 54962306a36Sopenharmony_ci HPI_ADAPTER_DELETE); 55062306a36Sopenharmony_ci hm.adapter_index = pa->adapter->index; 55162306a36Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* unmap PCI memory space, mapped during device init. */ 55462306a36Sopenharmony_ci for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; ++idx) 55562306a36Sopenharmony_ci iounmap(pci.ap_mem_base[idx]); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (pa->irq) 55862306a36Sopenharmony_ci free_irq(pa->irq, pa); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci vfree(pa->p_buffer); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (1) 56362306a36Sopenharmony_ci dev_info(&pci_dev->dev, 56462306a36Sopenharmony_ci "remove %04x:%04x,%04x:%04x,%04x, HPI index %d\n", 56562306a36Sopenharmony_ci pci_dev->vendor, pci_dev->device, 56662306a36Sopenharmony_ci pci_dev->subsystem_vendor, pci_dev->subsystem_device, 56762306a36Sopenharmony_ci pci_dev->devfn, pa->adapter->index); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci memset(pa, 0, sizeof(*pa)); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_civoid __init asihpi_init(void) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct hpi_message hm; 57562306a36Sopenharmony_ci struct hpi_response hr; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci memset(adapters, 0, sizeof(adapters)); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci printk(KERN_INFO "ASIHPI driver " HPI_VER_STRING "\n"); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, 58262306a36Sopenharmony_ci HPI_SUBSYS_DRIVER_LOAD); 58362306a36Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_civoid asihpi_exit(void) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci struct hpi_message hm; 58962306a36Sopenharmony_ci struct hpi_response hr; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, 59262306a36Sopenharmony_ci HPI_SUBSYS_DRIVER_UNLOAD); 59362306a36Sopenharmony_ci hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); 59462306a36Sopenharmony_ci} 595