| From 2cba27bce0470a06237b3bd7883d43ade0d5c39c Mon Sep 17 00:00:00 2001 |
| From: Phil Elwell <phil@raspberrypi.org> |
| Date: Thu, 1 Nov 2018 17:31:37 +0000 |
| Subject: [PATCH] vchiq: Add 36-bit address support |
| |
| Conditional on a new compatible string, change the pagelist encoding |
| such that the top 24 bits are the pfn, leaving 8 bits for run length |
| (-1). |
| |
| Signed-off-by: Phil Elwell <phil@raspberrypi.org> |
| --- |
| .../interface/vchiq_arm/vchiq_2835_arm.c | 90 ++++++++++++++----- |
| .../interface/vchiq_arm/vchiq_arm.c | 6 ++ |
| .../interface/vchiq_arm/vchiq_arm.h | 1 + |
| 3 files changed, 75 insertions(+), 22 deletions(-) |
| |
| --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c |
| +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c |
| @@ -16,6 +16,8 @@ |
| #include <soc/bcm2835/raspberrypi-firmware.h> |
| |
| #define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32) |
| +#define VC_SAFE(x) (g_use_36bit_addrs ? ((u32)(x) | 0xc0000000) : (u32)(x)) |
| +#define IS_VC_SAFE(x) (g_use_36bit_addrs ? !((x) & ~0x3fffffffull) : 1) |
| |
| #include "vchiq_arm.h" |
| #include "vchiq_connected.h" |
| @@ -63,6 +65,7 @@ static void __iomem *g_regs; |
| */ |
| static unsigned int g_cache_line_size = 32; |
| static struct dma_pool *g_dma_pool; |
| +static unsigned int g_use_36bit_addrs = 0; |
| static unsigned int g_fragments_size; |
| static char *g_fragments_base; |
| static char *g_free_fragments; |
| @@ -106,6 +109,8 @@ int vchiq_platform_init(struct platform_ |
| g_cache_line_size = drvdata->cache_line_size; |
| g_fragments_size = 2 * g_cache_line_size; |
| |
| + g_use_36bit_addrs = (dev->dma_pfn_offset == 0); |
| + |
| /* Allocate space for the channels in coherent memory */ |
| slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE); |
| frag_mem_size = PAGE_ALIGN(g_fragments_size * MAX_FRAGMENTS); |
| @@ -117,14 +122,21 @@ int vchiq_platform_init(struct platform_ |
| return -ENOMEM; |
| } |
| |
| + if (!IS_VC_SAFE(slot_phys)) { |
| + dev_err(dev, "allocated DMA memory %pad is not VC-safe\n", |
| + &slot_phys); |
| + return -ENOMEM; |
| + } |
| + |
| WARN_ON(((unsigned long)slot_mem & (PAGE_SIZE - 1)) != 0); |
| + channelbase = VC_SAFE(slot_phys); |
| |
| vchiq_slot_zero = vchiq_init_slots(slot_mem, slot_mem_size); |
| if (!vchiq_slot_zero) |
| return -EINVAL; |
| |
| vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX] = |
| - (int)slot_phys + slot_mem_size; |
| + channelbase + slot_mem_size; |
| vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX] = |
| MAX_FRAGMENTS; |
| |
| @@ -158,7 +170,6 @@ int vchiq_platform_init(struct platform_ |
| } |
| |
| /* Send the base address of the slots to VideoCore */ |
| - channelbase = slot_phys; |
| err = rpi_firmware_property(fw, RPI_FIRMWARE_VCHIQ_INIT, |
| &channelbase, sizeof(channelbase)); |
| if (err || channelbase) { |
| @@ -244,7 +255,7 @@ vchiq_prepare_bulk_data(struct vchiq_bul |
| if (!pagelistinfo) |
| return VCHIQ_ERROR; |
| |
| - bulk->data = (void *)(unsigned long)pagelistinfo->dma_addr; |
| + bulk->data = (void *)VC_SAFE(pagelistinfo->dma_addr); |
| |
| /* |
| * Store the pagelistinfo address in remote_data, |
| @@ -522,25 +533,60 @@ create_pagelist(char __user *buf, size_t |
| |
| /* Combine adjacent blocks for performance */ |
| k = 0; |
| - for_each_sg(scatterlist, sg, dma_buffers, i) { |
| - u32 len = sg_dma_len(sg); |
| - u32 addr = sg_dma_address(sg); |
| - |
| - /* Note: addrs is the address + page_count - 1 |
| - * The firmware expects blocks after the first to be page- |
| - * aligned and a multiple of the page size |
| - */ |
| - WARN_ON(len == 0); |
| - WARN_ON(i && (i != (dma_buffers - 1)) && (len & ~PAGE_MASK)); |
| - WARN_ON(i && (addr & ~PAGE_MASK)); |
| - if (k > 0 && |
| - ((addrs[k - 1] & PAGE_MASK) + |
| - (((addrs[k - 1] & ~PAGE_MASK) + 1) << PAGE_SHIFT)) |
| - == (addr & PAGE_MASK)) |
| - addrs[k - 1] += ((len + PAGE_SIZE - 1) >> PAGE_SHIFT); |
| - else |
| - addrs[k++] = (addr & PAGE_MASK) | |
| - (((len + PAGE_SIZE - 1) >> PAGE_SHIFT) - 1); |
| + if (g_use_36bit_addrs) { |
| + for_each_sg(scatterlist, sg, dma_buffers, i) { |
| + u32 len = sg_dma_len(sg); |
| + u64 addr = sg_dma_address(sg); |
| + u32 page_id = (u32)((addr >> 4) & ~0xff); |
| + u32 sg_pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; |
| + |
| + /* Note: addrs is the address + page_count - 1 |
| + * The firmware expects blocks after the first to be page- |
| + * aligned and a multiple of the page size |
| + */ |
| + WARN_ON(len == 0); |
| + WARN_ON(i && |
| + (i != (dma_buffers - 1)) && (len & ~PAGE_MASK)); |
| + WARN_ON(i && (addr & ~PAGE_MASK)); |
| + WARN_ON(upper_32_bits(addr) > 0xf); |
| + if (k > 0 && |
| + ((addrs[k - 1] & ~0xff) + |
| + (((addrs[k - 1] & 0xff) + 1) << 8) |
| + == page_id)) { |
| + u32 inc_pages = min(sg_pages, |
| + 0xff - (addrs[k - 1] & 0xff)); |
| + addrs[k - 1] += inc_pages; |
| + page_id += inc_pages << 8; |
| + sg_pages -= inc_pages; |
| + } |
| + while (sg_pages) { |
| + u32 inc_pages = min(sg_pages, 0x100u); |
| + addrs[k++] = page_id | (inc_pages - 1); |
| + page_id += inc_pages << 8; |
| + sg_pages -= inc_pages; |
| + } |
| + } |
| + } else { |
| + for_each_sg(scatterlist, sg, dma_buffers, i) { |
| + u32 len = sg_dma_len(sg); |
| + u32 addr = VC_SAFE(sg_dma_address(sg)); |
| + u32 new_pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; |
| + |
| + /* Note: addrs is the address + page_count - 1 |
| + * The firmware expects blocks after the first to be page- |
| + * aligned and a multiple of the page size |
| + */ |
| + WARN_ON(len == 0); |
| + WARN_ON(i && (i != (dma_buffers - 1)) && (len & ~PAGE_MASK)); |
| + WARN_ON(i && (addr & ~PAGE_MASK)); |
| + if (k > 0 && |
| + ((addrs[k - 1] & PAGE_MASK) + |
| + (((addrs[k - 1] & ~PAGE_MASK) + 1) << PAGE_SHIFT)) |
| + == (addr & PAGE_MASK)) |
| + addrs[k - 1] += new_pages; |
| + else |
| + addrs[k++] = (addr & PAGE_MASK) | (new_pages - 1); |
| + } |
| } |
| |
| /* Partial cache lines (fragments) require special measures */ |
| --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c |
| +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c |
| @@ -149,6 +149,11 @@ static struct vchiq_drvdata bcm2836_drvd |
| .cache_line_size = 64, |
| }; |
| |
| +static struct vchiq_drvdata bcm2838_drvdata = { |
| + .cache_line_size = 64, |
| + .use_36bit_addrs = true, |
| +}; |
| + |
| static const char *const ioctl_names[] = { |
| "CONNECT", |
| "SHUTDOWN", |
| @@ -3164,6 +3169,7 @@ void vchiq_platform_conn_state_changed(s |
| static const struct of_device_id vchiq_of_match[] = { |
| { .compatible = "brcm,bcm2835-vchiq", .data = &bcm2835_drvdata }, |
| { .compatible = "brcm,bcm2836-vchiq", .data = &bcm2836_drvdata }, |
| + { .compatible = "brcm,bcm2838-vchiq", .data = &bcm2838_drvdata }, |
| {}, |
| }; |
| MODULE_DEVICE_TABLE(of, vchiq_of_match); |
| --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h |
| +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h |
| @@ -97,6 +97,7 @@ struct vchiq_arm_state { |
| |
| struct vchiq_drvdata { |
| const unsigned int cache_line_size; |
| + const bool use_36bit_addrs; |
| struct rpi_firmware *fw; |
| }; |
| |