blob: d7b0697bdd7cb4c7cc8dde267ee2e9309a0ea50e [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
// Copyright(c) 2017-2021 Intel Corporation
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <uapi/misc/intel/gna.h>
#include "device.h"
#include "hw.h"
#include "ioctl.h"
#include "request.h"
static int recovery_timeout = 60;
#ifdef CONFIG_DEBUG_INTEL_GNA
module_param(recovery_timeout, int, 0644);
MODULE_PARM_DESC(recovery_timeout, "Recovery timeout in seconds");
#endif
static int __maybe_unused gna_runtime_suspend(struct device *dev)
{
struct gna_private *gna_priv = dev_get_drvdata(dev);
u32 val = gna_reg_read(gna_priv, GNA_MMIO_D0I3C);
dev_dbg(dev, "%s D0I3, reg %.8x\n", __func__, val);
return 0;
}
static int __maybe_unused gna_runtime_resume(struct device *dev)
{
struct gna_private *gna_priv = dev_get_drvdata(dev);
u32 val = gna_reg_read(gna_priv, GNA_MMIO_D0I3C);
dev_dbg(dev, "%s D0I3, reg %.8x\n", __func__, val);
return 0;
}
const struct dev_pm_ops __maybe_unused gna_pm = {
SET_RUNTIME_PM_OPS(gna_runtime_suspend, gna_runtime_resume, NULL)
};
static int gna_open(struct inode *inode, struct file *f)
{
struct gna_file_private *file_priv;
struct gna_private *gna_priv;
gna_priv = container_of(f->private_data, struct gna_private, misc);
file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
if (!file_priv)
return -ENOMEM;
file_priv->fd = f;
file_priv->gna_priv = gna_priv;
mutex_init(&file_priv->memlist_lock);
INIT_LIST_HEAD(&file_priv->memory_list);
INIT_LIST_HEAD(&file_priv->flist);
mutex_lock(&gna_priv->flist_lock);
list_add_tail(&file_priv->flist, &gna_priv->file_list);
mutex_unlock(&gna_priv->flist_lock);
f->private_data = file_priv;
return 0;
}
static int gna_release(struct inode *inode, struct file *f)
{
struct gna_memory_object *iter_mo, *temp_mo;
struct gna_file_private *file_priv;
struct gna_private *gna_priv;
/* free all memory objects created by that file */
file_priv = (struct gna_file_private *)f->private_data;
gna_priv = file_priv->gna_priv;
mutex_lock(&file_priv->memlist_lock);
list_for_each_entry_safe(iter_mo, temp_mo, &file_priv->memory_list, file_mem_list) {
queue_work(gna_priv->request_wq, &iter_mo->work);
wait_event(iter_mo->waitq, true);
gna_memory_free(gna_priv, iter_mo);
}
mutex_unlock(&file_priv->memlist_lock);
gna_delete_file_requests(f, gna_priv);
mutex_lock(&gna_priv->flist_lock);
list_del_init(&file_priv->flist);
mutex_unlock(&gna_priv->flist_lock);
kfree(file_priv);
f->private_data = NULL;
return 0;
}
static const struct file_operations gna_file_ops = {
.owner = THIS_MODULE,
.open = gna_open,
.release = gna_release,
.unlocked_ioctl = gna_ioctl,
};
static void gna_devm_idr_destroy(void *data)
{
struct idr *idr = data;
idr_destroy(idr);
}
static void gna_devm_deregister_misc_dev(void *data)
{
struct miscdevice *misc = data;
misc_deregister(misc);
}
static int gna_devm_register_misc_dev(struct device *parent, struct miscdevice *misc)
{
int ret;
ret = misc_register(misc);
if (ret) {
dev_err(parent, "misc device %s registering failed. errcode: %d\n",
misc->name, ret);
gna_devm_deregister_misc_dev(misc);
} else {
dev_dbg(parent, "device: %s registered\n",
misc->name);
}
ret = devm_add_action(parent, gna_devm_deregister_misc_dev, misc);
if (ret) {
dev_err(parent, "could not add devm action for %s misc deregister\n", misc->name);
gna_devm_deregister_misc_dev(misc);
}
return ret;
}
static void gna_pm_init(struct device *dev)
{
pm_runtime_set_autosuspend_delay(dev, 200);
pm_runtime_use_autosuspend(dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_allow(dev);
pm_runtime_put_noidle(dev);
}
static void gna_pm_remove(void *data)
{
struct device *dev = data;
pm_runtime_get_noresume(dev);
}
static irqreturn_t gna_interrupt(int irq, void *priv)
{
struct gna_private *gna_priv;
gna_priv = (struct gna_private *)priv;
gna_priv->dev_busy = false;
wake_up(&gna_priv->dev_busy_waitq);
return IRQ_HANDLED;
}
static void gna_devm_destroy_workqueue(void *data)
{
struct workqueue_struct *request_wq = data;
destroy_workqueue(request_wq);
}
static int gna_devm_create_singlethread_workqueue(struct gna_private *gna_priv)
{
struct device *dev = gna_parent(gna_priv);
const char *name = gna_name(gna_priv);
int ret;
gna_priv->request_wq = create_singlethread_workqueue(name);
if (!gna_priv->request_wq) {
dev_err(dev, "could not create %s workqueue\n", name);
return -EFAULT;
}
ret = devm_add_action(dev, gna_devm_destroy_workqueue, gna_priv->request_wq);
if (ret) {
dev_err(dev, "could not add devm action for %s workqueue\n", name);
gna_devm_destroy_workqueue(gna_priv->request_wq);
}
return ret;
}
int gna_probe(struct device *parent, struct gna_dev_info *dev_info, void __iomem *iobase, int irq)
{
static atomic_t dev_last_idx = ATOMIC_INIT(-1);
struct gna_private *gna_priv;
const char *dev_misc_name;
u32 bld_reg;
int ret;
gna_priv = devm_kzalloc(parent, sizeof(*gna_priv), GFP_KERNEL);
if (!gna_priv)
return -ENOMEM;
gna_priv->index = atomic_inc_return(&dev_last_idx);
gna_priv->recovery_timeout_jiffies = msecs_to_jiffies(recovery_timeout * 1000);
gna_priv->iobase = iobase;
gna_priv->info = *dev_info;
gna_priv->misc.parent = parent;
dev_misc_name = devm_kasprintf(parent, GFP_KERNEL, "%s%d", GNA_DV_NAME, gna_priv->index);
if (!dev_misc_name)
return -ENOMEM;
gna_priv->misc.name = dev_misc_name;
if (!(sizeof(dma_addr_t) > 4) ||
dma_set_mask(parent, DMA_BIT_MASK(64))) {
ret = dma_set_mask(parent, DMA_BIT_MASK(32));
if (ret) {
dev_err(parent, "dma_set_mask error: %d\n", ret);
return ret;
}
}
bld_reg = gna_reg_read(gna_priv, GNA_MMIO_IBUFFS);
gna_priv->hw_info.in_buf_s = bld_reg & GENMASK(7, 0);
ret = gna_mmu_alloc(gna_priv);
if (ret) {
dev_err(parent, "mmu allocation failed\n");
return ret;
}
dev_dbg(parent, "maximum memory size %llu num pd %d\n",
gna_priv->info.max_hw_mem, gna_priv->info.num_pagetables);
dev_dbg(parent, "desc rsvd size %d mmu vamax size %d\n",
gna_priv->info.desc_info.rsvd_size,
gna_priv->info.desc_info.mmu_info.vamax_size);
mutex_init(&gna_priv->mmu_lock);
idr_init(&gna_priv->memory_idr);
ret = devm_add_action(parent, gna_devm_idr_destroy, &gna_priv->memory_idr);
if (ret) {
dev_err(parent, "could not add devm action for idr\n");
return ret;
}
mutex_init(&gna_priv->memidr_lock);
mutex_init(&gna_priv->flist_lock);
INIT_LIST_HEAD(&gna_priv->file_list);
atomic_set(&gna_priv->request_count, 0);
mutex_init(&gna_priv->reqlist_lock);
INIT_LIST_HEAD(&gna_priv->request_list);
init_waitqueue_head(&gna_priv->dev_busy_waitq);
ret = gna_devm_create_singlethread_workqueue(gna_priv);
if (ret)
return ret;
ret = devm_request_irq(parent, irq, gna_interrupt,
IRQF_SHARED, dev_misc_name, gna_priv);
if (ret) {
dev_err(parent, "could not register for interrupt\n");
return ret;
}
gna_priv->misc.minor = MISC_DYNAMIC_MINOR;
gna_priv->misc.fops = &gna_file_ops;
gna_priv->misc.mode = 0666;
ret = gna_devm_register_misc_dev(parent, &gna_priv->misc);
if (ret)
return ret;
dev_set_drvdata(parent, gna_priv);
gna_pm_init(parent);
ret = devm_add_action(parent, gna_pm_remove, parent);
if (ret) {
dev_err(parent, "could not add devm action for pm\n");
return ret;
}
return 0;
}
static u32 gna_device_type_by_hwid(u32 hwid)
{
switch (hwid) {
case GNA_DEV_HWID_CNL:
return GNA_DEV_TYPE_0_9;
case GNA_DEV_HWID_GLK:
case GNA_DEV_HWID_EHL:
case GNA_DEV_HWID_ICL:
return GNA_DEV_TYPE_1_0;
case GNA_DEV_HWID_JSL:
case GNA_DEV_HWID_TGL:
case GNA_DEV_HWID_RKL:
return GNA_DEV_TYPE_2_0;
case GNA_DEV_HWID_ADL:
case GNA_DEV_HWID_RPL:
return GNA_DEV_TYPE_3_0;
default:
return 0;
}
}
int gna_getparam(struct gna_private *gna_priv, union gna_parameter *param)
{
switch (param->in.id) {
case GNA_PARAM_DEVICE_ID:
param->out.value = gna_priv->info.hwid;
break;
case GNA_PARAM_RECOVERY_TIMEOUT:
param->out.value = jiffies_to_msecs(gna_priv->recovery_timeout_jiffies) / 1000;
break;
case GNA_PARAM_INPUT_BUFFER_S:
param->out.value = gna_priv->hw_info.in_buf_s;
break;
case GNA_PARAM_DEVICE_TYPE:
param->out.value = gna_device_type_by_hwid(gna_priv->info.hwid);
break;
default:
dev_err(gna_dev(gna_priv), "unknown parameter id %llu\n", param->in.id);
return -EINVAL;
}
return 0;
}
MODULE_AUTHOR("Intel Corporation");
MODULE_DESCRIPTION("Intel(R) Gaussian & Neural Accelerator (Intel(R) GNA) Driver");
MODULE_LICENSE("GPL");