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