1 From 2a47ccf97c6a91bc56f8cfb387d47f59cc347dd5 Mon Sep 17 00:00:00 2001
2 From: Phil Elwell <phil@raspberrypi.com>
3 Date: Sat, 14 Oct 2023 14:57:49 +0100
4 Subject: [PATCH] vc_mem: Add the DMA memcpy support from bcm2708_fb
6 bcm2708_fb is disabled by the vc4-kms-v3d overlay, which means that the
7 DMA memcpy support it provides is not available to allow vclog to read
8 the VC logs from the top 16MB on Pi 2 and Pi 3. Add the code to the
9 vc_mem driver, which will still be enabled.
11 It ought to be possible to do a proper DMA_MEM_TO_MEM copy via the
12 generic DMA customer API, but that can be a later step.
14 Signed-off-by: Phil Elwell <phil@raspberrypi.com>
16 drivers/char/broadcom/vc_mem.c | 259 +++++++++++++++++++++++++++++++++
17 1 file changed, 259 insertions(+)
19 --- a/drivers/char/broadcom/vc_mem.c
20 +++ b/drivers/char/broadcom/vc_mem.c
22 #include <linux/uaccess.h>
23 #include <linux/dma-mapping.h>
24 #include <linux/broadcom/vc_mem.h>
25 +#include <linux/compat.h>
26 +#include <linux/platform_data/dma-bcm2708.h>
27 +#include <soc/bcm2835/raspberrypi-firmware.h>
29 #define DRIVER_NAME "vc-mem"
31 +/* N.B. These use a different magic value for compatibility with bmc7208_fb */
32 +#define VC_MEM_IOC_DMACOPY _IOW('z', 0x22, struct vc_mem_dmacopy)
33 +#define VC_MEM_IOC_DMACOPY32 _IOW('z', 0x22, struct vc_mem_dmacopy32)
35 +/* address with no aliases */
36 +#define INTALIAS_NORMAL(x) ((x) & ~0xc0000000)
37 +/* cache coherent but non-allocating in L1 and L2 */
38 +#define INTALIAS_L1L2_NONALLOCATING(x) (((x) & ~0xc0000000) | 0x80000000)
40 /* Device (/dev) related variables */
41 static dev_t vc_mem_devnum;
42 static struct class *vc_mem_class;
43 @@ -36,6 +48,20 @@ static int vc_mem_inited;
44 static struct dentry *vc_mem_debugfs_entry;
47 +struct vc_mem_dmacopy {
54 +struct vc_mem_dmacopy32 {
62 * Videocore memory addresses and size
64 @@ -62,6 +88,20 @@ static uint phys_addr;
72 + void __iomem *dma_chan_base;
73 + wait_queue_head_t dma_waitq;
74 + void *cb_base; /* DMA control blocks */
75 + dma_addr_t cb_handle;
78 +struct { u32 base, length; } gpu_mem;
79 +static struct mutex dma_mutex;
80 +static struct vc_mem_dma vc_mem_dma;
83 vc_mem_open(struct inode *inode, struct file *file)
85 @@ -99,6 +139,189 @@ vc_mem_get_current_size(void)
87 EXPORT_SYMBOL_GPL(vc_mem_get_current_size);
90 +vc_mem_dma_init(void)
92 + struct vc_mem_dma *vcdma = &vc_mem_dma;
93 + struct platform_device *pdev;
94 + struct device_node *fwnode;
95 + struct rpi_firmware *fw;
103 + fwnode = of_find_node_by_path("/system");
104 + rc = of_property_read_u32(fwnode, "linux,revision", &revision);
105 + revision = (revision >> 12) & 0xf;
106 + if (revision != 1 && revision != 2) {
107 + /* Only BCM2709 and BCM2710 may have logs where the ARMs
113 + fwnode = rpi_firmware_find_node();
117 + pdev = of_find_device_by_node(fwnode);
120 + rc = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
124 + fw = rpi_firmware_get(fwnode);
127 + rc = rpi_firmware_property(fw, RPI_FIRMWARE_GET_VC_MEMORY,
128 + &gpu_mem, sizeof(gpu_mem));
132 + gpu_mem.base = INTALIAS_NORMAL(gpu_mem.base);
134 + if (!gpu_mem.base || !gpu_mem.length) {
135 + dev_err(dev, "%s: unable to determine gpu memory (%x,%x)\n",
136 + __func__, gpu_mem.base, gpu_mem.length);
140 + vcdma->cb_base = dma_alloc_wc(dev, SZ_4K, &vcdma->cb_handle, GFP_KERNEL);
141 + if (!vcdma->cb_base) {
142 + dev_err(dev, "failed to allocate DMA CBs\n");
146 + rc = bcm_dma_chan_alloc(BCM_DMA_FEATURE_BULK,
147 + &vcdma->dma_chan_base,
150 + dev_err(dev, "failed to allocate a DMA channel\n");
154 + vcdma->dma_chan = rc;
156 + init_waitqueue_head(&vcdma->dma_waitq);
163 + dma_free_wc(dev, SZ_4K, vcdma->cb_base, vcdma->cb_handle);
169 +vc_mem_dma_uninit(void)
171 + struct vc_mem_dma *vcdma = &vc_mem_dma;
174 + bcm_dma_chan_free(vcdma->dma_chan);
175 + dma_free_wc(vcdma->dev, SZ_4K, vcdma->cb_base, vcdma->cb_handle);
180 +static int dma_memcpy(struct vc_mem_dma *vcdma, dma_addr_t dst, dma_addr_t src,
183 + struct bcm2708_dma_cb *cb = vcdma->cb_base;
184 + int burst_size = (vcdma->dma_chan == 0) ? 8 : 2;
186 + cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH |
187 + BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH |
197 + bcm_dma_start(vcdma->dma_chan_base, vcdma->cb_handle);
198 + bcm_dma_wait_idle(vcdma->dma_chan_base);
203 +static long vc_mem_copy(struct vc_mem_dmacopy *ioparam)
205 + struct vc_mem_dma *vcdma = &vc_mem_dma;
206 + size_t size = PAGE_SIZE;
207 + const u32 dma_xfer_chunk = 256;
209 + dma_addr_t bus_addr;
213 + /* restrict this to root user */
214 + if (!uid_eq(current_euid(), GLOBAL_ROOT_UID))
217 + if (mutex_lock_interruptible(&dma_mutex))
220 + rc = vc_mem_dma_init();
224 + vcdma = &vc_mem_dma;
226 + if (INTALIAS_NORMAL(ioparam->src) < gpu_mem.base ||
227 + INTALIAS_NORMAL(ioparam->src) >= gpu_mem.base + gpu_mem.length) {
228 + pr_err("%s: invalid memory access %x (%x-%x)", __func__,
229 + INTALIAS_NORMAL(ioparam->src), gpu_mem.base,
230 + gpu_mem.base + gpu_mem.length);
235 + buf = dma_alloc_coherent(vcdma->dev, PAGE_ALIGN(size), &bus_addr,
242 + for (offset = 0; offset < ioparam->length; offset += size) {
243 + size_t remaining = ioparam->length - offset;
244 + size_t s = min(size, remaining);
245 + u8 *p = (u8 *)((uintptr_t)ioparam->src + offset);
246 + u8 *q = (u8 *)ioparam->dst + offset;
248 + rc = dma_memcpy(vcdma, bus_addr,
249 + INTALIAS_L1L2_NONALLOCATING((u32)(uintptr_t)p),
250 + (s + dma_xfer_chunk - 1) & ~(dma_xfer_chunk - 1));
252 + dev_err(vcdma->dev, "dma_memcpy failed\n");
255 + if (copy_to_user(q, buf, s) != 0) {
256 + pr_err("%s: copy_to_user failed\n", __func__);
264 + dma_free_coherent(vcdma->dev, PAGE_ALIGN(size), buf,
267 + mutex_unlock(&dma_mutex);
273 vc_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
275 @@ -163,6 +386,21 @@ vc_mem_ioctl(struct file *file, unsigned
279 + case VC_MEM_IOC_DMACOPY:
281 + struct vc_mem_dmacopy ioparam;
282 + /* Get the parameter data.
285 + (&ioparam, (void *)arg, sizeof(ioparam))) {
286 + pr_err("%s: copy_from_user failed\n", __func__);
291 + rc = vc_mem_copy(&ioparam);
297 @@ -193,6 +431,24 @@ vc_mem_compat_ioctl(struct file *file, u
301 + case VC_MEM_IOC_DMACOPY32:
303 + struct vc_mem_dmacopy32 param32;
304 + struct vc_mem_dmacopy param;
305 + /* Get the parameter data.
307 + if (copy_from_user(¶m32, (void *)arg, sizeof(param32))) {
308 + pr_err("%s: copy_from_user failed\n", __func__);
312 + param.dst = compat_ptr(param32.dst);
313 + param.src = param32.src;
314 + param.length = param32.length;
315 + rc = vc_mem_copy(¶m);
320 rc = vc_mem_ioctl(file, cmd, arg);
322 @@ -330,6 +586,7 @@ vc_mem_init(void)
323 vc_mem_debugfs_init(dev);
326 + mutex_init(&dma_mutex);
330 @@ -352,6 +609,7 @@ vc_mem_exit(void)
332 pr_debug("%s: called\n", __func__);
334 + vc_mem_dma_uninit();
336 #ifdef CONFIG_DEBUG_FS
337 vc_mem_debugfs_deinit();
338 @@ -360,6 +618,7 @@ vc_mem_exit(void)
339 class_destroy(vc_mem_class);
340 cdev_del(&vc_mem_cdev);
341 unregister_chrdev_region(vc_mem_devnum, 1);