162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
462306a36Sopenharmony_ci/* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/delay.h>
762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
862306a36Sopenharmony_ci#include <linux/idr.h>
962306a36Sopenharmony_ci#include <linux/interrupt.h>
1062306a36Sopenharmony_ci#include <linux/list.h>
1162306a36Sopenharmony_ci#include <linux/kref.h>
1262306a36Sopenharmony_ci#include <linux/mhi.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/msi.h>
1562306a36Sopenharmony_ci#include <linux/mutex.h>
1662306a36Sopenharmony_ci#include <linux/pci.h>
1762306a36Sopenharmony_ci#include <linux/spinlock.h>
1862306a36Sopenharmony_ci#include <linux/workqueue.h>
1962306a36Sopenharmony_ci#include <linux/wait.h>
2062306a36Sopenharmony_ci#include <drm/drm_accel.h>
2162306a36Sopenharmony_ci#include <drm/drm_drv.h>
2262306a36Sopenharmony_ci#include <drm/drm_file.h>
2362306a36Sopenharmony_ci#include <drm/drm_gem.h>
2462306a36Sopenharmony_ci#include <drm/drm_ioctl.h>
2562306a36Sopenharmony_ci#include <uapi/drm/qaic_accel.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "mhi_controller.h"
2862306a36Sopenharmony_ci#include "qaic.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ciMODULE_IMPORT_NS(DMA_BUF);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define PCI_DEV_AIC100			0xa100
3362306a36Sopenharmony_ci#define QAIC_NAME			"qaic"
3462306a36Sopenharmony_ci#define QAIC_DESC			"Qualcomm Cloud AI Accelerators"
3562306a36Sopenharmony_ci#define CNTL_MAJOR			5
3662306a36Sopenharmony_ci#define CNTL_MINOR			0
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cibool datapath_polling;
3962306a36Sopenharmony_cimodule_param(datapath_polling, bool, 0400);
4062306a36Sopenharmony_ciMODULE_PARM_DESC(datapath_polling, "Operate the datapath in polling mode");
4162306a36Sopenharmony_cistatic bool link_up;
4262306a36Sopenharmony_cistatic DEFINE_IDA(qaic_usrs);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int qaic_create_drm_device(struct qaic_device *qdev, s32 partition_id);
4562306a36Sopenharmony_cistatic void qaic_destroy_drm_device(struct qaic_device *qdev, s32 partition_id);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic void free_usr(struct kref *kref)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct qaic_user *usr = container_of(kref, struct qaic_user, ref_count);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	cleanup_srcu_struct(&usr->qddev_lock);
5262306a36Sopenharmony_ci	ida_free(&qaic_usrs, usr->handle);
5362306a36Sopenharmony_ci	kfree(usr);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int qaic_open(struct drm_device *dev, struct drm_file *file)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct qaic_drm_device *qddev = dev->dev_private;
5962306a36Sopenharmony_ci	struct qaic_device *qdev = qddev->qdev;
6062306a36Sopenharmony_ci	struct qaic_user *usr;
6162306a36Sopenharmony_ci	int rcu_id;
6262306a36Sopenharmony_ci	int ret;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	rcu_id = srcu_read_lock(&qdev->dev_lock);
6562306a36Sopenharmony_ci	if (qdev->in_reset) {
6662306a36Sopenharmony_ci		ret = -ENODEV;
6762306a36Sopenharmony_ci		goto dev_unlock;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	usr = kmalloc(sizeof(*usr), GFP_KERNEL);
7162306a36Sopenharmony_ci	if (!usr) {
7262306a36Sopenharmony_ci		ret = -ENOMEM;
7362306a36Sopenharmony_ci		goto dev_unlock;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	usr->handle = ida_alloc(&qaic_usrs, GFP_KERNEL);
7762306a36Sopenharmony_ci	if (usr->handle < 0) {
7862306a36Sopenharmony_ci		ret = usr->handle;
7962306a36Sopenharmony_ci		goto free_usr;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci	usr->qddev = qddev;
8262306a36Sopenharmony_ci	atomic_set(&usr->chunk_id, 0);
8362306a36Sopenharmony_ci	init_srcu_struct(&usr->qddev_lock);
8462306a36Sopenharmony_ci	kref_init(&usr->ref_count);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&qddev->users_mutex);
8762306a36Sopenharmony_ci	if (ret)
8862306a36Sopenharmony_ci		goto cleanup_usr;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	list_add(&usr->node, &qddev->users);
9162306a36Sopenharmony_ci	mutex_unlock(&qddev->users_mutex);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	file->driver_priv = usr;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	srcu_read_unlock(&qdev->dev_lock, rcu_id);
9662306a36Sopenharmony_ci	return 0;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cicleanup_usr:
9962306a36Sopenharmony_ci	cleanup_srcu_struct(&usr->qddev_lock);
10062306a36Sopenharmony_ci	ida_free(&qaic_usrs, usr->handle);
10162306a36Sopenharmony_cifree_usr:
10262306a36Sopenharmony_ci	kfree(usr);
10362306a36Sopenharmony_cidev_unlock:
10462306a36Sopenharmony_ci	srcu_read_unlock(&qdev->dev_lock, rcu_id);
10562306a36Sopenharmony_ci	return ret;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic void qaic_postclose(struct drm_device *dev, struct drm_file *file)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct qaic_user *usr = file->driver_priv;
11162306a36Sopenharmony_ci	struct qaic_drm_device *qddev;
11262306a36Sopenharmony_ci	struct qaic_device *qdev;
11362306a36Sopenharmony_ci	int qdev_rcu_id;
11462306a36Sopenharmony_ci	int usr_rcu_id;
11562306a36Sopenharmony_ci	int i;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	qddev = usr->qddev;
11862306a36Sopenharmony_ci	usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
11962306a36Sopenharmony_ci	if (qddev) {
12062306a36Sopenharmony_ci		qdev = qddev->qdev;
12162306a36Sopenharmony_ci		qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
12262306a36Sopenharmony_ci		if (!qdev->in_reset) {
12362306a36Sopenharmony_ci			qaic_release_usr(qdev, usr);
12462306a36Sopenharmony_ci			for (i = 0; i < qdev->num_dbc; ++i)
12562306a36Sopenharmony_ci				if (qdev->dbc[i].usr && qdev->dbc[i].usr->handle == usr->handle)
12662306a36Sopenharmony_ci					release_dbc(qdev, i);
12762306a36Sopenharmony_ci		}
12862306a36Sopenharmony_ci		srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		mutex_lock(&qddev->users_mutex);
13162306a36Sopenharmony_ci		if (!list_empty(&usr->node))
13262306a36Sopenharmony_ci			list_del_init(&usr->node);
13362306a36Sopenharmony_ci		mutex_unlock(&qddev->users_mutex);
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
13762306a36Sopenharmony_ci	kref_put(&usr->ref_count, free_usr);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	file->driver_priv = NULL;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ciDEFINE_DRM_ACCEL_FOPS(qaic_accel_fops);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic const struct drm_ioctl_desc qaic_drm_ioctls[] = {
14562306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(QAIC_MANAGE, qaic_manage_ioctl, 0),
14662306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(QAIC_CREATE_BO, qaic_create_bo_ioctl, 0),
14762306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(QAIC_MMAP_BO, qaic_mmap_bo_ioctl, 0),
14862306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(QAIC_ATTACH_SLICE_BO, qaic_attach_slice_bo_ioctl, 0),
14962306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(QAIC_EXECUTE_BO, qaic_execute_bo_ioctl, 0),
15062306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(QAIC_PARTIAL_EXECUTE_BO, qaic_partial_execute_bo_ioctl, 0),
15162306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(QAIC_WAIT_BO, qaic_wait_bo_ioctl, 0),
15262306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(QAIC_PERF_STATS_BO, qaic_perf_stats_bo_ioctl, 0),
15362306a36Sopenharmony_ci};
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic const struct drm_driver qaic_accel_driver = {
15662306a36Sopenharmony_ci	.driver_features	= DRIVER_GEM | DRIVER_COMPUTE_ACCEL,
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	.name			= QAIC_NAME,
15962306a36Sopenharmony_ci	.desc			= QAIC_DESC,
16062306a36Sopenharmony_ci	.date			= "20190618",
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	.fops			= &qaic_accel_fops,
16362306a36Sopenharmony_ci	.open			= qaic_open,
16462306a36Sopenharmony_ci	.postclose		= qaic_postclose,
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	.ioctls			= qaic_drm_ioctls,
16762306a36Sopenharmony_ci	.num_ioctls		= ARRAY_SIZE(qaic_drm_ioctls),
16862306a36Sopenharmony_ci	.gem_prime_import	= qaic_gem_prime_import,
16962306a36Sopenharmony_ci};
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int qaic_create_drm_device(struct qaic_device *qdev, s32 partition_id)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct qaic_drm_device *qddev;
17462306a36Sopenharmony_ci	struct drm_device *ddev;
17562306a36Sopenharmony_ci	struct device *pdev;
17662306a36Sopenharmony_ci	int ret;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* Hold off implementing partitions until the uapi is determined */
17962306a36Sopenharmony_ci	if (partition_id != QAIC_NO_PARTITION)
18062306a36Sopenharmony_ci		return -EINVAL;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	pdev = &qdev->pdev->dev;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	qddev = kzalloc(sizeof(*qddev), GFP_KERNEL);
18562306a36Sopenharmony_ci	if (!qddev)
18662306a36Sopenharmony_ci		return -ENOMEM;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	ddev = drm_dev_alloc(&qaic_accel_driver, pdev);
18962306a36Sopenharmony_ci	if (IS_ERR(ddev)) {
19062306a36Sopenharmony_ci		ret = PTR_ERR(ddev);
19162306a36Sopenharmony_ci		goto ddev_fail;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	ddev->dev_private = qddev;
19562306a36Sopenharmony_ci	qddev->ddev = ddev;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	qddev->qdev = qdev;
19862306a36Sopenharmony_ci	qddev->partition_id = partition_id;
19962306a36Sopenharmony_ci	INIT_LIST_HEAD(&qddev->users);
20062306a36Sopenharmony_ci	mutex_init(&qddev->users_mutex);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	qdev->qddev = qddev;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	ret = drm_dev_register(ddev, 0);
20562306a36Sopenharmony_ci	if (ret) {
20662306a36Sopenharmony_ci		pci_dbg(qdev->pdev, "%s: drm_dev_register failed %d\n", __func__, ret);
20762306a36Sopenharmony_ci		goto drm_reg_fail;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return 0;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cidrm_reg_fail:
21362306a36Sopenharmony_ci	mutex_destroy(&qddev->users_mutex);
21462306a36Sopenharmony_ci	qdev->qddev = NULL;
21562306a36Sopenharmony_ci	drm_dev_put(ddev);
21662306a36Sopenharmony_ciddev_fail:
21762306a36Sopenharmony_ci	kfree(qddev);
21862306a36Sopenharmony_ci	return ret;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic void qaic_destroy_drm_device(struct qaic_device *qdev, s32 partition_id)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct qaic_drm_device *qddev;
22462306a36Sopenharmony_ci	struct qaic_user *usr;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	qddev = qdev->qddev;
22762306a36Sopenharmony_ci	qdev->qddev = NULL;
22862306a36Sopenharmony_ci	if (!qddev)
22962306a36Sopenharmony_ci		return;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/*
23262306a36Sopenharmony_ci	 * Existing users get unresolvable errors till they close FDs.
23362306a36Sopenharmony_ci	 * Need to sync carefully with users calling close(). The
23462306a36Sopenharmony_ci	 * list of users can be modified elsewhere when the lock isn't
23562306a36Sopenharmony_ci	 * held here, but the sync'ing the srcu with the mutex held
23662306a36Sopenharmony_ci	 * could deadlock. Grab the mutex so that the list will be
23762306a36Sopenharmony_ci	 * unmodified. The user we get will exist as long as the
23862306a36Sopenharmony_ci	 * lock is held. Signal that the qcdev is going away, and
23962306a36Sopenharmony_ci	 * grab a reference to the user so they don't go away for
24062306a36Sopenharmony_ci	 * synchronize_srcu(). Then release the mutex to avoid
24162306a36Sopenharmony_ci	 * deadlock and make sure the user has observed the signal.
24262306a36Sopenharmony_ci	 * With the lock released, we cannot maintain any state of the
24362306a36Sopenharmony_ci	 * user list.
24462306a36Sopenharmony_ci	 */
24562306a36Sopenharmony_ci	mutex_lock(&qddev->users_mutex);
24662306a36Sopenharmony_ci	while (!list_empty(&qddev->users)) {
24762306a36Sopenharmony_ci		usr = list_first_entry(&qddev->users, struct qaic_user, node);
24862306a36Sopenharmony_ci		list_del_init(&usr->node);
24962306a36Sopenharmony_ci		kref_get(&usr->ref_count);
25062306a36Sopenharmony_ci		usr->qddev = NULL;
25162306a36Sopenharmony_ci		mutex_unlock(&qddev->users_mutex);
25262306a36Sopenharmony_ci		synchronize_srcu(&usr->qddev_lock);
25362306a36Sopenharmony_ci		kref_put(&usr->ref_count, free_usr);
25462306a36Sopenharmony_ci		mutex_lock(&qddev->users_mutex);
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci	mutex_unlock(&qddev->users_mutex);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (qddev->ddev) {
25962306a36Sopenharmony_ci		drm_dev_unregister(qddev->ddev);
26062306a36Sopenharmony_ci		drm_dev_put(qddev->ddev);
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	kfree(qddev);
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int qaic_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	u16 major = -1, minor = -1;
26962306a36Sopenharmony_ci	struct qaic_device *qdev;
27062306a36Sopenharmony_ci	int ret;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/*
27362306a36Sopenharmony_ci	 * Invoking this function indicates that the control channel to the
27462306a36Sopenharmony_ci	 * device is available. We use that as a signal to indicate that
27562306a36Sopenharmony_ci	 * the device side firmware has booted. The device side firmware
27662306a36Sopenharmony_ci	 * manages the device resources, so we need to communicate with it
27762306a36Sopenharmony_ci	 * via the control channel in order to utilize the device. Therefore
27862306a36Sopenharmony_ci	 * we wait until this signal to create the drm dev that userspace will
27962306a36Sopenharmony_ci	 * use to control the device, because without the device side firmware,
28062306a36Sopenharmony_ci	 * userspace can't do anything useful.
28162306a36Sopenharmony_ci	 */
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	qdev->in_reset = false;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	dev_set_drvdata(&mhi_dev->dev, qdev);
28862306a36Sopenharmony_ci	qdev->cntl_ch = mhi_dev;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	ret = qaic_control_open(qdev);
29162306a36Sopenharmony_ci	if (ret) {
29262306a36Sopenharmony_ci		pci_dbg(qdev->pdev, "%s: control_open failed %d\n", __func__, ret);
29362306a36Sopenharmony_ci		return ret;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	ret = get_cntl_version(qdev, NULL, &major, &minor);
29762306a36Sopenharmony_ci	if (ret || major != CNTL_MAJOR || minor > CNTL_MINOR) {
29862306a36Sopenharmony_ci		pci_err(qdev->pdev, "%s: Control protocol version (%d.%d) not supported. Supported version is (%d.%d). Ret: %d\n",
29962306a36Sopenharmony_ci			__func__, major, minor, CNTL_MAJOR, CNTL_MINOR, ret);
30062306a36Sopenharmony_ci		ret = -EINVAL;
30162306a36Sopenharmony_ci		goto close_control;
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	ret = qaic_create_drm_device(qdev, QAIC_NO_PARTITION);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	return ret;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ciclose_control:
30962306a36Sopenharmony_ci	qaic_control_close(qdev);
31062306a36Sopenharmony_ci	return ret;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic void qaic_mhi_remove(struct mhi_device *mhi_dev)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci/* This is redundant since we have already observed the device crash */
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic void qaic_notify_reset(struct qaic_device *qdev)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	int i;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	qdev->in_reset = true;
32362306a36Sopenharmony_ci	/* wake up any waiters to avoid waiting for timeouts at sync */
32462306a36Sopenharmony_ci	wake_all_cntl(qdev);
32562306a36Sopenharmony_ci	for (i = 0; i < qdev->num_dbc; ++i)
32662306a36Sopenharmony_ci		wakeup_dbc(qdev, i);
32762306a36Sopenharmony_ci	synchronize_srcu(&qdev->dev_lock);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_civoid qaic_dev_reset_clean_local_state(struct qaic_device *qdev, bool exit_reset)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	int i;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	qaic_notify_reset(qdev);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* remove drmdevs to prevent new users from coming in */
33762306a36Sopenharmony_ci	qaic_destroy_drm_device(qdev, QAIC_NO_PARTITION);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	/* start tearing things down */
34062306a36Sopenharmony_ci	for (i = 0; i < qdev->num_dbc; ++i)
34162306a36Sopenharmony_ci		release_dbc(qdev, i);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (exit_reset)
34462306a36Sopenharmony_ci		qdev->in_reset = false;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic struct qaic_device *create_qdev(struct pci_dev *pdev, const struct pci_device_id *id)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	struct qaic_device *qdev;
35062306a36Sopenharmony_ci	int i;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	qdev = devm_kzalloc(&pdev->dev, sizeof(*qdev), GFP_KERNEL);
35362306a36Sopenharmony_ci	if (!qdev)
35462306a36Sopenharmony_ci		return NULL;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (id->device == PCI_DEV_AIC100) {
35762306a36Sopenharmony_ci		qdev->num_dbc = 16;
35862306a36Sopenharmony_ci		qdev->dbc = devm_kcalloc(&pdev->dev, qdev->num_dbc, sizeof(*qdev->dbc), GFP_KERNEL);
35962306a36Sopenharmony_ci		if (!qdev->dbc)
36062306a36Sopenharmony_ci			return NULL;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	qdev->cntl_wq = alloc_workqueue("qaic_cntl", WQ_UNBOUND, 0);
36462306a36Sopenharmony_ci	if (!qdev->cntl_wq)
36562306a36Sopenharmony_ci		return NULL;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	pci_set_drvdata(pdev, qdev);
36862306a36Sopenharmony_ci	qdev->pdev = pdev;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	mutex_init(&qdev->cntl_mutex);
37162306a36Sopenharmony_ci	INIT_LIST_HEAD(&qdev->cntl_xfer_list);
37262306a36Sopenharmony_ci	init_srcu_struct(&qdev->dev_lock);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	for (i = 0; i < qdev->num_dbc; ++i) {
37562306a36Sopenharmony_ci		spin_lock_init(&qdev->dbc[i].xfer_lock);
37662306a36Sopenharmony_ci		qdev->dbc[i].qdev = qdev;
37762306a36Sopenharmony_ci		qdev->dbc[i].id = i;
37862306a36Sopenharmony_ci		INIT_LIST_HEAD(&qdev->dbc[i].xfer_list);
37962306a36Sopenharmony_ci		init_srcu_struct(&qdev->dbc[i].ch_lock);
38062306a36Sopenharmony_ci		init_waitqueue_head(&qdev->dbc[i].dbc_release);
38162306a36Sopenharmony_ci		INIT_LIST_HEAD(&qdev->dbc[i].bo_lists);
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	return qdev;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic void cleanup_qdev(struct qaic_device *qdev)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	int i;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	for (i = 0; i < qdev->num_dbc; ++i)
39262306a36Sopenharmony_ci		cleanup_srcu_struct(&qdev->dbc[i].ch_lock);
39362306a36Sopenharmony_ci	cleanup_srcu_struct(&qdev->dev_lock);
39462306a36Sopenharmony_ci	pci_set_drvdata(qdev->pdev, NULL);
39562306a36Sopenharmony_ci	destroy_workqueue(qdev->cntl_wq);
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic int init_pci(struct qaic_device *qdev, struct pci_dev *pdev)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	int bars;
40162306a36Sopenharmony_ci	int ret;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	bars = pci_select_bars(pdev, IORESOURCE_MEM);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/* make sure the device has the expected BARs */
40662306a36Sopenharmony_ci	if (bars != (BIT(0) | BIT(2) | BIT(4))) {
40762306a36Sopenharmony_ci		pci_dbg(pdev, "%s: expected BARs 0, 2, and 4 not found in device. Found 0x%x\n",
40862306a36Sopenharmony_ci			__func__, bars);
40962306a36Sopenharmony_ci		return -EINVAL;
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	ret = pcim_enable_device(pdev);
41362306a36Sopenharmony_ci	if (ret)
41462306a36Sopenharmony_ci		return ret;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
41762306a36Sopenharmony_ci	if (ret)
41862306a36Sopenharmony_ci		return ret;
41962306a36Sopenharmony_ci	ret = dma_set_max_seg_size(&pdev->dev, UINT_MAX);
42062306a36Sopenharmony_ci	if (ret)
42162306a36Sopenharmony_ci		return ret;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	qdev->bar_0 = devm_ioremap_resource(&pdev->dev, &pdev->resource[0]);
42462306a36Sopenharmony_ci	if (IS_ERR(qdev->bar_0))
42562306a36Sopenharmony_ci		return PTR_ERR(qdev->bar_0);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	qdev->bar_2 = devm_ioremap_resource(&pdev->dev, &pdev->resource[2]);
42862306a36Sopenharmony_ci	if (IS_ERR(qdev->bar_2))
42962306a36Sopenharmony_ci		return PTR_ERR(qdev->bar_2);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	/* Managed release since we use pcim_enable_device above */
43262306a36Sopenharmony_ci	pci_set_master(pdev);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return 0;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic int init_msi(struct qaic_device *qdev, struct pci_dev *pdev)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	int mhi_irq;
44062306a36Sopenharmony_ci	int ret;
44162306a36Sopenharmony_ci	int i;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	/* Managed release since we use pcim_enable_device */
44462306a36Sopenharmony_ci	ret = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI);
44562306a36Sopenharmony_ci	if (ret < 0)
44662306a36Sopenharmony_ci		return ret;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (ret < 32) {
44962306a36Sopenharmony_ci		pci_err(pdev, "%s: Requested 32 MSIs. Obtained %d MSIs which is less than the 32 required.\n",
45062306a36Sopenharmony_ci			__func__, ret);
45162306a36Sopenharmony_ci		return -ENODEV;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	mhi_irq = pci_irq_vector(pdev, 0);
45562306a36Sopenharmony_ci	if (mhi_irq < 0)
45662306a36Sopenharmony_ci		return mhi_irq;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	for (i = 0; i < qdev->num_dbc; ++i) {
45962306a36Sopenharmony_ci		ret = devm_request_threaded_irq(&pdev->dev, pci_irq_vector(pdev, i + 1),
46062306a36Sopenharmony_ci						dbc_irq_handler, dbc_irq_threaded_fn, IRQF_SHARED,
46162306a36Sopenharmony_ci						"qaic_dbc", &qdev->dbc[i]);
46262306a36Sopenharmony_ci		if (ret)
46362306a36Sopenharmony_ci			return ret;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		if (datapath_polling) {
46662306a36Sopenharmony_ci			qdev->dbc[i].irq = pci_irq_vector(pdev, i + 1);
46762306a36Sopenharmony_ci			disable_irq_nosync(qdev->dbc[i].irq);
46862306a36Sopenharmony_ci			INIT_WORK(&qdev->dbc[i].poll_work, irq_polling_work);
46962306a36Sopenharmony_ci		}
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	return mhi_irq;
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic int qaic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct qaic_device *qdev;
47862306a36Sopenharmony_ci	int mhi_irq;
47962306a36Sopenharmony_ci	int ret;
48062306a36Sopenharmony_ci	int i;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	qdev = create_qdev(pdev, id);
48362306a36Sopenharmony_ci	if (!qdev)
48462306a36Sopenharmony_ci		return -ENOMEM;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	ret = init_pci(qdev, pdev);
48762306a36Sopenharmony_ci	if (ret)
48862306a36Sopenharmony_ci		goto cleanup_qdev;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	for (i = 0; i < qdev->num_dbc; ++i)
49162306a36Sopenharmony_ci		qdev->dbc[i].dbc_base = qdev->bar_2 + QAIC_DBC_OFF(i);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	mhi_irq = init_msi(qdev, pdev);
49462306a36Sopenharmony_ci	if (mhi_irq < 0) {
49562306a36Sopenharmony_ci		ret = mhi_irq;
49662306a36Sopenharmony_ci		goto cleanup_qdev;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	qdev->mhi_cntrl = qaic_mhi_register_controller(pdev, qdev->bar_0, mhi_irq);
50062306a36Sopenharmony_ci	if (IS_ERR(qdev->mhi_cntrl)) {
50162306a36Sopenharmony_ci		ret = PTR_ERR(qdev->mhi_cntrl);
50262306a36Sopenharmony_ci		goto cleanup_qdev;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	return 0;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_cicleanup_qdev:
50862306a36Sopenharmony_ci	cleanup_qdev(qdev);
50962306a36Sopenharmony_ci	return ret;
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic void qaic_pci_remove(struct pci_dev *pdev)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct qaic_device *qdev = pci_get_drvdata(pdev);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (!qdev)
51762306a36Sopenharmony_ci		return;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	qaic_dev_reset_clean_local_state(qdev, false);
52062306a36Sopenharmony_ci	qaic_mhi_free_controller(qdev->mhi_cntrl, link_up);
52162306a36Sopenharmony_ci	cleanup_qdev(qdev);
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic void qaic_pci_shutdown(struct pci_dev *pdev)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	/* see qaic_exit for what link_up is doing */
52762306a36Sopenharmony_ci	link_up = true;
52862306a36Sopenharmony_ci	qaic_pci_remove(pdev);
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic pci_ers_result_t qaic_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t error)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	return PCI_ERS_RESULT_NEED_RESET;
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic void qaic_pci_reset_prepare(struct pci_dev *pdev)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct qaic_device *qdev = pci_get_drvdata(pdev);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	qaic_notify_reset(qdev);
54162306a36Sopenharmony_ci	qaic_mhi_start_reset(qdev->mhi_cntrl);
54262306a36Sopenharmony_ci	qaic_dev_reset_clean_local_state(qdev, false);
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic void qaic_pci_reset_done(struct pci_dev *pdev)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	struct qaic_device *qdev = pci_get_drvdata(pdev);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	qdev->in_reset = false;
55062306a36Sopenharmony_ci	qaic_mhi_reset_done(qdev->mhi_cntrl);
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic const struct mhi_device_id qaic_mhi_match_table[] = {
55462306a36Sopenharmony_ci	{ .chan = "QAIC_CONTROL", },
55562306a36Sopenharmony_ci	{},
55662306a36Sopenharmony_ci};
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic struct mhi_driver qaic_mhi_driver = {
55962306a36Sopenharmony_ci	.id_table = qaic_mhi_match_table,
56062306a36Sopenharmony_ci	.remove = qaic_mhi_remove,
56162306a36Sopenharmony_ci	.probe = qaic_mhi_probe,
56262306a36Sopenharmony_ci	.ul_xfer_cb = qaic_mhi_ul_xfer_cb,
56362306a36Sopenharmony_ci	.dl_xfer_cb = qaic_mhi_dl_xfer_cb,
56462306a36Sopenharmony_ci	.driver = {
56562306a36Sopenharmony_ci		.name = "qaic_mhi",
56662306a36Sopenharmony_ci	},
56762306a36Sopenharmony_ci};
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic const struct pci_device_id qaic_ids[] = {
57062306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_QCOM, PCI_DEV_AIC100), },
57162306a36Sopenharmony_ci	{ }
57262306a36Sopenharmony_ci};
57362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, qaic_ids);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic const struct pci_error_handlers qaic_pci_err_handler = {
57662306a36Sopenharmony_ci	.error_detected = qaic_pci_error_detected,
57762306a36Sopenharmony_ci	.reset_prepare = qaic_pci_reset_prepare,
57862306a36Sopenharmony_ci	.reset_done = qaic_pci_reset_done,
57962306a36Sopenharmony_ci};
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic struct pci_driver qaic_pci_driver = {
58262306a36Sopenharmony_ci	.name = QAIC_NAME,
58362306a36Sopenharmony_ci	.id_table = qaic_ids,
58462306a36Sopenharmony_ci	.probe = qaic_pci_probe,
58562306a36Sopenharmony_ci	.remove = qaic_pci_remove,
58662306a36Sopenharmony_ci	.shutdown = qaic_pci_shutdown,
58762306a36Sopenharmony_ci	.err_handler = &qaic_pci_err_handler,
58862306a36Sopenharmony_ci};
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic int __init qaic_init(void)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	int ret;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	ret = mhi_driver_register(&qaic_mhi_driver);
59562306a36Sopenharmony_ci	if (ret) {
59662306a36Sopenharmony_ci		pr_debug("qaic: mhi_driver_register failed %d\n", ret);
59762306a36Sopenharmony_ci		return ret;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	ret = pci_register_driver(&qaic_pci_driver);
60162306a36Sopenharmony_ci	if (ret) {
60262306a36Sopenharmony_ci		pr_debug("qaic: pci_register_driver failed %d\n", ret);
60362306a36Sopenharmony_ci		goto free_mhi;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	return 0;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cifree_mhi:
60962306a36Sopenharmony_ci	mhi_driver_unregister(&qaic_mhi_driver);
61062306a36Sopenharmony_ci	return ret;
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic void __exit qaic_exit(void)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	/*
61662306a36Sopenharmony_ci	 * We assume that qaic_pci_remove() is called due to a hotplug event
61762306a36Sopenharmony_ci	 * which would mean that the link is down, and thus
61862306a36Sopenharmony_ci	 * qaic_mhi_free_controller() should not try to access the device during
61962306a36Sopenharmony_ci	 * cleanup.
62062306a36Sopenharmony_ci	 * We call pci_unregister_driver() below, which also triggers
62162306a36Sopenharmony_ci	 * qaic_pci_remove(), but since this is module exit, we expect the link
62262306a36Sopenharmony_ci	 * to the device to be up, in which case qaic_mhi_free_controller()
62362306a36Sopenharmony_ci	 * should try to access the device during cleanup to put the device in
62462306a36Sopenharmony_ci	 * a sane state.
62562306a36Sopenharmony_ci	 * For that reason, we set link_up here to let qaic_mhi_free_controller
62662306a36Sopenharmony_ci	 * know the expected link state. Since the module is going to be
62762306a36Sopenharmony_ci	 * removed at the end of this, we don't need to worry about
62862306a36Sopenharmony_ci	 * reinitializing the link_up state after the cleanup is done.
62962306a36Sopenharmony_ci	 */
63062306a36Sopenharmony_ci	link_up = true;
63162306a36Sopenharmony_ci	pci_unregister_driver(&qaic_pci_driver);
63262306a36Sopenharmony_ci	mhi_driver_unregister(&qaic_mhi_driver);
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cimodule_init(qaic_init);
63662306a36Sopenharmony_cimodule_exit(qaic_exit);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ciMODULE_AUTHOR(QAIC_DESC " Kernel Driver Team");
63962306a36Sopenharmony_ciMODULE_DESCRIPTION(QAIC_DESC " Accel Driver");
64062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
641