18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2013 - Virtual Open Systems
48c2ecf20Sopenharmony_ci * Author: Antonios Motakis <a.motakis@virtualopensystems.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/vfio.h>
108c2ecf20Sopenharmony_ci#include <linux/amba/bus.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "vfio_platform_private.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define DRIVER_VERSION  "0.10"
158c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR   "Antonios Motakis <a.motakis@virtualopensystems.com>"
168c2ecf20Sopenharmony_ci#define DRIVER_DESC     "VFIO for AMBA devices - User Level meta-driver"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/* probing devices from the AMBA bus */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic struct resource *get_amba_resource(struct vfio_platform_device *vdev,
218c2ecf20Sopenharmony_ci					  int i)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	struct amba_device *adev = (struct amba_device *) vdev->opaque;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	if (i == 0)
268c2ecf20Sopenharmony_ci		return &adev->res;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	return NULL;
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int get_amba_irq(struct vfio_platform_device *vdev, int i)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct amba_device *adev = (struct amba_device *) vdev->opaque;
348c2ecf20Sopenharmony_ci	int ret = 0;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if (i < AMBA_NR_IRQS)
378c2ecf20Sopenharmony_ci		ret = adev->irq[i];
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	/* zero is an unset IRQ for AMBA devices */
408c2ecf20Sopenharmony_ci	return ret ? ret : -ENXIO;
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int vfio_amba_probe(struct amba_device *adev, const struct amba_id *id)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct vfio_platform_device *vdev;
468c2ecf20Sopenharmony_ci	int ret;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
498c2ecf20Sopenharmony_ci	if (!vdev)
508c2ecf20Sopenharmony_ci		return -ENOMEM;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	vdev->name = kasprintf(GFP_KERNEL, "vfio-amba-%08x", adev->periphid);
538c2ecf20Sopenharmony_ci	if (!vdev->name) {
548c2ecf20Sopenharmony_ci		kfree(vdev);
558c2ecf20Sopenharmony_ci		return -ENOMEM;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	vdev->opaque = (void *) adev;
598c2ecf20Sopenharmony_ci	vdev->flags = VFIO_DEVICE_FLAGS_AMBA;
608c2ecf20Sopenharmony_ci	vdev->get_resource = get_amba_resource;
618c2ecf20Sopenharmony_ci	vdev->get_irq = get_amba_irq;
628c2ecf20Sopenharmony_ci	vdev->parent_module = THIS_MODULE;
638c2ecf20Sopenharmony_ci	vdev->reset_required = false;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	ret = vfio_platform_probe_common(vdev, &adev->dev);
668c2ecf20Sopenharmony_ci	if (ret) {
678c2ecf20Sopenharmony_ci		kfree(vdev->name);
688c2ecf20Sopenharmony_ci		kfree(vdev);
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return ret;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic void vfio_amba_remove(struct amba_device *adev)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct vfio_platform_device *vdev =
778c2ecf20Sopenharmony_ci		vfio_platform_remove_common(&adev->dev);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	kfree(vdev->name);
808c2ecf20Sopenharmony_ci	kfree(vdev);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic const struct amba_id pl330_ids[] = {
848c2ecf20Sopenharmony_ci	{ 0, 0 },
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(amba, pl330_ids);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic struct amba_driver vfio_amba_driver = {
908c2ecf20Sopenharmony_ci	.probe = vfio_amba_probe,
918c2ecf20Sopenharmony_ci	.remove = vfio_amba_remove,
928c2ecf20Sopenharmony_ci	.id_table = pl330_ids,
938c2ecf20Sopenharmony_ci	.drv = {
948c2ecf20Sopenharmony_ci		.name = "vfio-amba",
958c2ecf20Sopenharmony_ci		.owner = THIS_MODULE,
968c2ecf20Sopenharmony_ci	},
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cimodule_amba_driver(vfio_amba_driver);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION);
1028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1038c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
1048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
105