sdk-hwV1.3/lichee/linux-4.9/drivers/media/sunxi_nna/nna_sunxi.c

602 lines
14 KiB
C

#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/time.h>
#include <linux/cdev.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/dma-buf.h>
#include <linux/nna_driver.h>
#include "nna_driver_i.h"
//#define NNA_DEBUG_IRQ
#define TIMEOUT_MS 3000
#define PRINTK_IOMMU_ADDR 0
static nna_context *nna_priv;
static struct attribute *nna_attributes[] = {
//&dev_attr_debug.attr,
//&dev_attr_func_runtime.attr,
NULL
};
static struct attribute_group nna_attribute_group = {
.name = "attr",
.attrs = nna_attributes
};
static const struct of_device_id sunxi_nna_match[] = {
{.compatible = "allwinner,sunxi-nna",},
{},
};
int reg2status(int val)
{
int ret = 0;
switch (val) {
case 0x150001:
case 0x140001:
ret = CONV_ACT_SIG;
break;
case 0x150011:
case 0x100011:
ret = CONV_ACT_POOL_SIG;
break;
case 0x150000:
ret = CONV_SIG;
break;
case 0x1:
ret = ELTWISE_OR_PRELU_SIG;
break;
case 0x11:
ret = PRELU_POOL_SIG;
break;
case 0x10:
ret = POOL_SIG;
break;
default:
break;
}
return ret;
}
static irqreturn_t nna_interrupt(int irq, void *dev_id)
{
__u32 ret = readl(nna_priv->io + OFFSET_NNA_GLB_S_INTR_STATUS_0);
writel(ret, nna_priv->io + OFFSET_NNA_GLB_S_INTR_STATUS_0);
nna_priv->nna_irq_status |= ret;
wake_up(&nna_priv->nna_queue);
return IRQ_HANDLED;
}
long nna_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
__s32 ret = 0;
__s32 restime = 0;
int i;
struct sg_table *sgt, *sgt_bak;
struct scatterlist *sgl, *sgl_bak;
struct user_dma_param user_data;
struct nna_dma_buf *nna_buf = NULL;
#if defined(NNA_DEBUG_IRQ)
struct timeval time_start, time_end;
unsigned int runtime;
do_gettimeofday(&time_start);
#endif
mutex_lock(&nna_priv->mutex);
switch (cmd) {
case NNA_CMD_ENABLE_IRQ:{
NNA_INFO("enable nna irq\n");
enable_irq(nna_priv->irq);
break;
}
case NNA_CMD_DISABLE_IRQ:{
NNA_INFO("disable nna irq\n");
disable_irq(nna_priv->irq);
break;
}
case NNA_CMD_SET_FREQ:{
if (arg > NNA_CLOCK_1200M) {
NNA_WARN("freq set too large:%ld", arg);
break;
}
NNA_INFO("old clk freq:%ld\n", clk_get_rate(nna_priv->clk));
clk_set_rate(nna_priv->clk, arg*1000000);
sunxi_periph_reset_assert(nna_priv->clk);
sunxi_periph_reset_deassert(nna_priv->clk);
NNA_INFO("new clk freq:%ld\n", clk_get_rate(nna_priv->clk));
break;
}
case NNA_CMD_RESET_NNA:{
sunxi_periph_reset_assert(nna_priv->clk_rst);
sunxi_periph_reset_deassert(nna_priv->clk_rst);
break;
}
case NNA_CMD_QUERY:{
restime = wait_event_timeout(nna_priv->nna_queue,
arg == (arg & nna_priv->nna_irq_status),
msecs_to_jiffies(TIMEOUT_MS));
if (restime <= 0) {
NNA_ERR("wait status timeout arg 0x%lx, status 0x%x\n", arg, nna_priv->nna_irq_status);
ret = -1;
nna_priv->nna_irq_status = 0;
} else {
nna_priv->nna_irq_status = 0;
ret = nna_priv->nna_irq_status;
}
break;
}
case NNA_CMD_MAP_DMA_FD: {
nna_buf = (struct nna_dma_buf *)kmalloc(sizeof(struct nna_dma_buf), GFP_KERNEL);
if (nna_buf == NULL) {
NNA_ERR("malloc nna_dma_buf failed\n");
return -EFAULT;
}
if (copy_from_user(&user_data, (void __user *)arg,
sizeof(struct user_dma_param))) {
NNA_ERR("IOCTL_GET_IOMMU_ADDR copy_from_user error");
return -EFAULT;
}
nna_buf->fd = user_data.fd;
nna_buf->dma_buf = dma_buf_get(nna_buf->fd);
if (nna_buf->dma_buf < 0) {
NNA_ERR("ve get dma_buf error");
return -EFAULT;
}
nna_buf->attachment = dma_buf_attach(nna_buf->dma_buf, nna_priv->platform_dev);
if (nna_buf->attachment < 0) {
NNA_ERR("ve get dma_buf_attachment error");
goto RELEASE_DMA_BUF;
}
sgt = dma_buf_map_attachment(nna_buf->attachment, DMA_BIDIRECTIONAL);
sgt_bak = kmalloc(sizeof(struct sg_table), GFP_KERNEL | __GFP_ZERO);
if (sgt_bak == NULL)
NNA_ERR("alloc sgt fail\n");
if (sg_alloc_table(sgt_bak, sgt->nents, GFP_KERNEL) != 0)
NNA_ERR("alloc table fail\n");
sgl_bak = sgt_bak->sgl;
for_each_sg(sgt->sgl, sgl, sgt->nents, i) {
sg_set_page(sgl_bak, sg_page(sgl), sgl->length, sgl->offset);
sgl_bak = sg_next(sgl_bak);
}
nna_buf->sgt = sgt_bak;
if (nna_buf->sgt < 0) {
NNA_ERR("get sg_table error\n");
goto RELEASE_DMA_BUF;
}
ret = dma_map_sg(nna_priv->platform_dev, nna_buf->sgt->sgl, nna_buf->sgt->nents,
DMA_BIDIRECTIONAL);
if (ret != 1) {
NNA_ERR("dma_map_sg error\n");
goto RELEASE_DMA_BUF;
}
nna_buf->phy_addr = sg_dma_address(nna_buf->sgt->sgl);
user_data.phy_addr = (unsigned int)(nna_buf->phy_addr & 0xffffffff);
if (copy_to_user((void __user *)arg, &user_data, sizeof(struct user_dma_param))) {
NNA_ERR("get iommu copy_to_user error\n");
goto RELEASE_DMA_BUF;
}
nna_buf->p_id = current->tgid;
#if PRINTK_IOMMU_ADDR
NNA_INFO("fd:%d, iommu_addr:%lx, dma_buf:%p, dma_buf_attach:%p, sg_table:%p, nents:%d, pid:%d\n",
nna_buf->fd,
nna_buf->iommu_addr,
nna_buf->dma_buf,
nna_buf->attachment,
nna_buf->sgt,
nna_buf->sgt->nents,
nna_buf->p_id);
#endif
ret = 0;
aw_mem_list_add_tail(&nna_buf->i_list, &nna_priv->list);
break;
RELEASE_DMA_BUF:
if (nna_buf->dma_buf > 0) {
if (nna_buf->attachment > 0) {
if (nna_buf->sgt > 0) {
dma_unmap_sg(nna_priv->platform_dev, nna_buf->sgt->sgl, nna_buf->sgt->nents,
DMA_BIDIRECTIONAL);
dma_buf_unmap_attachment(nna_buf->attachment, nna_buf->sgt, DMA_BIDIRECTIONAL);
sg_free_table(nna_buf->sgt);
kfree(nna_buf->sgt);
}
dma_buf_detach(nna_buf->dma_buf, nna_buf->attachment);
}
dma_buf_put(nna_buf->dma_buf);
}
kfree(nna_buf);
ret = -EINVAL;
break;
}
case NNA_CMD_UNMAP_DMA_FD:
{
if (copy_from_user(&user_data, (void __user *)arg,
sizeof(struct user_dma_param))) {
NNA_ERR("copy_from_user error");
return -EFAULT;
}
aw_mem_list_for_each_entry(nna_buf, &nna_priv->list, i_list) {
if (nna_buf->fd == user_data.fd && nna_buf->p_id == current->tgid) {
#if PRINTK_IOMMU_ADDR
NNA_INFO("free: fd:%d, iommu_addr:%lx, dma_buf:%p, dma_buf_attach:%p, sg_table:%p nets:%d, pid:%d\n",
nna_buf->fd,
nna_buf->iommu_addr,
nna_buf->dma_buf,
nna_buf->attachment,
nna_buf->sgt,
nna_buf->sgt->nents,
nna_buf->p_id);
#endif
if (nna_buf->dma_buf > 0) {
if (nna_buf->attachment > 0) {
if (nna_buf->sgt > 0) {
dma_unmap_sg(nna_priv->platform_dev, nna_buf->sgt->sgl, nna_buf->sgt->nents,
DMA_BIDIRECTIONAL);
dma_buf_unmap_attachment(nna_buf->attachment, nna_buf->sgt,
DMA_BIDIRECTIONAL);
sg_free_table(nna_buf->sgt);
kfree(nna_buf->sgt);
}
dma_buf_detach(nna_buf->dma_buf, nna_buf->attachment);
}
dma_buf_put(nna_buf->dma_buf);
}
aw_mem_list_del(&nna_buf->i_list);
kfree(nna_buf);
break;
}
}
break;
}
/* Invalid IOCTL call */
default:
ret = -EINVAL;
}
mutex_unlock(&nna_priv->mutex);
#if defined(NNA_DEBUG_IRQ)
do_gettimeofday(&time_end);
runtime = (time_end.tv_sec - time_start.tv_sec) * 1000000 +
(time_end.tv_usec - time_start.tv_usec);
NNA_ERR("%s:use %u us!\n", __func__, runtime);
#endif
return ret;
}
static int nna_open(struct inode *inode, struct file *file)
{
mutex_lock(&nna_priv->mutex);
if (nna_priv->refs_count > 0) {
NNA_INFO("open device refs_count:%d", nna_priv->refs_count);
nna_priv->refs_count++;
mutex_unlock(&nna_priv->mutex);
return 0;
}
NNA_INFO("nna_open irqnum = %d\n", nna_priv->irq);
AW_MEM_INIT_LIST_HEAD(&nna_priv->list);
clk_prepare_enable(nna_priv->clk);
nna_priv->refs_count++;
mutex_unlock(&nna_priv->mutex);
return 0;
}
static int nna_release(struct inode *inode, struct file *file)
{
struct aw_mem_list_head *pos, *q;
struct nna_dma_buf *nna_buf;
mutex_lock(&nna_priv->mutex);
if (nna_priv->refs_count > 1) {
nna_priv->refs_count--;
NNA_INFO("close device refs_count:%d", nna_priv->refs_count);
mutex_unlock(&nna_priv->mutex);
return 0;
}
NNA_INFO("nna_release irqnum = %d\n", nna_priv->irq);
clk_disable_unprepare(nna_priv->clk);
aw_mem_list_for_each_safe(pos, q, &nna_priv->list) {
nna_buf = aw_mem_list_entry(pos, struct nna_dma_buf, i_list);
if (nna_buf->p_id == current->tgid) {
#if PRINTK_IOMMU_ADDR
NNA_INFO("free: fd:%d, iommu_addr:%lx, dma_buf:%p, dma_buf_attach:%p, sg_table:%p nets:%d, pid:%d\n",
nna_buf->fd,
nna_buf->iommu_addr,
nna_buf->dma_buf,
nna_buf->attachment,
nna_buf->sgt,
nna_buf->sgt->nents,
nna_buf->p_id);
#endif
if (nna_buf->dma_buf > 0) {
if (nna_buf->attachment > 0) {
if (nna_buf->sgt > 0) {
dma_unmap_sg(nna_priv->platform_dev, nna_buf->sgt->sgl, nna_buf->sgt->nents,
DMA_BIDIRECTIONAL);
dma_buf_unmap_attachment(nna_buf->attachment, nna_buf->sgt, DMA_BIDIRECTIONAL);
sg_free_table(nna_buf->sgt);
kfree(nna_buf->sgt);
}
dma_buf_detach(nna_buf->dma_buf, nna_buf->attachment);
}
dma_buf_put(nna_buf->dma_buf);
}
aw_mem_list_del(&nna_buf->i_list);
kfree(nna_buf);
}
}
nna_priv->refs_count--;
mutex_unlock(&nna_priv->mutex);
return 0;
}
static const struct file_operations nna_fops = {
.owner = THIS_MODULE,
.open = nna_open,
.release = nna_release,
.unlocked_ioctl = nna_ioctl,
};
static int nna_int(nna_context *devp)
{
alloc_chrdev_region(&devp->devid, 0, 1, "nna_chrdev");
devp->nna_cdev = cdev_alloc();
if (devp->nna_cdev == NULL) {
NNA_ERR("cdev alloc error!\n");
return -1;
}
cdev_init(devp->nna_cdev, &nna_fops);
devp->nna_cdev->owner = THIS_MODULE;
if (cdev_add(devp->nna_cdev, devp->devid, 1) != 0) {
NNA_ERR("major number %d but add failed.\n", MAJOR(devp->devid));
return -1;
}
devp->nna_class = class_create(THIS_MODULE, "nna");
if (IS_ERR(devp->nna_class)) {
NNA_ERR("create class error\n");
return -1;
}
devp->nna_dev = device_create(devp->nna_class, NULL, devp->devid, NULL, "nna");
if (IS_ERR(devp->nna_dev)) {
NNA_ERR("create device error\n");
return -1;
}
if (sysfs_create_group(&devp->nna_dev->kobj, &nna_attribute_group) < 0) {
NNA_ERR("sysfs_create_file fail!\n");
return -1;
}
return 0;
}
static int nna_deint(nna_context *devp)
{
sysfs_remove_group(&devp->nna_dev->kobj, &nna_attribute_group);
device_destroy(devp->nna_class, devp->devid);
class_destroy(devp->nna_class);
cdev_del(devp->nna_cdev);
unregister_chrdev_region(devp->devid, 1);
return 0;
}
static int nna_probe(struct platform_device *pdev)
{
int size;
struct resource *res;
int ret = 0;
NNA_INFO("start nna_probe\n");
nna_priv = kmalloc(sizeof(nna_context), GFP_KERNEL);
if (nna_priv == NULL) {
NNA_ERR("malloc mem for nna device err!\n");
return -ENOMEM;
}
memset(nna_priv, 0, sizeof(nna_context));
if (nna_int(nna_priv) != 0) {
NNA_ERR("nna init failed!\n");
goto EXIT;
}
nna_priv->platform_dev = &pdev->dev;
/*get the register addr*/
nna_priv->io = of_iomap(pdev->dev.of_node, 0);
if (nna_priv->io == NULL) {
NNA_WARN("device tree not support io reigster,we try the ohter way.\n");
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
NNA_ERR("failed to get memory register\n");
ret = -ENXIO;
goto EXIT;
}
size = (res->end - res->start) + 1;
/* map the memory */
nna_priv->io = ioremap(res->start, size);
if (nna_priv->io == NULL) {
NNA_ERR("we can't map the register addr\n");
ret = -ENXIO;
goto EXIT;
}
}
/* get the irq */
nna_priv->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
if (!nna_priv->irq) {
NNA_WARN("device tree not support irq,we try the ohter way.\n");
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
NNA_ERR("failed to get irq resource\n");
ret = -ENXIO;
goto EXIT;
}
nna_priv->irq = res->start;
}
NNA_INFO("get irq num: %d\n", nna_priv->irq);
/*get the clk*/
nna_priv->clk = of_clk_get(pdev->dev.of_node, 0);
if (IS_ERR(nna_priv->clk)) {
NNA_ERR("fail to get clk\n");
goto EXIT;
}
nna_priv->clk_rst = of_clk_get(pdev->dev.of_node, 1);
if (IS_ERR(nna_priv->clk)) {
NNA_ERR("fail to get clk\n");
goto EXIT;
}
/* request the irq */
ret = request_threaded_irq(nna_priv->irq, NULL, nna_interrupt,
IRQF_EARLY_RESUME | IRQF_ONESHOT, dev_name(&pdev->dev), &nna_priv->devid);
if (ret) {
NNA_ERR("failed to install irq resource\n");
goto EXIT;
}
disable_irq(nna_priv->irq);
/*mutex init*/
mutex_init(&nna_priv->mutex);
init_waitqueue_head(&nna_priv->nna_queue);
nna_priv->nna_irq_status = 0;
return 0;
EXIT:
if (nna_priv->clk != NULL)
clk_put(nna_priv->clk);
if (nna_priv->io != NULL)
iounmap(nna_priv->io);
if (nna_priv != NULL)
kfree(nna_priv);
return ret;
}
static int nna_remove(struct platform_device *pdev)
{
NNA_INFO("nna_remove\n");
mutex_destroy(&nna_priv->mutex);
free_irq(nna_priv->irq, &nna_priv->devid);
clk_put(nna_priv->clk);
iounmap(nna_priv->io);
nna_deint(nna_priv);
kfree(nna_priv);
return 0;
}
static int nna_suspend(struct platform_device *pdev, pm_message_t state)
{
NNA_INFO("nna_suspend\n");
mutex_lock(&nna_priv->mutex);
enable_irq_wake(nna_priv->irq);
mutex_unlock(&nna_priv->mutex);
return 0;
}
static int nna_resume(struct platform_device *pdev)
{
NNA_INFO("nna_resume\n");
mutex_lock(&nna_priv->mutex);
disable_irq_wake(nna_priv->irq);
mutex_unlock(&nna_priv->mutex);
return 0;
}
static struct platform_driver nna_driver = {
.probe = nna_probe,
.remove = nna_remove,
.suspend = nna_suspend,
.resume = nna_resume,
.driver = {
.name = "nna",
.owner = THIS_MODULE,
.of_match_table = sunxi_nna_match,
},
};
int __init nna_module_init(void)
{
int ret;
NNA_INFO("nna module init.\n");
ret = platform_driver_register(&nna_driver);
return ret;
}
static void __exit nna_module_exit(void)
{
NNA_INFO("nna_module_exit\n");
platform_driver_unregister(&nna_driver);
}
module_init(nna_module_init);
module_exit(nna_module_exit);
MODULE_AUTHOR("jilinglin");
MODULE_AUTHOR("jilinglin<jilinglin@allwinnertech.com>");
MODULE_DESCRIPTION("nna driver");
MODULE_LICENSE("GPL");