]> rtime.felk.cvut.cz Git - zynq/linux.git/blob - drivers/uio/uio_xilinx_ai_engine.c
uio: xilinx_ai_engine: Set the uio device as pdev driver data
[zynq/linux.git] / drivers / uio / uio_xilinx_ai_engine.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Xilinx UIO driver for AI Engine
4  *
5  * Copyright (C) 2018 Xilinx, Inc.
6  *
7  * Author: Hyun Woo Kwon <hyun.kwon@xilinx.com>
8  */
9
10 #include <linux/mm.h>
11 #include <linux/module.h>
12 #include <linux/of.h>
13 #include <linux/of_device.h>
14 #include <linux/platform_data/uio_dmem_genirq.h>
15 #include <linux/platform_device.h>
16 #include <linux/uio_driver.h>
17
18 #define DRIVER_NAME "xilinx-aiengine"
19
20 static uint xilinx_ai_engine_mem_cnt = 1;
21 module_param_named(mem_cnt, xilinx_ai_engine_mem_cnt, uint, 0444);
22 MODULE_PARM_DESC(mem_cnt, "Dynamic memory allocation count (default: 1)");
23
24 static uint xilinx_ai_engine_mem_size = 32 * 1024 * 1024;
25 module_param_named(mem_size, xilinx_ai_engine_mem_size, uint, 0444);
26 MODULE_PARM_DESC(mem_size,
27                  "Dynamic memory allocation size in bytes (default: 32 MB)");
28
29 static int xilinx_ai_engine_mem_index(struct uio_info *info,
30                                       struct vm_area_struct *vma)
31 {
32         if (vma->vm_pgoff < MAX_UIO_MAPS) {
33                 if (info->mem[vma->vm_pgoff].size == 0)
34                         return -1;
35                 return (int)vma->vm_pgoff;
36         }
37         return -1;
38 }
39
40 static const struct vm_operations_struct xilinx_ai_engine_vm_ops = {
41 #ifdef CONFIG_HAVE_IOREMAP_PROT
42         .access = generic_access_phys,
43 #endif
44 };
45
46 static int xilinx_ai_engine_mmap(struct uio_info *info,
47                                  struct vm_area_struct *vma)
48 {
49         int mi = xilinx_ai_engine_mem_index(info, vma);
50         struct uio_mem *mem;
51
52         if (mi < 0)
53                 return -EINVAL;
54         mem = info->mem + mi;
55
56         if (mem->addr & ~PAGE_MASK)
57                 return -ENODEV;
58         if (vma->vm_end - vma->vm_start > mem->size)
59                 return -EINVAL;
60
61         vma->vm_ops = &xilinx_ai_engine_vm_ops;
62         /*
63          * Make the dynamic memory mapping as write-combined. Only first one
64          * will be the mmio region, which will be mapped as noncached.
65          */
66         if (mi < 1)
67                 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
68         else
69                 vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
70
71         /*
72          * We cannot use the vm_iomap_memory() helper here,
73          * because vma->vm_pgoff is the map index we looked
74          * up above in uio_find_mem_index(), rather than an
75          * actual page offset into the mmap.
76          *
77          * So we just do the physical mmap without a page
78          * offset.
79          */
80         return remap_pfn_range(vma,
81                                vma->vm_start,
82                                mem->addr >> PAGE_SHIFT,
83                                vma->vm_end - vma->vm_start,
84                                vma->vm_page_prot);
85 }
86
87 static int xilinx_ai_engine_probe(struct platform_device *pdev)
88 {
89         struct platform_device *uio;
90         struct uio_dmem_genirq_pdata *pdata;
91         unsigned int i;
92         int ret;
93
94         uio = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE);
95         if (!uio)
96                 return -ENOMEM;
97         uio->driver_override = "uio_dmem_genirq";
98         uio->dev.parent = &pdev->dev;
99
100         pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
101         if (!pdata) {
102                 ret = -ENOMEM;
103                 goto err_out;
104         }
105
106         pdata->num_dynamic_regions = xilinx_ai_engine_mem_cnt;
107         pdata->dynamic_region_sizes = &xilinx_ai_engine_mem_size;
108         pdata->uioinfo.name = DRIVER_NAME;
109         pdata->uioinfo.version = "devicetree";
110         pdata->uioinfo.irq = UIO_IRQ_CUSTOM;
111         pdata->uioinfo.mmap = xilinx_ai_engine_mmap;
112         /* Set the offset value as it's map index for each memory */
113         for (i = 0; i < MAX_UIO_MAPS; i++)
114                 pdata->uioinfo.mem[i].offs = i << PAGE_SHIFT;
115         ret = platform_device_add_data(uio, pdata, sizeof(*pdata));
116         if (ret)
117                 goto err_out;
118
119         /* Mirror the parent device resource to uio device */
120         ret = platform_device_add_resources(uio, pdev->resource,
121                                             pdev->num_resources);
122         if (ret)
123                 goto err_out;
124
125         /* Configure the dma for uio device using the parent of_node */
126         uio->dev.bus = &platform_bus_type;
127         ret = of_dma_configure(&uio->dev, of_node_get(pdev->dev.of_node), true);
128         of_node_put(pdev->dev.of_node);
129         if (ret)
130                 goto err_out;
131
132         ret = platform_device_add(uio);
133         if (ret)
134                 goto err_out;
135         platform_set_drvdata(uio, pdata);
136         platform_set_drvdata(pdev, uio);
137
138         dev_info(&pdev->dev, "Xilinx AI Engine UIO driver probed");
139         return 0;
140
141 err_out:
142         platform_device_put(pdev);
143         dev_err(&pdev->dev,
144                 "failed to probe Xilinx AI Engine UIO driver");
145         return ret;
146 }
147
148 static int xilinx_ai_engine_remove(struct platform_device *pdev)
149 {
150         struct platform_device *uio = platform_get_drvdata(pdev);
151
152         platform_device_unregister(uio);
153         of_node_put(pdev->dev.of_node);
154
155         return 0;
156 }
157
158 static const struct of_device_id xilinx_ai_engine_of_match[] = {
159         { .compatible = "xlnx,ai_engine", },
160         { /* end of table */ },
161 };
162 MODULE_DEVICE_TABLE(of, xilinx_ai_engine_of_match);
163
164 static struct platform_driver xilinx_ai_engine_driver = {
165         .probe                  = xilinx_ai_engine_probe,
166         .remove                 = xilinx_ai_engine_remove,
167         .driver                 = {
168                 .name           = DRIVER_NAME,
169                 .of_match_table = xilinx_ai_engine_of_match,
170         },
171 };
172
173 module_platform_driver(xilinx_ai_engine_driver);
174
175 MODULE_AUTHOR("Xilinx, Inc.");
176 MODULE_LICENSE("GPL v2");