From: Álvaro Fernández Rojas Date: Fri, 27 Dec 2024 16:14:40 +0000 (+0100) Subject: bcm27xx: pull 6.6 patches from RPi repo X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=692205305db14deeff1a2dc4a6d7f87e19fc418b;p=openwrt%2Fstaging%2Fnbd.git bcm27xx: pull 6.6 patches from RPi repo Adds latest 6.6 patches from the Raspberry Pi repository. These patches were generated from: https://github.com/raspberrypi/linux/commits/rpi-6.6.y/ With the following command: git format-patch -N v6.6.67..HEAD (HEAD -> 811ff707533bcd67cdcd368bbd46223082009b12) Signed-off-by: Álvaro Fernández Rojas --- diff --git a/target/linux/bcm27xx/bcm2708/config-6.6 b/target/linux/bcm27xx/bcm2708/config-6.6 index b587b5d719..a8a3cc9af7 100644 --- a/target/linux/bcm27xx/bcm2708/config-6.6 +++ b/target/linux/bcm27xx/bcm2708/config-6.6 @@ -314,6 +314,7 @@ CONFIG_PRINTK_TIME=y CONFIG_PTP_1588_CLOCK_OPTIONAL=y CONFIG_PWM=y CONFIG_PWM_BCM2835=y +CONFIG_PWM_GPIO=y CONFIG_PWM_SYSFS=y CONFIG_RANDSTRUCT_NONE=y CONFIG_RASPBERRYPI_FIRMWARE=y @@ -346,6 +347,7 @@ CONFIG_SERIAL_DEV_BUS=y # CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_RPI_FW=y CONFIG_SG_POOL=y CONFIG_SMSC_PHY=y CONFIG_SOFTIRQ_ON_OWN_STACK=y diff --git a/target/linux/bcm27xx/bcm2709/config-6.6 b/target/linux/bcm27xx/bcm2709/config-6.6 index 4a295d52c8..edcc8f58cc 100644 --- a/target/linux/bcm27xx/bcm2709/config-6.6 +++ b/target/linux/bcm27xx/bcm2709/config-6.6 @@ -398,6 +398,7 @@ CONFIG_PTP_1588_CLOCK=y CONFIG_PTP_1588_CLOCK_OPTIONAL=y CONFIG_PWM=y CONFIG_PWM_BCM2835=y +CONFIG_PWM_GPIO=y CONFIG_PWM_SYSFS=y CONFIG_RANDSTRUCT_NONE=y CONFIG_RAS=y @@ -435,6 +436,7 @@ CONFIG_SERIAL_DEV_BUS=y # CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_RPI_FW=y CONFIG_SG_POOL=y CONFIG_SMP=y CONFIG_SMP_ON_UP=y diff --git a/target/linux/bcm27xx/bcm2710/config-6.6 b/target/linux/bcm27xx/bcm2710/config-6.6 index 97f2f110b0..18a9c26b01 100644 --- a/target/linux/bcm27xx/bcm2710/config-6.6 +++ b/target/linux/bcm27xx/bcm2710/config-6.6 @@ -389,6 +389,7 @@ CONFIG_PRINTK_TIME=y CONFIG_PTP_1588_CLOCK_OPTIONAL=y CONFIG_PWM=y CONFIG_PWM_BCM2835=y +CONFIG_PWM_GPIO=y CONFIG_PWM_SYSFS=y CONFIG_QUEUED_RWLOCKS=y CONFIG_QUEUED_SPINLOCKS=y @@ -428,6 +429,7 @@ CONFIG_SERIAL_DEV_BUS=y # CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_RPI_FW=y CONFIG_SG_POOL=y CONFIG_SMP=y CONFIG_SMSC_PHY=y diff --git a/target/linux/bcm27xx/bcm2711/config-6.6 b/target/linux/bcm27xx/bcm2711/config-6.6 index f9b7469607..a48771b8e7 100644 --- a/target/linux/bcm27xx/bcm2711/config-6.6 +++ b/target/linux/bcm27xx/bcm2711/config-6.6 @@ -405,6 +405,7 @@ CONFIG_PTP_1588_CLOCK=y CONFIG_PTP_1588_CLOCK_OPTIONAL=y CONFIG_PWM=y CONFIG_PWM_BCM2835=y +CONFIG_PWM_GPIO=y CONFIG_PWM_SYSFS=y CONFIG_QUEUED_RWLOCKS=y CONFIG_QUEUED_SPINLOCKS=y @@ -445,6 +446,7 @@ CONFIG_SERIAL_DEV_BUS=y # CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_RPI_FW=y CONFIG_SG_POOL=y CONFIG_SMP=y CONFIG_SOCK_RX_QUEUE_MAPPING=y diff --git a/target/linux/bcm27xx/bcm2712/config-6.6 b/target/linux/bcm27xx/bcm2712/config-6.6 index 5ed564f5ab..9dac0688df 100644 --- a/target/linux/bcm27xx/bcm2712/config-6.6 +++ b/target/linux/bcm27xx/bcm2712/config-6.6 @@ -513,6 +513,7 @@ CONFIG_PTP_1588_CLOCK_OPTIONAL=y CONFIG_PWM=y CONFIG_PWM_BCM2835=y CONFIG_PWM_BRCMSTB=y +CONFIG_PWM_GPIO=y CONFIG_PWM_RP1=y CONFIG_PWM_SYSFS=y CONFIG_QUEUED_RWLOCKS=y @@ -577,6 +578,7 @@ CONFIG_SPARSEMEM_VMEMMAP=y CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y CONFIG_SPARSE_IRQ=y CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y +CONFIG_SRAM=y # CONFIG_STRIP_ASM_SYMS is not set CONFIG_SUSPEND=y CONFIG_SUSPEND_FREEZER=y diff --git a/target/linux/bcm27xx/config-6.6 b/target/linux/bcm27xx/config-6.6 index 50ca86f585..e69dbf5d74 100644 --- a/target/linux/bcm27xx/config-6.6 +++ b/target/linux/bcm27xx/config-6.6 @@ -28,6 +28,8 @@ # CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 is not set # CONFIG_RP1_PIO is not set # CONFIG_SENSORS_RP1_ADC is not set +# CONFIG_SERIAL_RPI_FW is not set +# CONFIG_SND_PIMIDI is not set # CONFIG_SPI_RP2040_GPIO_BRIDGE is not set # CONFIG_VIDEO_AD5398 is not set # CONFIG_VIDEO_ARDUCAM_64MP is not set diff --git a/target/linux/bcm27xx/patches-6.6/950-0297-staging-vchiq_arm-Add-36-bit-address-support.patch b/target/linux/bcm27xx/patches-6.6/950-0297-staging-vchiq_arm-Add-36-bit-address-support.patch deleted file mode 100644 index 11147d9fd9..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-0297-staging-vchiq_arm-Add-36-bit-address-support.patch +++ /dev/null @@ -1,256 +0,0 @@ -From fc5ed9d9bf0411523220bab60304da6d23257a64 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Thu, 1 Nov 2018 17:31:37 +0000 -Subject: [PATCH 0297/1085] staging: vchiq_arm: 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), giving a 36-bit address range. - -Manage the split between addresses for the VPU and addresses for the -40-bit DMA controller with a dedicated DMA device pointer that on non- -BCM2711 platforms is the same as the main VCHIQ device. This allows -the VCHIQ node to stay in the usual place in the DT. - -Signed-off-by: Phil Elwell ---- - .../interface/vchiq_arm/vchiq_arm.c | 125 +++++++++++++----- - 1 file changed, 90 insertions(+), 35 deletions(-) - ---- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c -+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c -@@ -73,6 +73,7 @@ static struct platform_device *bcm2835_i - - struct vchiq_drvdata { - const unsigned int cache_line_size; -+ const bool use_36bit_addrs; - struct rpi_firmware *fw; - }; - -@@ -118,6 +119,11 @@ struct vchiq_arm_state { - int first_connect; - }; - -+static struct vchiq_drvdata bcm2711_drvdata = { -+ .cache_line_size = 64, -+ .use_36bit_addrs = true, -+}; -+ - struct vchiq_pagelist_info { - struct pagelist *pagelist; - size_t pagelist_buffer_size; -@@ -142,10 +148,12 @@ static void __iomem *g_regs; - * of 32. - */ - static unsigned int g_cache_line_size = 32; -+static unsigned int g_use_36bit_addrs = 0; - static unsigned int g_fragments_size; - static char *g_fragments_base; - static char *g_free_fragments; - static struct semaphore g_free_fragments_sema; -+static struct device *g_dma_dev; - - static DEFINE_SEMAPHORE(g_free_fragments_mutex, 1); - -@@ -175,7 +183,7 @@ static void - cleanup_pagelistinfo(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagelistinfo) - { - if (pagelistinfo->scatterlist_mapped) { -- dma_unmap_sg(instance->state->dev, pagelistinfo->scatterlist, -+ dma_unmap_sg(g_dma_dev, pagelistinfo->scatterlist, - pagelistinfo->num_pages, pagelistinfo->dma_dir); - } - -@@ -335,7 +343,7 @@ create_pagelist(struct vchiq_instance *i - count -= len; - } - -- dma_buffers = dma_map_sg(instance->state->dev, -+ dma_buffers = dma_map_sg(g_dma_dev, - scatterlist, - num_pages, - pagelistinfo->dma_dir); -@@ -349,22 +357,61 @@ create_pagelist(struct vchiq_instance *i - - /* 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 (is_adjacent_block(addrs, addr, k)) -- 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 = 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 */ -@@ -408,7 +455,7 @@ free_pagelist(struct vchiq_instance *ins - * NOTE: dma_unmap_sg must be called before the - * cpu can touch any of the data/pages. - */ -- dma_unmap_sg(instance->state->dev, pagelistinfo->scatterlist, -+ dma_unmap_sg(g_dma_dev, pagelistinfo->scatterlist, - pagelistinfo->num_pages, pagelistinfo->dma_dir); - pagelistinfo->scatterlist_mapped = 0; - -@@ -463,6 +510,7 @@ free_pagelist(struct vchiq_instance *ins - static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) - { - struct device *dev = &pdev->dev; -+ struct device *dma_dev = NULL; - struct vchiq_drvdata *drvdata = platform_get_drvdata(pdev); - struct rpi_firmware *fw = drvdata->fw; - struct vchiq_slot_zero *vchiq_slot_zero; -@@ -484,6 +532,24 @@ static int vchiq_platform_init(struct pl - g_cache_line_size = drvdata->cache_line_size; - g_fragments_size = 2 * g_cache_line_size; - -+ if (drvdata->use_36bit_addrs) { -+ struct device_node *dma_node = -+ of_find_compatible_node(NULL, NULL, "brcm,bcm2711-dma"); -+ -+ if (dma_node) { -+ struct platform_device *pdev; -+ -+ pdev = of_find_device_by_node(dma_node); -+ if (pdev) -+ dma_dev = &pdev->dev; -+ of_node_put(dma_node); -+ g_use_36bit_addrs = true; -+ } else { -+ dev_err(dev, "40-bit DMA controller not found\n"); -+ return -EINVAL; -+ } -+ } -+ - /* 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); -@@ -496,13 +562,14 @@ static int vchiq_platform_init(struct pl - } - - WARN_ON(((unsigned long)slot_mem & (PAGE_SIZE - 1)) != 0); -+ channelbase = slot_phys; - - vchiq_slot_zero = vchiq_init_slots(slot_mem, slot_mem_size); - if (!vchiq_slot_zero) - return -ENOMEM; - - 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; - -@@ -536,7 +603,6 @@ static int vchiq_platform_init(struct pl - } - - /* 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) { -@@ -550,6 +616,8 @@ static int vchiq_platform_init(struct pl - return -ENXIO; - } - -+ g_dma_dev = dma_dev ?: dev; -+ - vchiq_log_info(vchiq_arm_log_level, "vchiq_init - done (slots %pK, phys %pad)", - vchiq_slot_zero, &slot_phys); - -@@ -1755,6 +1823,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,bcm2711-vchiq", .data = &bcm2711_drvdata }, - {}, - }; - MODULE_DEVICE_TABLE(of, vchiq_of_match); -@@ -1787,22 +1856,8 @@ vchiq_register_child(struct platform_dev - - child->dev.of_node = np; - -- /* -- * We want the dma-ranges etc to be copied from a device with the -- * correct dma-ranges for the VPU. -- * VCHIQ on Pi4 is now under scb which doesn't get those dma-ranges. -- * Take the "dma" node as going to be suitable as it sees the world -- * through the same eyes as the VPU. -- */ -- np = of_find_node_by_path("dma"); -- if (!np) -- np = pdev->dev.of_node; -- - of_dma_configure(&child->dev, np, true); - -- if (np != pdev->dev.of_node) -- of_node_put(np); -- - return child; - } - diff --git a/target/linux/bcm27xx/patches-6.6/950-0298-staging-vchiq_arm-children-inherit-DMA-config.patch b/target/linux/bcm27xx/patches-6.6/950-0298-staging-vchiq_arm-children-inherit-DMA-config.patch deleted file mode 100644 index 87bea65fc7..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-0298-staging-vchiq_arm-children-inherit-DMA-config.patch +++ /dev/null @@ -1,36 +0,0 @@ -From d4712f611e6d60dd9cf09df581f5df6fad6a2207 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Tue, 21 Jul 2020 17:34:09 +0100 -Subject: [PATCH 0298/1085] staging: vchiq_arm: children inherit DMA config - -Although it is no longer necessary for vchiq's children to have a -different DMA configuration to the parent, they do still need to -explicitly to have their DMA configuration set - to be that of the -parent. - -Signed-off-by: Phil Elwell ---- - .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 10 ++++++++++ - 1 file changed, 10 insertions(+) - ---- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c -+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c -@@ -1856,8 +1856,18 @@ vchiq_register_child(struct platform_dev - - child->dev.of_node = np; - -+ /* -+ * We want the dma-ranges etc to be copied from the parent VCHIQ device -+ * to be passed on to the children without a node of their own. -+ */ -+ if (!np) -+ np = pdev->dev.of_node; -+ - of_dma_configure(&child->dev, np, true); - -+ if (np != pdev->dev.of_node) -+ of_node_put(np); -+ - return child; - } - diff --git a/target/linux/bcm27xx/patches-6.6/950-0299-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch b/target/linux/bcm27xx/patches-6.6/950-0299-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch deleted file mode 100644 index 162d333c61..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-0299-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch +++ /dev/null @@ -1,118 +0,0 @@ -From 9f328c347fc9a5495b8383aa2bae1d3bc242a2ab Mon Sep 17 00:00:00 2001 -From: detule -Date: Tue, 2 Oct 2018 04:10:08 -0400 -Subject: [PATCH 0299/1085] staging: vchiq_arm: Usa a DMA pool for small bulks - -During a bulk transfer we request a DMA allocation to hold the -scatter-gather list. Most of the time, this allocation is small -(<< PAGE_SIZE), however it can be requested at a high enough frequency -to cause fragmentation and/or stress the CMA allocator (think time -spent in compaction here, or during allocations elsewhere). - -Implement a pool to serve up small DMA allocations, falling back -to a coherent allocation if the request is greater than -VCHIQ_DMA_POOL_SIZE. - -Signed-off-by: Oliver Gjoneski ---- - .../interface/vchiq_arm/vchiq_arm.c | 33 ++++++++++++++++--- - 1 file changed, 29 insertions(+), 4 deletions(-) - ---- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c -+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c -@@ -22,6 +22,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -51,6 +52,8 @@ - - #define ARM_DS_ACTIVE BIT(2) - -+#define VCHIQ_DMA_POOL_SIZE PAGE_SIZE -+ - /* Override the default prefix, which would be vchiq_arm (from the filename) */ - #undef MODULE_PARAM_PREFIX - #define MODULE_PARAM_PREFIX DEVICE_NAME "." -@@ -128,6 +131,7 @@ struct vchiq_pagelist_info { - struct pagelist *pagelist; - size_t pagelist_buffer_size; - dma_addr_t dma_addr; -+ bool is_from_pool; - enum dma_data_direction dma_dir; - unsigned int num_pages; - unsigned int pages_need_release; -@@ -148,6 +152,7 @@ static void __iomem *g_regs; - * of 32. - */ - 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; -@@ -190,8 +195,13 @@ cleanup_pagelistinfo(struct vchiq_instan - if (pagelistinfo->pages_need_release) - unpin_user_pages(pagelistinfo->pages, pagelistinfo->num_pages); - -- dma_free_coherent(instance->state->dev, pagelistinfo->pagelist_buffer_size, -- pagelistinfo->pagelist, pagelistinfo->dma_addr); -+ if (pagelistinfo->is_from_pool) { -+ dma_pool_free(g_dma_pool, pagelistinfo->pagelist, -+ pagelistinfo->dma_addr); -+ } else { -+ dma_free_coherent(instance->state->dev, pagelistinfo->pagelist_buffer_size, -+ pagelistinfo->pagelist, pagelistinfo->dma_addr); -+ } - } - - static inline bool -@@ -226,6 +236,7 @@ create_pagelist(struct vchiq_instance *i - u32 *addrs; - unsigned int num_pages, offset, i, k; - int actual_pages; -+ bool is_from_pool; - size_t pagelist_size; - struct scatterlist *scatterlist, *sg; - int dma_buffers; -@@ -255,8 +266,14 @@ create_pagelist(struct vchiq_instance *i - /* Allocate enough storage to hold the page pointers and the page - * list - */ -- pagelist = dma_alloc_coherent(instance->state->dev, pagelist_size, &dma_addr, -- GFP_KERNEL); -+ if (pagelist_size > VCHIQ_DMA_POOL_SIZE) { -+ pagelist = dma_alloc_coherent(instance->state->dev, pagelist_size, &dma_addr, -+ GFP_KERNEL); -+ is_from_pool = false; -+ } else { -+ pagelist = dma_pool_alloc(g_dma_pool, GFP_KERNEL, &dma_addr); -+ is_from_pool = true; -+ } - - vchiq_log_trace(vchiq_arm_log_level, "%s - %pK", __func__, pagelist); - -@@ -277,6 +294,7 @@ create_pagelist(struct vchiq_instance *i - pagelistinfo->pagelist = pagelist; - pagelistinfo->pagelist_buffer_size = pagelist_size; - pagelistinfo->dma_addr = dma_addr; -+ pagelistinfo->is_from_pool = is_from_pool; - pagelistinfo->dma_dir = (type == PAGELIST_WRITE) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE; - pagelistinfo->num_pages = num_pages; -@@ -617,6 +635,13 @@ static int vchiq_platform_init(struct pl - } - - g_dma_dev = dma_dev ?: dev; -+ g_dma_pool = dmam_pool_create("vchiq_scatter_pool", dev, -+ VCHIQ_DMA_POOL_SIZE, g_cache_line_size, -+ 0); -+ if (!g_dma_pool) { -+ dev_err(dev, "failed to create dma pool"); -+ return -ENOMEM; -+ } - - vchiq_log_info(vchiq_arm_log_level, "vchiq_init - done (slots %pK, phys %pad)", - vchiq_slot_zero, &slot_phys); diff --git a/target/linux/bcm27xx/patches-6.6/950-0365-staging-vchiq_arm-Add-log_level-module-params.patch b/target/linux/bcm27xx/patches-6.6/950-0365-staging-vchiq_arm-Add-log_level-module-params.patch deleted file mode 100644 index 51815f2619..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-0365-staging-vchiq_arm-Add-log_level-module-params.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 79f24f7454a416fed9106c75ea9b3be480465dda Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Fri, 29 Apr 2022 09:19:10 +0100 -Subject: [PATCH 0365/1085] staging: vchiq_arm: Add log_level module params - -Add module parameters to control the logging levels for the various -vchiq logging categories. - -Signed-off-by: Phil Elwell ---- - .../staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 5 +++++ - 1 file changed, 5 insertions(+) - ---- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c -+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c -@@ -64,6 +64,11 @@ - /* Run time control of log level, based on KERN_XXX level. */ - int vchiq_arm_log_level = VCHIQ_LOG_DEFAULT; - int vchiq_susp_log_level = VCHIQ_LOG_ERROR; -+module_param_named(arm_log_level, vchiq_arm_log_level, int, 0644); -+module_param_named(susp_log_level, vchiq_susp_log_level, int, 0644); -+module_param_named(core_log_level, vchiq_core_log_level, int, 0644); -+module_param_named(core_msg_log_level, vchiq_core_msg_log_level, int, 0644); -+module_param_named(sync_log_level, vchiq_sync_log_level, int, 0644); - - DEFINE_SPINLOCK(msg_queue_spinlock); - struct vchiq_state g_state; diff --git a/target/linux/bcm27xx/patches-6.6/950-0998-i2c-designware-Add-support-for-bus-clear-feature.patch b/target/linux/bcm27xx/patches-6.6/950-0998-i2c-designware-Add-support-for-bus-clear-feature.patch deleted file mode 100644 index 691484d46e..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-0998-i2c-designware-Add-support-for-bus-clear-feature.patch +++ /dev/null @@ -1,157 +0,0 @@ -From 24cb07b0c0724a22e474d12e7c2d5b834bf3b076 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Tue, 26 Mar 2024 15:57:46 +0000 -Subject: [PATCH 0998/1085] i2c: designware: Add support for bus clear feature - -Newer versions of the DesignWare I2C block support the detection of -stuck signals, and a mechanism to recover from them. Add the required -software support to the driver. - -This change was prompted by the observation that reading a single byte -from register 0 of a VEML7700 seems to cause it to issue an ACK too -early, and the controller to complain about losing arbitration. There -is a suspicion that this may be a more widespread problem, but at least -this patch prevents the bus from locking up. - -See: https://github.com/raspberrypi/linux/issues/6057 - -Signed-off-by: Phil Elwell ---- - drivers/i2c/busses/i2c-designware-common.c | 12 ++++++++++++ - drivers/i2c/busses/i2c-designware-core.h | 8 ++++++++ - drivers/i2c/busses/i2c-designware-master.c | 19 ++++++++++++++++++- - 3 files changed, 38 insertions(+), 1 deletion(-) - ---- a/drivers/i2c/busses/i2c-designware-common.c -+++ b/drivers/i2c/busses/i2c-designware-common.c -@@ -57,6 +57,8 @@ static char *abort_sources[] = { - "slave lost the bus while transmitting data to a remote master", - [ABRT_SLAVE_RD_INTX] = - "incorrect slave-transmitter mode configuration", -+ [ABRT_SLAVE_SDA_STUCK_AT_LOW] = -+ "SDA stuck at low", - }; - - static int dw_reg_read(void *context, unsigned int reg, unsigned int *val) -@@ -609,8 +611,16 @@ int i2c_dw_wait_bus_not_busy(struct dw_i - int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev) - { - unsigned long abort_source = dev->abort_source; -+ unsigned int reg; - int i; - -+ if (abort_source & DW_IC_TX_ABRT_SLAVE_SDA_STUCK_AT_LOW) { -+ regmap_write(dev->map, DW_IC_ENABLE, -+ DW_IC_ENABLE_ENABLE | DW_IC_ENABLE_BUS_RECOVERY); -+ regmap_read_poll_timeout(dev->map, DW_IC_ENABLE, reg, -+ !(reg & DW_IC_ENABLE_BUS_RECOVERY), -+ 1100, 200000); -+ } - if (abort_source & DW_IC_TX_ABRT_NOACK) { - for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) - dev_dbg(dev->dev, -@@ -625,6 +635,8 @@ int i2c_dw_handle_tx_abort(struct dw_i2c - return -EAGAIN; - else if (abort_source & DW_IC_TX_ABRT_GCALL_READ) - return -EINVAL; /* wrong msgs[] data */ -+ else if (abort_source & DW_IC_TX_ABRT_SLAVE_SDA_STUCK_AT_LOW) -+ return -EREMOTEIO; - else - return -EIO; - } ---- a/drivers/i2c/busses/i2c-designware-core.h -+++ b/drivers/i2c/busses/i2c-designware-core.h -@@ -79,9 +79,12 @@ - #define DW_IC_TX_ABRT_SOURCE 0x80 - #define DW_IC_ENABLE_STATUS 0x9c - #define DW_IC_CLR_RESTART_DET 0xa8 -+#define DW_IC_SCL_STUCK_AT_LOW_TIMEOUT 0xac -+#define DW_IC_SDA_STUCK_AT_LOW_TIMEOUT 0xb0 - #define DW_IC_COMP_PARAM_1 0xf4 - #define DW_IC_COMP_VERSION 0xf8 - #define DW_IC_SDA_HOLD_MIN_VERS 0x3131312A /* "111*" == v1.11* */ -+#define DW_IC_BUS_CLEAR_MIN_VERS 0x3230302A /* "200*" == v2.00* */ - #define DW_IC_COMP_TYPE 0xfc - #define DW_IC_COMP_TYPE_VALUE 0x44570140 /* "DW" + 0x0140 */ - -@@ -111,6 +114,7 @@ - - #define DW_IC_ENABLE_ENABLE BIT(0) - #define DW_IC_ENABLE_ABORT BIT(1) -+#define DW_IC_ENABLE_BUS_RECOVERY BIT(3) - - #define DW_IC_STATUS_ACTIVITY BIT(0) - #define DW_IC_STATUS_TFE BIT(2) -@@ -118,6 +122,7 @@ - #define DW_IC_STATUS_MASTER_ACTIVITY BIT(5) - #define DW_IC_STATUS_SLAVE_ACTIVITY BIT(6) - #define DW_IC_STATUS_MASTER_HOLD_TX_FIFO_EMPTY BIT(7) -+#define DW_IC_STATUS_SDA_STUCK_NOT_RECOVERED BIT(11) - - #define DW_IC_SDA_HOLD_RX_SHIFT 16 - #define DW_IC_SDA_HOLD_RX_MASK GENMASK(23, 16) -@@ -165,6 +170,7 @@ - #define ABRT_SLAVE_FLUSH_TXFIFO 13 - #define ABRT_SLAVE_ARBLOST 14 - #define ABRT_SLAVE_RD_INTX 15 -+#define ABRT_SLAVE_SDA_STUCK_AT_LOW 17 - - #define DW_IC_TX_ABRT_7B_ADDR_NOACK BIT(ABRT_7B_ADDR_NOACK) - #define DW_IC_TX_ABRT_10ADDR1_NOACK BIT(ABRT_10ADDR1_NOACK) -@@ -180,6 +186,7 @@ - #define DW_IC_RX_ABRT_SLAVE_RD_INTX BIT(ABRT_SLAVE_RD_INTX) - #define DW_IC_RX_ABRT_SLAVE_ARBLOST BIT(ABRT_SLAVE_ARBLOST) - #define DW_IC_RX_ABRT_SLAVE_FLUSH_TXFIFO BIT(ABRT_SLAVE_FLUSH_TXFIFO) -+#define DW_IC_TX_ABRT_SLAVE_SDA_STUCK_AT_LOW BIT(ABRT_SLAVE_SDA_STUCK_AT_LOW) - - #define DW_IC_TX_ABRT_NOACK (DW_IC_TX_ABRT_7B_ADDR_NOACK | \ - DW_IC_TX_ABRT_10ADDR1_NOACK | \ ---- a/drivers/i2c/busses/i2c-designware-master.c -+++ b/drivers/i2c/busses/i2c-designware-master.c -@@ -212,6 +212,7 @@ static int i2c_dw_set_timings_master(str - */ - static int i2c_dw_init_master(struct dw_i2c_dev *dev) - { -+ unsigned int timeout = 0; - int ret; - - ret = i2c_dw_acquire_lock(dev); -@@ -235,6 +236,17 @@ static int i2c_dw_init_master(struct dw_ - regmap_write(dev->map, DW_IC_HS_SCL_LCNT, dev->hs_lcnt); - } - -+ if (dev->master_cfg & DW_IC_CON_BUS_CLEAR_CTRL) { -+ /* Set a sensible timeout if not already configured */ -+ regmap_read(dev->map, DW_IC_SDA_STUCK_AT_LOW_TIMEOUT, &timeout); -+ if (timeout == ~0) { -+ /* Use 10ms as a timeout, which is 1000 cycles at 100kHz */ -+ timeout = i2c_dw_clk_rate(dev) * 10; /* clock rate is in kHz */ -+ regmap_write(dev->map, DW_IC_SDA_STUCK_AT_LOW_TIMEOUT, timeout); -+ regmap_write(dev->map, DW_IC_SCL_STUCK_AT_LOW_TIMEOUT, timeout); -+ } -+ } -+ - /* Write SDA hold time if supported */ - if (dev->sda_hold_time) - regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time); -@@ -1071,6 +1083,7 @@ int i2c_dw_probe_master(struct dw_i2c_de - struct i2c_adapter *adap = &dev->adapter; - unsigned long irq_flags; - unsigned int ic_con; -+ unsigned int id_ver; - int ret; - - init_completion(&dev->cmd_complete); -@@ -1106,7 +1119,11 @@ int i2c_dw_probe_master(struct dw_i2c_de - if (ret) - return ret; - -- if (ic_con & DW_IC_CON_BUS_CLEAR_CTRL) -+ ret = regmap_read(dev->map, DW_IC_COMP_VERSION, &id_ver); -+ if (ret) -+ return ret; -+ -+ if (ic_con & DW_IC_CON_BUS_CLEAR_CTRL || id_ver >= DW_IC_BUS_CLEAR_MIN_VERS) - dev->master_cfg |= DW_IC_CON_BUS_CLEAR_CTRL; - - ret = dev->init(dev); diff --git a/target/linux/bcm27xx/patches-6.6/950-1267-dts-rp1-Disable-DMA-usage-for-UART0.patch b/target/linux/bcm27xx/patches-6.6/950-1267-dts-rp1-Disable-DMA-usage-for-UART0.patch deleted file mode 100644 index c809c4f10b..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-1267-dts-rp1-Disable-DMA-usage-for-UART0.patch +++ /dev/null @@ -1,34 +0,0 @@ -From cc63d552b9aab92fb581dfb08267d5af697f477b Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Wed, 18 Sep 2024 16:45:24 +0100 -Subject: [PATCH 1267/1350] dts: rp1: Disable DMA usage for UART0 - -Some recent DMA changes have led to data loss in UART0 on Pi 5. It also -seems that even prior to these changes there was a problem with aborted -transfers. - -As this is the only RP1 UART configured for DMA, it is better to remove -the DMA usage until it is shown to be reliable. - -Link: https://github.com/raspberrypi/linux/issues/6365 - -Signed-off-by: Phil Elwell ---- - arch/arm64/boot/dts/broadcom/rp1.dtsi | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - ---- a/arch/arm64/boot/dts/broadcom/rp1.dtsi -+++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi -@@ -55,9 +55,9 @@ - interrupts = ; - clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>; - clock-names = "uartclk", "apb_pclk"; -- dmas = <&rp1_dma RP1_DMA_UART0_TX>, -- <&rp1_dma RP1_DMA_UART0_RX>; -- dma-names = "tx", "rx"; -+ // dmas = <&rp1_dma RP1_DMA_UART0_TX>, -+ // <&rp1_dma RP1_DMA_UART0_RX>; -+ // dma-names = "tx", "rx"; - pinctrl-names = "default"; - arm,primecell-periphid = <0x00541011>; - uart-has-rtscts; diff --git a/target/linux/bcm27xx/patches-6.6/950-1351-drivers-media-bcm2835_isp-Cache-LS-table-dmabuf.patch b/target/linux/bcm27xx/patches-6.6/950-1351-drivers-media-bcm2835_isp-Cache-LS-table-dmabuf.patch new file mode 100644 index 0000000000..1f5ed7545b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1351-drivers-media-bcm2835_isp-Cache-LS-table-dmabuf.patch @@ -0,0 +1,141 @@ +From 25e6acfe00f589a5989ebd2c8d21a130fb3bf106 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +Date: Fri, 18 Oct 2024 09:18:10 +0100 +Subject: [PATCH] drivers: media: bcm2835_isp: Cache LS table dmabuf + +Clients such as libcamera do not change the LS table dmabuf on every +frame. In such cases instead of mapping/remapping the same dmabuf on +every frame to send to the firmware, cache the dmabuf once and only +update and remap if the dmabuf has been changed by the userland client. + +Signed-off-by: Naushir Patuck +--- + .../bcm2835-isp/bcm2835-v4l2-isp.c | 77 +++++++++++-------- + 1 file changed, 46 insertions(+), 31 deletions(-) + +--- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c ++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c +@@ -139,6 +139,8 @@ struct bcm2835_isp_dev { + /* Image pipeline controls. */ + int r_gain; + int b_gain; ++ struct dma_buf *last_ls_dmabuf; ++ struct mmal_parameter_lens_shading_v2 ls; + }; + + struct bcm2835_isp_buffer { +@@ -657,18 +659,18 @@ static void bcm2835_isp_node_stop_stream + atomic_dec(&dev->num_streaming); + /* If all ports disabled, then disable the component */ + if (atomic_read(&dev->num_streaming) == 0) { +- struct bcm2835_isp_lens_shading ls; + /* + * The ISP component on the firmware has a reference to the + * dmabuf handle for the lens shading table. Pass a null handle + * to remove that reference now. + */ +- memset(&ls, 0, sizeof(ls)); ++ memset(&dev->ls, 0, sizeof(dev->ls)); + /* Must set a valid grid size for the FW */ +- ls.grid_cell_size = 16; ++ dev->ls.grid_cell_size = 16; + set_isp_param(&dev->node[0], + MMAL_PARAMETER_LENS_SHADING_OVERRIDE, +- &ls, sizeof(ls)); ++ &dev->ls, sizeof(dev->ls)); ++ dev->last_ls_dmabuf = NULL; + + ret = vchiq_mmal_component_disable(dev->mmal_instance, + dev->component); +@@ -719,6 +721,36 @@ static inline unsigned int get_sizeimage + return (bpl * height * fmt->size_multiplier_x2) >> 1; + } + ++static int map_ls_table(struct bcm2835_isp_dev *dev, struct dma_buf *dmabuf, ++ const struct bcm2835_isp_lens_shading *v4l2_ls) ++{ ++ void *vcsm_handle; ++ int ret; ++ ++ if (IS_ERR_OR_NULL(dmabuf)) ++ return -EINVAL; ++ ++ /* ++ * struct bcm2835_isp_lens_shading and struct ++ * mmal_parameter_lens_shading_v2 match so that we can do a ++ * simple memcpy here. ++ * Only the dmabuf to the actual table needs any manipulation. ++ */ ++ memcpy(&dev->ls, v4l2_ls, sizeof(dev->ls)); ++ ret = vc_sm_cma_import_dmabuf(dmabuf, &vcsm_handle); ++ if (ret) { ++ dma_buf_put(dmabuf); ++ return ret; ++ } ++ ++ dev->ls.mem_handle_table = vc_sm_cma_int_handle(vcsm_handle); ++ dev->last_ls_dmabuf = dmabuf; ++ ++ vc_sm_cma_free(vcsm_handle); ++ ++ return 0; ++} ++ + static int bcm2835_isp_s_ctrl(struct v4l2_ctrl *ctrl) + { + struct bcm2835_isp_dev *dev = +@@ -754,44 +786,27 @@ static int bcm2835_isp_s_ctrl(struct v4l + case V4L2_CID_USER_BCM2835_ISP_LENS_SHADING: + { + struct bcm2835_isp_lens_shading *v4l2_ls; +- struct mmal_parameter_lens_shading_v2 ls; +- struct dma_buf *dmabuf; +- void *vcsm_handle; + + v4l2_ls = (struct bcm2835_isp_lens_shading *)ctrl->p_new.p_u8; +- /* +- * struct bcm2835_isp_lens_shading and struct +- * mmal_parameter_lens_shading_v2 match so that we can do a +- * simple memcpy here. +- * Only the dmabuf to the actual table needs any manipulation. +- */ +- memcpy(&ls, v4l2_ls, sizeof(ls)); ++ struct dma_buf *dmabuf = dma_buf_get(v4l2_ls->dmabuf); + +- dmabuf = dma_buf_get(v4l2_ls->dmabuf); +- if (IS_ERR_OR_NULL(dmabuf)) +- return -EINVAL; +- +- ret = vc_sm_cma_import_dmabuf(dmabuf, &vcsm_handle); +- if (ret) { +- dma_buf_put(dmabuf); +- return -EINVAL; +- } ++ if (dmabuf != dev->last_ls_dmabuf) ++ ret = map_ls_table(dev, dmabuf, v4l2_ls); + +- ls.mem_handle_table = vc_sm_cma_int_handle(vcsm_handle); +- if (ls.mem_handle_table) +- /* The VPU will take a reference on the vcsm handle, ++ if (!ret && dev->ls.mem_handle_table) ++ /* ++ * The VPU will take a reference on the vcsm handle, + * which in turn will retain a reference on the dmabuf. + * This code can therefore safely release all + * references to the buffer. + */ +- ret = set_isp_param(node, +- MMAL_PARAMETER_LENS_SHADING_OVERRIDE, +- &ls, +- sizeof(ls)); ++ ret = ++ set_isp_param(node, ++ MMAL_PARAMETER_LENS_SHADING_OVERRIDE, ++ &dev->ls, sizeof(dev->ls)); + else + ret = -EINVAL; + +- vc_sm_cma_free(vcsm_handle); + dma_buf_put(dmabuf); + break; + } diff --git a/target/linux/bcm27xx/patches-6.6/950-1351-mailbox-Add-RP1-mailbox-support.patch b/target/linux/bcm27xx/patches-6.6/950-1351-mailbox-Add-RP1-mailbox-support.patch deleted file mode 100644 index 23bdb6a8fb..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-1351-mailbox-Add-RP1-mailbox-support.patch +++ /dev/null @@ -1,252 +0,0 @@ -From 0d58d8cfb6f989f290d983552fcaa116e582e84a Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Thu, 31 Oct 2024 17:33:38 +0000 -Subject: [PATCH] mailbox: Add RP1 mailbox support - -The Raspberry Pi RP1 includes 2 M3 cores running firmware. This driver -adds a mailbox communication channel to them via a doorbell and some -shared memory. - -Signed-off-by: Phil Elwell ---- - drivers/mailbox/Kconfig | 9 ++ - drivers/mailbox/Makefile | 2 + - drivers/mailbox/rp1-mailbox.c | 208 ++++++++++++++++++++++++++++++++++ - 3 files changed, 219 insertions(+) - create mode 100644 drivers/mailbox/rp1-mailbox.c - ---- a/drivers/mailbox/Kconfig -+++ b/drivers/mailbox/Kconfig -@@ -295,4 +295,13 @@ config QCOM_IPCC - acts as an interrupt controller for receiving interrupts from clients. - Say Y here if you want to build this driver. - -+config MBOX_RP1 -+ tristate "RP1 Mailbox" -+ depends on MFD_RP1 -+ help -+ An implementation of a mailbox interface to the Raspberry Pi RP1 I/O -+ interface. Although written as a mailbox driver, the hardware only -+ provides an array of 32 doorbells. -+ Say Y here if you want to use the RP1 Mailbox. -+ - endif ---- a/drivers/mailbox/Makefile -+++ b/drivers/mailbox/Makefile -@@ -62,3 +62,5 @@ obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox - obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o - - obj-$(CONFIG_APPLE_MAILBOX) += apple-mailbox.o -+ -+obj-$(CONFIG_MBOX_RP1) += rp1-mailbox.o ---- /dev/null -+++ b/drivers/mailbox/rp1-mailbox.c -@@ -0,0 +1,208 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (C) 2023 Raspberry Pi Ltd. -+ * -+ * Parts of this driver are based on: -+ * - bcm2835-mailbox.c -+ * Copyright (C) 2010,2015 Broadcom -+ * Copyright (C) 2013-2014 Lubomir Rintel -+ * Copyright (C) 2013 Craig McGeachie -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * RP1's PROC_EVENTS register can generate interrupts on the M3 cores (when -+ * enabled). The 32-bit register is treated as 32 events, all of which share a -+ * common interrupt. HOST_EVENTS is the same in the reverse direction. -+ */ -+#define SYSCFG_PROC_EVENTS 0x00000008 -+#define SYSCFG_HOST_EVENTS 0x0000000c -+#define SYSCFG_HOST_EVENT_IRQ_EN 0x00000010 -+#define SYSCFG_HOST_EVENT_IRQ 0x00000014 -+ -+#define HW_SET_BITS 0x00002000 -+#define HW_CLR_BITS 0x00003000 -+ -+#define MAX_CHANS 4 /* 32 is the hardware limit */ -+ -+struct rp1_mbox { -+ void __iomem *regs; -+ unsigned int irq; -+ struct mbox_controller controller; -+}; -+ -+static struct rp1_mbox *rp1_chan_mbox(struct mbox_chan *chan) -+{ -+ return container_of(chan->mbox, struct rp1_mbox, controller); -+} -+ -+static unsigned int rp1_chan_event(struct mbox_chan *chan) -+{ -+ return (unsigned int)(uintptr_t)chan->con_priv; -+} -+ -+static irqreturn_t rp1_mbox_irq(int irq, void *dev_id) -+{ -+ struct rp1_mbox *mbox = dev_id; -+ struct mbox_chan *chan; -+ unsigned int doorbell; -+ unsigned int evs; -+ -+ evs = readl(mbox->regs + SYSCFG_HOST_EVENT_IRQ); -+ writel(evs, mbox->regs + SYSCFG_HOST_EVENTS + HW_CLR_BITS); -+ -+ while (evs) { -+ doorbell = __ffs(evs); -+ chan = &mbox->controller.chans[doorbell]; -+ mbox_chan_received_data(chan, NULL); -+ evs &= ~(1 << doorbell); -+ } -+ return IRQ_HANDLED; -+} -+ -+static int rp1_send_data(struct mbox_chan *chan, void *data) -+{ -+ struct rp1_mbox *mbox = rp1_chan_mbox(chan); -+ unsigned int event = rp1_chan_event(chan); -+ -+ writel(event, mbox->regs + SYSCFG_PROC_EVENTS + HW_SET_BITS); -+ -+ return 0; -+} -+ -+static int rp1_startup(struct mbox_chan *chan) -+{ -+ struct rp1_mbox *mbox = rp1_chan_mbox(chan); -+ unsigned int event = rp1_chan_event(chan); -+ -+ writel(event, mbox->regs + SYSCFG_HOST_EVENT_IRQ_EN + HW_SET_BITS); -+ -+ return 0; -+} -+ -+static void rp1_shutdown(struct mbox_chan *chan) -+{ -+ struct rp1_mbox *mbox = rp1_chan_mbox(chan); -+ unsigned int event = rp1_chan_event(chan); -+ -+ writel(event, mbox->regs + SYSCFG_HOST_EVENT_IRQ_EN + HW_CLR_BITS); -+} -+ -+static bool rp1_last_tx_done(struct mbox_chan *chan) -+{ -+ struct rp1_mbox *mbox = rp1_chan_mbox(chan); -+ unsigned int event = rp1_chan_event(chan); -+ unsigned int evs; -+ -+ evs = readl(mbox->regs + SYSCFG_HOST_EVENT_IRQ); -+ -+ return !(evs & event); -+} -+ -+static const struct mbox_chan_ops rp1_mbox_chan_ops = { -+ .send_data = rp1_send_data, -+ .startup = rp1_startup, -+ .shutdown = rp1_shutdown, -+ .last_tx_done = rp1_last_tx_done -+}; -+ -+static struct mbox_chan *rp1_mbox_xlate(struct mbox_controller *mbox, -+ const struct of_phandle_args *spec) -+{ -+ struct mbox_chan *chan; -+ unsigned int doorbell; -+ -+ if (spec->args_count != 1) -+ return ERR_PTR(-EINVAL); -+ -+ doorbell = spec->args[0]; -+ if (doorbell >= MAX_CHANS) -+ return ERR_PTR(-EINVAL); -+ -+ chan = &mbox->chans[doorbell]; -+ if (chan->con_priv) -+ return ERR_PTR(-EBUSY); -+ -+ chan->con_priv = (void *)(uintptr_t)(1 << doorbell); -+ -+ return chan; -+} -+ -+static int rp1_mbox_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct mbox_chan *chans; -+ struct rp1_mbox *mbox; -+ int ret = 0; -+ -+ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); -+ if (mbox == NULL) -+ return -ENOMEM; -+ -+ ret = devm_request_irq(dev, platform_get_irq(pdev, 0), -+ rp1_mbox_irq, 0, dev_name(dev), mbox); -+ if (ret) { -+ dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n", -+ ret); -+ return -ENODEV; -+ } -+ -+ mbox->regs = devm_platform_ioremap_resource(pdev, 0); -+ if (IS_ERR(mbox->regs)) { -+ ret = PTR_ERR(mbox->regs); -+ return ret; -+ } -+ -+ chans = devm_kcalloc(dev, MAX_CHANS, sizeof(*chans), GFP_KERNEL); -+ if (!chans) -+ return -ENOMEM; -+ -+ mbox->controller.txdone_poll = true; -+ mbox->controller.txpoll_period = 5; -+ mbox->controller.ops = &rp1_mbox_chan_ops; -+ mbox->controller.of_xlate = &rp1_mbox_xlate; -+ mbox->controller.dev = dev; -+ mbox->controller.num_chans = MAX_CHANS; -+ mbox->controller.chans = chans; -+ -+ ret = devm_mbox_controller_register(dev, &mbox->controller); -+ if (ret) -+ return ret; -+ -+ platform_set_drvdata(pdev, mbox); -+ -+ return 0; -+} -+ -+static const struct of_device_id rp1_mbox_of_match[] = { -+ { .compatible = "raspberrypi,rp1-mbox", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, rp1_mbox_of_match); -+ -+static struct platform_driver rp1_mbox_driver = { -+ .driver = { -+ .name = "rp1-mbox", -+ .of_match_table = rp1_mbox_of_match, -+ }, -+ .probe = rp1_mbox_probe, -+}; -+ -+module_platform_driver(rp1_mbox_driver); -+ -+MODULE_AUTHOR("Phil Elwell "); -+MODULE_DESCRIPTION("RP1 mailbox IPC driver"); -+MODULE_LICENSE("GPL v2"); diff --git a/target/linux/bcm27xx/patches-6.6/950-1352-firmware-Add-an-RP1-firmware-interface.patch b/target/linux/bcm27xx/patches-6.6/950-1352-firmware-Add-an-RP1-firmware-interface.patch deleted file mode 100644 index 3a9eb03c94..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-1352-firmware-Add-an-RP1-firmware-interface.patch +++ /dev/null @@ -1,421 +0,0 @@ -From 67daeadcaa7cee1f4b9df7aa108d199e73f35451 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Thu, 31 Oct 2024 17:36:54 +0000 -Subject: [PATCH] firmware: Add an RP1 firmware interface over mbox - -The RP1 firmware runs a simple communications channel over some shared -memory and a mailbox. This driver provides access to that channel. - -Signed-off-by: Phil Elwell ---- - drivers/firmware/Kconfig | 9 + - drivers/firmware/Makefile | 1 + - drivers/firmware/rp1.c | 316 +++++++++++++++++++++++++++++++++++ - include/linux/rp1-firmware.h | 53 ++++++ - 4 files changed, 379 insertions(+) - create mode 100644 drivers/firmware/rp1.c - create mode 100644 include/linux/rp1-firmware.h - ---- a/drivers/firmware/Kconfig -+++ b/drivers/firmware/Kconfig -@@ -153,6 +153,15 @@ config RASPBERRYPI_FIRMWARE - This option enables support for communicating with the firmware on the - Raspberry Pi. - -+config FIRMWARE_RP1 -+ tristate "RP1 Firmware Driver" -+ depends on MBOX_RP1 -+ help -+ The Raspberry Pi RP1 processor presents a firmware -+ interface using shared memory and a mailbox. To enable -+ the driver that communicates with it, say Y. Otherwise, -+ say N. -+ - config FW_CFG_SYSFS - tristate "QEMU fw_cfg device support in sysfs" - depends on SYSFS && (ARM || ARM64 || PARISC || PPC_PMAC || SPARC || X86) ---- a/drivers/firmware/Makefile -+++ b/drivers/firmware/Makefile -@@ -17,6 +17,7 @@ obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o - obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o - obj-$(CONFIG_MTK_ADSP_IPC) += mtk-adsp-ipc.o - obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o -+obj-$(CONFIG_FIRMWARE_RP1) += rp1.o - obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o - obj-$(CONFIG_QCOM_SCM) += qcom-scm.o - qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o ---- /dev/null -+++ b/drivers/firmware/rp1.c -@@ -0,0 +1,316 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (C) 2023-24 Raspberry Pi Ltd. -+ * -+ * Parts of this driver are based on: -+ * - raspberrypi.c, by Eric Anholt -+ * Copyright (C) 2015 Broadcom -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define RP1_MAILBOX_FIRMWARE 0 -+ -+enum rp1_firmware_ops { -+ MBOX_SUCCESS = 0x0000, -+ GET_FIRMWARE_VERSION = 0x0001, // na -> 160-bit version -+ GET_FEATURE = 0x0002, // FOURCC -> op base (0 == unsupported), op count -+ -+ COMMON_COUNT -+}; -+ -+struct rp1_firmware { -+ struct mbox_client cl; -+ struct mbox_chan *chan; /* The doorbell channel */ -+ uint32_t __iomem *buf; /* The shared buffer */ -+ u32 buf_size; /* The size of the shared buffer */ -+ struct completion c; -+ -+ struct kref consumers; -+}; -+ -+struct rp1_get_feature_resp { -+ uint32_t op_base; -+ uint32_t op_count; -+}; -+ -+static DEFINE_MUTEX(transaction_lock); -+ -+static const struct of_device_id rp1_firmware_of_match[] = { -+ { .compatible = "raspberrypi,rp1-firmware", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, rp1_firmware_of_match); -+ -+static void response_callback(struct mbox_client *cl, void *msg) -+{ -+ struct rp1_firmware *fw = container_of(cl, struct rp1_firmware, cl); -+ -+ complete(&fw->c); -+} -+ -+/* -+ * Sends a request to the RP1 firmware and synchronously waits for the reply. -+ * Returns zero or a positive count of response bytes on success, negative on -+ * error. -+ */ -+ -+int rp1_firmware_message(struct rp1_firmware *fw, uint16_t op, -+ const void *data, unsigned int data_len, -+ void *resp, unsigned int resp_space) -+{ -+ int ret; -+ u32 rc; -+ -+ if (data_len + 4 > fw->buf_size) -+ return -EINVAL; -+ -+ mutex_lock(&transaction_lock); -+ -+ memcpy_toio(&fw->buf[1], data, data_len); -+ writel((op << 16) | data_len, fw->buf); -+ -+ reinit_completion(&fw->c); -+ ret = mbox_send_message(fw->chan, NULL); -+ if (ret >= 0) { -+ if (wait_for_completion_timeout(&fw->c, HZ)) -+ ret = 0; -+ else -+ ret = -ETIMEDOUT; -+ } else { -+ dev_err(fw->cl.dev, "mbox_send_message returned %d\n", ret); -+ } -+ -+ if (ret == 0) { -+ rc = readl(fw->buf); -+ if (rc & 0x80000000) { -+ ret = (int32_t)rc; -+ } else { -+ ret = min(rc, resp_space); -+ memcpy_fromio(resp, &fw->buf[1], ret); -+ } -+ } -+ -+ mutex_unlock(&transaction_lock); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(rp1_firmware_message); -+ -+static void rp1_firmware_delete(struct kref *kref) -+{ -+ struct rp1_firmware *fw = container_of(kref, struct rp1_firmware, consumers); -+ -+ mbox_free_channel(fw->chan); -+ kfree(fw); -+} -+ -+void rp1_firmware_put(struct rp1_firmware *fw) -+{ -+ kref_put(&fw->consumers, rp1_firmware_delete); -+} -+EXPORT_SYMBOL_GPL(rp1_firmware_put); -+ -+int rp1_firmware_get_feature(struct rp1_firmware *fw, uint32_t fourcc, -+ uint32_t *op_base, uint32_t *op_count) -+{ -+ struct rp1_get_feature_resp resp; -+ int ret; -+ -+ memset(&resp, 0, sizeof(resp)); -+ ret = rp1_firmware_message(fw, GET_FEATURE, -+ &fourcc, sizeof(fourcc), -+ &resp, sizeof(resp)); -+ *op_base = resp.op_base; -+ *op_count = resp.op_count; -+ if (ret < 0) -+ return ret; -+ if (ret < sizeof(resp) || !resp.op_base) -+ return -EOPNOTSUPP; -+ return 0; -+} -+EXPORT_SYMBOL_GPL(rp1_firmware_get_feature); -+ -+static void devm_rp1_firmware_put(void *data) -+{ -+ struct rp1_firmware *fw = data; -+ -+ rp1_firmware_put(fw); -+} -+ -+/** -+ * rp1_firmware_get - Get pointer to rp1_firmware structure. -+ * -+ * The reference to rp1_firmware has to be released with rp1_firmware_put(). -+ * -+ * Returns an error pointer on failure. -+ */ -+struct rp1_firmware *rp1_firmware_get(struct device_node *client) -+{ -+ const char *match = rp1_firmware_of_match[0].compatible; -+ struct platform_device *pdev; -+ struct device_node *fwnode; -+ struct rp1_firmware *fw; -+ -+ if (client) { -+ fwnode = of_parse_phandle(client, "firmware", 0); -+ if (!fwnode) -+ fwnode = of_get_parent(client); -+ if (fwnode && !of_device_is_compatible(fwnode, match)) { -+ of_node_put(fwnode); -+ fwnode = NULL; -+ } -+ } -+ -+ if (!fwnode) -+ fwnode = of_find_matching_node(NULL, rp1_firmware_of_match); -+ -+ if (!fwnode) -+ return ERR_PTR(-ENOENT); -+ -+ pdev = of_find_device_by_node(fwnode); -+ of_node_put(fwnode); -+ -+ if (!pdev) -+ return ERR_PTR(-EPROBE_DEFER); -+ -+ fw = platform_get_drvdata(pdev); -+ if (!fw) -+ goto err_defer; -+ -+ if (!kref_get_unless_zero(&fw->consumers)) -+ goto err_defer; -+ -+ put_device(&pdev->dev); -+ -+ return fw; -+ -+err_defer: -+ put_device(&pdev->dev); -+ return ERR_PTR(-EPROBE_DEFER); -+} -+EXPORT_SYMBOL_GPL(rp1_firmware_get); -+ -+/** -+ * devm_rp1_firmware_get - Get pointer to rp1_firmware structure. -+ * @firmware_node: Pointer to the firmware Device Tree node. -+ * -+ * Returns NULL is the firmware device is not ready. -+ */ -+struct rp1_firmware *devm_rp1_firmware_get(struct device *dev, struct device_node *client) -+{ -+ struct rp1_firmware *fw; -+ int ret; -+ -+ fw = rp1_firmware_get(client); -+ if (IS_ERR(fw)) -+ return fw; -+ -+ ret = devm_add_action_or_reset(dev, devm_rp1_firmware_put, fw); -+ if (ret) -+ return ERR_PTR(ret); -+ -+ return fw; -+} -+EXPORT_SYMBOL_GPL(devm_rp1_firmware_get); -+ -+static int rp1_firmware_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct device_node *shmem; -+ struct rp1_firmware *fw; -+ struct resource res; -+ uint32_t version[5]; -+ int ret; -+ -+ shmem = of_parse_phandle(dev->of_node, "shmem", 0); -+ if (!of_device_is_compatible(shmem, "raspberrypi,rp1-shmem")) { -+ of_node_put(shmem); -+ return -ENXIO; -+ } -+ -+ ret = of_address_to_resource(shmem, 0, &res); -+ of_node_put(shmem); -+ if (ret) { -+ dev_err(dev, "failed to get shared memory (%pOF) - %d\n", shmem, ret); -+ return ret; -+ } -+ -+ /* -+ * Memory will be freed by rp1_firmware_delete() once all users have -+ * released their firmware handles. Don't use devm_kzalloc() here. -+ */ -+ fw = kzalloc(sizeof(*fw), GFP_KERNEL); -+ if (!fw) -+ return -ENOMEM; -+ -+ fw->buf_size = resource_size(&res); -+ fw->buf = devm_ioremap(dev, res.start, fw->buf_size); -+ if (!fw->buf) { -+ dev_err(dev, "failed to ioremap shared memory\n"); -+ kfree(fw); -+ return -EADDRNOTAVAIL; -+ } -+ -+ fw->cl.dev = dev; -+ fw->cl.rx_callback = response_callback; -+ fw->cl.tx_block = false; -+ -+ fw->chan = mbox_request_channel(&fw->cl, RP1_MAILBOX_FIRMWARE); -+ if (IS_ERR(fw->chan)) { -+ int ret = PTR_ERR(fw->chan); -+ -+ if (ret != -EPROBE_DEFER) -+ dev_err(dev, "Failed to get mbox channel: %d\n", ret); -+ kfree(fw); -+ return ret; -+ } -+ -+ init_completion(&fw->c); -+ kref_init(&fw->consumers); -+ -+ platform_set_drvdata(pdev, fw); -+ -+ ret = rp1_firmware_message(fw, GET_FIRMWARE_VERSION, -+ NULL, 0, &version, sizeof(version)); -+ if (ret == sizeof(version)) { -+ dev_info(dev, "RP1 Firmware version %08x%08x%08x%08x%08x\n", -+ version[0], version[1], version[2], version[3], version[4]); -+ ret = 0; -+ } else if (ret >= 0) { -+ ret = -EIO; -+ } -+ -+ return ret; -+} -+ -+static int rp1_firmware_remove(struct platform_device *pdev) -+{ -+ struct rp1_firmware *fw = platform_get_drvdata(pdev); -+ -+ rp1_firmware_put(fw); -+ -+ return 0; -+} -+ -+static struct platform_driver rp1_firmware_driver = { -+ .driver = { -+ .name = "rp1-firmware", -+ .of_match_table = rp1_firmware_of_match, -+ }, -+ .probe = rp1_firmware_probe, -+ .remove = rp1_firmware_remove, -+}; -+ -+module_platform_driver(rp1_firmware_driver); -+ -+MODULE_AUTHOR("Phil Elwell "); -+MODULE_DESCRIPTION("RP1 firmware driver"); -+MODULE_LICENSE("GPL v2"); ---- /dev/null -+++ b/include/linux/rp1-firmware.h -@@ -0,0 +1,53 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (C) 2023 2023-2024 Raspberry Pi Ltd. -+ */ -+ -+#ifndef __SOC_RP1_FIRMWARE_H__ -+#define __SOC_RP1_FIRMWARE_H__ -+ -+#include -+#include -+ -+#define RP1_FOURCC(s) ((uint32_t)((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | (s[3] << 0))) -+ -+struct rp1_firmware; -+ -+#if IS_ENABLED(CONFIG_FIRMWARE_RP1) -+int rp1_firmware_message(struct rp1_firmware *fw, uint16_t op, -+ const void *data, unsigned int data_len, -+ void *resp, unsigned int resp_space); -+void rp1_firmware_put(struct rp1_firmware *fw); -+struct rp1_firmware *rp1_firmware_get(struct device_node *fwnode); -+struct rp1_firmware *devm_rp1_firmware_get(struct device *dev, struct device_node *fwnode); -+int rp1_firmware_get_feature(struct rp1_firmware *fw, uint32_t fourcc, -+ uint32_t *op_base, uint32_t *op_count); -+#else -+static inline int rp1_firmware_message(struct rp1_firmware *fw, uint16_t op, -+ const void *data, unsigned int data_len, -+ void *resp, unsigned int resp_space) -+{ -+ return -EOPNOTSUPP; -+} -+ -+static inline void rp1_firmware_put(struct rp1_firmware *fw) { } -+ -+static inline struct rp1_firmware *rp1_firmware_get(struct device_node *fwnode) -+{ -+ return NULL; -+} -+ -+static inline struct rp1_firmware *devm_rp1_firmware_get(struct device *dev, -+ struct device_node *fwnode) -+{ -+ return NULL; -+} -+ -+static inline int rp1_firmware_get_feature(struct rp1_firmware *fw, uint32_t fourcc, -+ uint32_t *op_base, uint32_t *op_count) -+{ -+ return -EOPNOTSUPP; -+} -+#endif -+ -+#endif /* __SOC_RP1_FIRMWARE_H__ */ diff --git a/target/linux/bcm27xx/patches-6.6/950-1353-misc-Add-RP1-PIO-driver.patch b/target/linux/bcm27xx/patches-6.6/950-1353-misc-Add-RP1-PIO-driver.patch deleted file mode 100644 index 521129b919..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-1353-misc-Add-RP1-PIO-driver.patch +++ /dev/null @@ -1,1385 +0,0 @@ -From 55fd5c9018e1520d45f08cf08630a493ec7dedea Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Thu, 31 Oct 2024 18:26:00 +0000 -Subject: [PATCH] misc: Add RP1 PIO driver - -Provide remote access to the PIO hardware in RP1. There is a single -instance, with 4 state machines. - -Signed-off-by: Phil Elwell ---- - drivers/misc/Kconfig | 8 + - drivers/misc/Makefile | 1 + - drivers/misc/rp1-fw-pio.h | 53 ++ - drivers/misc/rp1-pio.c | 1064 ++++++++++++++++++++++++++++++++ - include/uapi/misc/rp1_pio_if.h | 212 +++++++ - 5 files changed, 1338 insertions(+) - create mode 100644 drivers/misc/rp1-fw-pio.h - create mode 100644 drivers/misc/rp1-pio.c - create mode 100644 include/uapi/misc/rp1_pio_if.h - ---- a/drivers/misc/Kconfig -+++ b/drivers/misc/Kconfig -@@ -17,6 +17,14 @@ config BCM2835_SMI - Driver for enabling and using Broadcom's Secondary/Slow Memory Interface. - Appears as /dev/bcm2835_smi. For ioctl interface see drivers/misc/bcm2835_smi.h - -+config RP1_PIO -+ tristate "Raspberry Pi RP1 PIO driver" -+ select FIRMWARE_RP1 -+ default n -+ help -+ Driver providing control of the Raspberry Pi PIO block, as found in -+ RP1. -+ - config AD525X_DPOT - tristate "Analog Devices Digital Potentiometers" - depends on (I2C || SPI) && SYSFS ---- a/drivers/misc/Makefile -+++ b/drivers/misc/Makefile -@@ -18,6 +18,7 @@ obj-$(CONFIG_TIFM_7XX1) += tifm_7 - obj-$(CONFIG_PHANTOM) += phantom.o - obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o - obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o -+obj-$(CONFIG_RP1_PIO) += rp1-pio.o - obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o - obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o - obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o ---- /dev/null -+++ b/drivers/misc/rp1-fw-pio.h -@@ -0,0 +1,53 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (C) 2023 2023-2024 Raspberry Pi Ltd. -+ */ -+ -+#ifndef __SOC_RP1_FIRMWARE_OPS_H__ -+#define __SOC_RP1_FIRMWARE_OPS_H__ -+ -+#include -+ -+#define FOURCC_PIO RP1_FOURCC("PIO ") -+ -+enum rp1_pio_ops { -+ PIO_CAN_ADD_PROGRAM, // u16 num_instrs, u16 origin -> origin -+ PIO_ADD_PROGRAM, // u16 num_instrs, u16 origin, u16 prog[] -> rc -+ PIO_REMOVE_PROGRAM, // u16 num_instrs, u16 origin -+ PIO_CLEAR_INSTR_MEM, // - -+ -+ PIO_SM_CLAIM, // u16 mask -> sm -+ PIO_SM_UNCLAIM, // u16 mask -+ PIO_SM_IS_CLAIMED, // u16 mask -> claimed -+ -+ PIO_SM_INIT, // u16 sm, u16 initial_pc, u32 sm_config[4] -+ PIO_SM_SET_CONFIG, // u16 sm, u16 rsvd, u32 sm_config[4] -+ PIO_SM_EXEC, // u16 sm, u16 instr, u8 blocking, u8 rsvd -+ PIO_SM_CLEAR_FIFOS, // u16 sm -+ PIO_SM_SET_CLKDIV, // u16 sm, u16 div_int, u8 div_frac, u8 rsvd -+ PIO_SM_SET_PINS, // u16 sm, u16 rsvd, u32 values, u32 mask -+ PIO_SM_SET_PINDIRS, // u16 sm, u16 rsvd, u32 dirs, u32 mask -+ PIO_SM_SET_ENABLED, // u16 mask, u8 enable, u8 rsvd -+ PIO_SM_RESTART, // u16 mask -+ PIO_SM_CLKDIV_RESTART, // u16 mask -+ PIO_SM_ENABLE_SYNC, // u16 mask -+ PIO_SM_PUT, // u16 sm, u8 blocking, u8 rsvd, u32 data -+ PIO_SM_GET, // u16 sm, u8 blocking, u8 rsvd -> u32 data -+ PIO_SM_SET_DMACTRL, // u16 sm, u16 is_tx, u32 ctrl -+ -+ GPIO_INIT, // u16 gpio -+ GPIO_SET_FUNCTION, // u16 gpio, u16 fn -+ GPIO_SET_PULLS, // u16 gpio, u8 up, u8 down -+ GPIO_SET_OUTOVER, // u16 gpio, u16 value -+ GPIO_SET_INOVER, // u16 gpio, u16 value -+ GPIO_SET_OEOVER, // u16 gpio, u16 value -+ GPIO_SET_INPUT_ENABLED, // u16 gpio, u16 value -+ GPIO_SET_DRIVE_STRENGTH, // u16 gpio, u16 value -+ -+ READ_HW, // src address, len -> data bytes -+ WRITE_HW, // dst address, data -+ -+ PIO_COUNT -+}; -+ -+#endif ---- /dev/null -+++ b/drivers/misc/rp1-pio.c -@@ -0,0 +1,1064 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// PIO driver for RP1 -+// -+// Copyright (C) 2023-2024 Raspberry Pi Ltd. -+// -+// Parts of this driver are based on: -+// - vcio.c, by Noralf Trønnes -+// Copyright (C) 2010 Broadcom -+// Copyright (C) 2015 Noralf Trønnes -+// Copyright (C) 2021 Raspberry Pi (Trading) Ltd. -+// - bcm2835_smi.c & bcm2835_smi_dev.c by Luke Wren -+// Copyright (c) 2015 Raspberry Pi (Trading) Ltd. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "rp1-fw-pio.h" -+ -+#define DRIVER_NAME "rp1-pio" -+ -+#define RP1_PIO_SMS_COUNT 4 -+#define RP1_PIO_INSTR_COUNT 32 -+ -+#define MAX_ARG_SIZE 256 -+ -+#define RP1_PIO_FIFO_TX0 0x00 -+#define RP1_PIO_FIFO_TX1 0x04 -+#define RP1_PIO_FIFO_TX2 0x08 -+#define RP1_PIO_FIFO_TX3 0x0c -+#define RP1_PIO_FIFO_RX0 0x10 -+#define RP1_PIO_FIFO_RX1 0x14 -+#define RP1_PIO_FIFO_RX2 0x18 -+#define RP1_PIO_FIFO_RX3 0x1c -+ -+#define RP1_PIO_DMACTRL_DEFAULT 0x80000104 -+ -+#define HANDLER(_n, _f) \ -+ [_IOC_NR(PIO_IOC_ ## _n)] = { #_n, rp1_pio_ ## _f, _IOC_SIZE(PIO_IOC_ ## _n) } -+ -+ -+#define ROUND_UP(x, y) (((x) + (y) - 1) - (((x) + (y) - 1) % (y))) -+ -+#define DMA_BOUNCE_BUFFER_SIZE 0x1000 -+#define DMA_BOUNCE_BUFFER_COUNT 4 -+ -+struct dma_buf_info { -+ void *buf; -+ dma_addr_t phys; -+ struct scatterlist sgl; -+}; -+ -+struct dma_info { -+ struct semaphore buf_sem; -+ struct dma_chan *chan; -+ size_t buf_size; -+ size_t buf_count; -+ unsigned int head_idx; -+ unsigned int tail_idx; -+ struct dma_buf_info bufs[DMA_BOUNCE_BUFFER_COUNT]; -+}; -+ -+struct rp1_pio_device { -+ struct platform_device *pdev; -+ struct rp1_firmware *fw; -+ uint16_t fw_pio_base; -+ uint16_t fw_pio_count; -+ dev_t dev_num; -+ struct class *dev_class; -+ struct cdev cdev; -+ phys_addr_t phys_addr; -+ uint32_t claimed_sms; -+ uint32_t claimed_dmas; -+ spinlock_t lock; -+ struct mutex instr_mutex; -+ struct dma_info dma_configs[RP1_PIO_SMS_COUNT][RP1_PIO_DIR_COUNT]; -+ uint32_t used_instrs; -+ uint8_t instr_refcounts[RP1_PIO_INSTR_COUNT]; -+ uint16_t instrs[RP1_PIO_INSTR_COUNT]; -+ uint client_count; -+}; -+ -+struct rp1_pio_client { -+ struct rp1_pio_device *pio; -+ uint32_t claimed_sms; -+ uint32_t claimed_instrs; -+ uint32_t claimed_dmas; -+}; -+ -+static struct rp1_pio_device *g_pio; -+ -+static int rp1_pio_message(struct rp1_pio_device *pio, -+ uint16_t op, const void *data, unsigned int data_len) -+{ -+ uint32_t rc; -+ int ret; -+ -+ if (op >= pio->fw_pio_count) -+ return -EOPNOTSUPP; -+ ret = rp1_firmware_message(pio->fw, pio->fw_pio_base + op, -+ data, data_len, -+ &rc, sizeof(rc)); -+ if (ret == 4) -+ ret = rc; -+ return ret; -+} -+ -+static int rp1_pio_message_resp(struct rp1_pio_device *pio, -+ uint16_t op, const void *data, unsigned int data_len, -+ void *resp, void __user *userbuf, unsigned int resp_len) -+{ -+ uint32_t resp_buf[1 + 32]; -+ int ret; -+ -+ if (op >= pio->fw_pio_count) -+ return -EOPNOTSUPP; -+ if (resp_len + 4 >= sizeof(resp_buf)) -+ return -EINVAL; -+ if (!resp && !userbuf) -+ return -EINVAL; -+ ret = rp1_firmware_message(pio->fw, pio->fw_pio_base + op, -+ data, data_len, -+ resp_buf, resp_len + 4); -+ if (ret >= 4 && !resp_buf[0]) { -+ ret -= 4; -+ if (resp) -+ memcpy(resp, &resp_buf[1], ret); -+ else if (copy_to_user(userbuf, &resp_buf[1], ret)) -+ ret = -EFAULT; -+ } else if (ret >= 0) { -+ ret = -EIO; -+ } -+ return ret; -+} -+ -+static int rp1_pio_read_hw(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_device *pio = client->pio; -+ struct rp1_access_hw_args *args = param; -+ -+ return rp1_pio_message_resp(pio, READ_HW, -+ args, 8, NULL, args->data, args->len); -+} -+ -+static int rp1_pio_write_hw(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_device *pio = client->pio; -+ struct rp1_access_hw_args *args = param; -+ uint32_t write_buf[32 + 1]; -+ int len; -+ -+ len = min(args->len, sizeof(write_buf) - 4); -+ write_buf[0] = args->addr; -+ if (copy_from_user(&write_buf[1], args->data, len)) -+ return -EFAULT; -+ return rp1_firmware_message(pio->fw, pio->fw_pio_base + WRITE_HW, -+ write_buf, 4 + len, NULL, 0); -+} -+ -+static int rp1_pio_find_program(struct rp1_pio_device *pio, -+ struct rp1_pio_add_program_args *prog) -+{ -+ uint start, end, prog_size; -+ uint32_t used_mask; -+ uint i; -+ -+ start = (prog->origin != RP1_PIO_ORIGIN_ANY) ? prog->origin : 0; -+ end = (prog->origin != RP1_PIO_ORIGIN_ANY) ? prog->origin : -+ (RP1_PIO_INSTRUCTION_COUNT - prog->num_instrs); -+ prog_size = sizeof(prog->instrs[0]) * prog->num_instrs; -+ used_mask = (uint32_t)(~0) >> (32 - prog->num_instrs); -+ -+ /* Find the best match */ -+ for (i = start; i <= end; i++) { -+ uint32_t mask = used_mask << i; -+ -+ if ((pio->used_instrs & mask) != mask) -+ continue; -+ if (!memcmp(pio->instrs + i, prog->instrs, prog_size)) -+ return i; -+ } -+ -+ return -1; -+} -+ -+static int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_add_program_args *args = param; -+ struct rp1_pio_device *pio = client->pio; -+ int offset; -+ -+ if (args->num_instrs > RP1_PIO_INSTR_COUNT || -+ ((args->origin != RP1_PIO_ORIGIN_ANY) && -+ (args->origin >= RP1_PIO_INSTR_COUNT || -+ ((args->origin + args->num_instrs) > RP1_PIO_INSTR_COUNT)))) -+ return -EINVAL; -+ -+ mutex_lock(&pio->instr_mutex); -+ offset = rp1_pio_find_program(pio, args); -+ mutex_unlock(&pio->instr_mutex); -+ if (offset >= 0) -+ return offset; -+ -+ /* Don't send the instructions, just the header */ -+ return rp1_pio_message(pio, PIO_CAN_ADD_PROGRAM, args, -+ offsetof(struct rp1_pio_add_program_args, instrs)); -+} -+ -+static int rp1_pio_add_program(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_add_program_args *args = param; -+ struct rp1_pio_device *pio = client->pio; -+ int offset; -+ uint i; -+ -+ if (args->num_instrs > RP1_PIO_INSTR_COUNT || -+ ((args->origin != RP1_PIO_ORIGIN_ANY) && -+ (args->origin >= RP1_PIO_INSTR_COUNT || -+ ((args->origin + args->num_instrs) > RP1_PIO_INSTR_COUNT)))) -+ return -EINVAL; -+ -+ mutex_lock(&pio->instr_mutex); -+ offset = rp1_pio_find_program(pio, args); -+ if (offset < 0) -+ offset = rp1_pio_message(client->pio, PIO_ADD_PROGRAM, args, sizeof(*args)); -+ -+ if (offset >= 0) { -+ uint32_t used_mask; -+ uint prog_size; -+ -+ used_mask = ((uint32_t)(~0) >> (-args->num_instrs & 0x1f)) << offset; -+ prog_size = sizeof(args->instrs[0]) * args->num_instrs; -+ -+ if ((pio->used_instrs & used_mask) != used_mask) { -+ pio->used_instrs |= used_mask; -+ memcpy(pio->instrs + offset, args->instrs, prog_size); -+ } -+ client->claimed_instrs |= used_mask; -+ for (i = 0; i < args->num_instrs; i++) -+ pio->instr_refcounts[offset + i]++; -+ } -+ mutex_unlock(&pio->instr_mutex); -+ return offset; -+} -+ -+static void rp1_pio_remove_instrs(struct rp1_pio_device *pio, uint32_t mask) -+{ -+ struct rp1_pio_remove_program_args args; -+ uint i; -+ -+ mutex_lock(&pio->instr_mutex); -+ args.num_instrs = 0; -+ for (i = 0; ; i++, mask >>= 1) { -+ if ((mask & 1) && pio->instr_refcounts[i] && !--pio->instr_refcounts[i]) { -+ pio->used_instrs &= ~(1 << i); -+ args.num_instrs++; -+ } else if (args.num_instrs) { -+ args.origin = i - args.num_instrs; -+ rp1_pio_message(pio, PIO_REMOVE_PROGRAM, &args, sizeof(args)); -+ args.num_instrs = 0; -+ } -+ if (!mask) -+ break; -+ } -+ mutex_unlock(&pio->instr_mutex); -+} -+ -+static int rp1_pio_remove_program(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_remove_program_args *args = param; -+ uint32_t used_mask; -+ int ret = -ENOENT; -+ -+ if (args->num_instrs > RP1_PIO_INSTR_COUNT || -+ args->origin >= RP1_PIO_INSTR_COUNT || -+ (args->origin + args->num_instrs) > RP1_PIO_INSTR_COUNT) -+ return -EINVAL; -+ -+ used_mask = ((uint32_t)(~0) >> (32 - args->num_instrs)) << args->origin; -+ if ((client->claimed_instrs & used_mask) == used_mask) { -+ client->claimed_instrs &= ~used_mask; -+ rp1_pio_remove_instrs(client->pio, used_mask); -+ ret = 0; -+ } -+ return ret; -+} -+ -+static int rp1_pio_clear_instr_mem(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_device *pio = client->pio; -+ -+ mutex_lock(&pio->instr_mutex); -+ (void)rp1_pio_message(client->pio, PIO_CLEAR_INSTR_MEM, NULL, 0); -+ memset(pio->instr_refcounts, 0, sizeof(pio->instr_refcounts)); -+ pio->used_instrs = 0; -+ client->claimed_instrs = 0; -+ mutex_unlock(&pio->instr_mutex); -+ return 0; -+} -+ -+static int rp1_pio_sm_claim(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_claim_args *args = param; -+ struct rp1_pio_device *pio = client->pio; -+ int ret; -+ -+ mutex_lock(&pio->instr_mutex); -+ ret = rp1_pio_message(client->pio, PIO_SM_CLAIM, args, sizeof(*args)); -+ if (ret >= 0) { -+ if (args->mask) -+ client->claimed_sms |= args->mask; -+ else -+ client->claimed_sms |= (1 << ret); -+ pio->claimed_sms |= client->claimed_sms; -+ } -+ mutex_unlock(&pio->instr_mutex); -+ return ret; -+} -+ -+static int rp1_pio_sm_unclaim(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_claim_args *args = param; -+ struct rp1_pio_device *pio = client->pio; -+ -+ mutex_lock(&pio->instr_mutex); -+ (void)rp1_pio_message(client->pio, PIO_SM_UNCLAIM, args, sizeof(*args)); -+ client->claimed_sms &= ~args->mask; -+ pio->claimed_sms &= ~args->mask; -+ mutex_unlock(&pio->instr_mutex); -+ return 0; -+} -+ -+static int rp1_pio_sm_is_claimed(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_claim_args *args = param; -+ -+ return rp1_pio_message(client->pio, PIO_SM_IS_CLAIMED, args, sizeof(*args)); -+} -+ -+static int rp1_pio_sm_init(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_init_args *args = param; -+ -+ return rp1_pio_message(client->pio, PIO_SM_INIT, args, sizeof(*args)); -+} -+ -+static int rp1_pio_sm_set_config(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_set_config_args *args = param; -+ -+ return rp1_pio_message(client->pio, PIO_SM_SET_CONFIG, args, sizeof(*args)); -+} -+ -+static int rp1_pio_sm_exec(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_exec_args *args = param; -+ -+ return rp1_pio_message(client->pio, PIO_SM_EXEC, args, sizeof(*args)); -+} -+ -+static int rp1_pio_sm_clear_fifos(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_clear_fifos_args *args = param; -+ -+ return rp1_pio_message(client->pio, PIO_SM_CLEAR_FIFOS, args, sizeof(*args)); -+} -+ -+static int rp1_pio_sm_set_clkdiv(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_set_clkdiv_args *args = param; -+ -+ return rp1_pio_message(client->pio, PIO_SM_SET_CLKDIV, args, sizeof(*args)); -+} -+ -+static int rp1_pio_sm_set_pins(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_set_pins_args *args = param; -+ -+ return rp1_pio_message(client->pio, PIO_SM_SET_PINS, args, sizeof(*args)); -+} -+ -+static int rp1_pio_sm_set_pindirs(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_set_pindirs_args *args = param; -+ -+ return rp1_pio_message(client->pio, PIO_SM_SET_PINDIRS, args, sizeof(*args)); -+} -+ -+static int rp1_pio_sm_set_enabled(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_set_enabled_args *args = param; -+ -+ return rp1_pio_message(client->pio, PIO_SM_SET_ENABLED, args, sizeof(*args)); -+} -+ -+static int rp1_pio_sm_restart(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_restart_args *args = param; -+ -+ return rp1_pio_message(client->pio, PIO_SM_RESTART, args, sizeof(*args)); -+} -+ -+static int rp1_pio_sm_clkdiv_restart(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_restart_args *args = param; -+ -+ return rp1_pio_message(client->pio, PIO_SM_CLKDIV_RESTART, args, sizeof(*args)); -+} -+ -+static int rp1_pio_sm_enable_sync(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_enable_sync_args *args = param; -+ -+ return rp1_pio_message(client->pio, PIO_SM_ENABLE_SYNC, args, sizeof(*args)); -+} -+ -+static int rp1_pio_sm_put(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_put_args *args = param; -+ -+ return rp1_pio_message(client->pio, PIO_SM_PUT, args, sizeof(*args)); -+} -+ -+static int rp1_pio_sm_get(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_get_args *args = param; -+ int ret; -+ -+ ret = rp1_pio_message_resp(client->pio, PIO_SM_GET, args, sizeof(*args), -+ &args->data, NULL, sizeof(args->data)); -+ if (ret >= 0) -+ return offsetof(struct rp1_pio_sm_get_args, data) + ret; -+ return ret; -+} -+ -+static int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_set_dmactrl_args *args = param; -+ -+ return rp1_pio_message(client->pio, PIO_SM_SET_DMACTRL, args, sizeof(*args)); -+} -+ -+static int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_gpio_init_args *args = param; -+ -+ return rp1_pio_message(client->pio, GPIO_INIT, args, sizeof(*args)); -+} -+ -+static int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_gpio_set_function_args *args = param; -+ -+ return rp1_pio_message(client->pio, GPIO_SET_FUNCTION, args, sizeof(*args)); -+} -+ -+static int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_gpio_set_pulls_args *args = param; -+ -+ return rp1_pio_message(client->pio, GPIO_SET_PULLS, args, sizeof(*args)); -+} -+ -+static int rp1_pio_gpio_set_outover(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_gpio_set_args *args = param; -+ -+ return rp1_pio_message(client->pio, GPIO_SET_OUTOVER, args, sizeof(*args)); -+} -+ -+static int rp1_pio_gpio_set_inover(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_gpio_set_args *args = param; -+ -+ return rp1_pio_message(client->pio, GPIO_SET_INOVER, args, sizeof(*args)); -+} -+ -+static int rp1_pio_gpio_set_oeover(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_gpio_set_args *args = param; -+ -+ return rp1_pio_message(client->pio, GPIO_SET_OEOVER, args, sizeof(*args)); -+} -+ -+static int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_gpio_set_args *args = param; -+ -+ return rp1_pio_message(client->pio, GPIO_SET_INPUT_ENABLED, args, sizeof(*args)); -+} -+ -+static int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_gpio_set_args *args = param; -+ -+ return rp1_pio_message(client->pio, GPIO_SET_DRIVE_STRENGTH, args, sizeof(*args)); -+} -+ -+static void rp1_pio_sm_dma_callback(void *param) -+{ -+ struct dma_info *dma = param; -+ -+ up(&dma->buf_sem); -+} -+ -+static void rp1_pio_sm_dma_free(struct device *dev, struct dma_info *dma) -+{ -+ dmaengine_terminate_all(dma->chan); -+ while (dma->buf_count > 0) { -+ dma->buf_count--; -+ dma_free_coherent(dev, ROUND_UP(dma->buf_size, PAGE_SIZE), -+ dma->bufs[dma->buf_count].buf, dma->bufs[dma->buf_count].phys); -+ } -+ -+ dma_release_channel(dma->chan); -+} -+ -+static int rp1_pio_sm_config_xfer(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_config_xfer_args *args = param; -+ struct rp1_pio_sm_set_dmactrl_args set_dmactrl_args; -+ struct rp1_pio_device *pio = client->pio; -+ struct platform_device *pdev = pio->pdev; -+ struct device *dev = &pdev->dev; -+ struct dma_slave_config config = {}; -+ phys_addr_t fifo_addr; -+ struct dma_info *dma; -+ uint32_t dma_mask; -+ char chan_name[4]; -+ uint buf_size; -+ int ret = 0; -+ -+ if (args->sm >= RP1_PIO_SMS_COUNT || args->dir >= RP1_PIO_DIR_COUNT || -+ !args->buf_size || (args->buf_size & 3) || -+ !args->buf_count || args->buf_count > DMA_BOUNCE_BUFFER_COUNT) -+ return -EINVAL; -+ -+ dma_mask = 1 << (args->sm * 2 + args->dir); -+ -+ dma = &pio->dma_configs[args->sm][args->dir]; -+ -+ spin_lock(&pio->lock); -+ if (pio->claimed_dmas & dma_mask) -+ rp1_pio_sm_dma_free(dev, dma); -+ pio->claimed_dmas |= dma_mask; -+ client->claimed_dmas |= dma_mask; -+ spin_unlock(&pio->lock); -+ -+ dma->buf_size = args->buf_size; -+ /* Round up the allocations */ -+ buf_size = ROUND_UP(args->buf_size, PAGE_SIZE); -+ sema_init(&dma->buf_sem, 0); -+ -+ /* Allocate and configure a DMA channel */ -+ /* Careful - each SM FIFO has its own DREQ value */ -+ chan_name[0] = (args->dir == RP1_PIO_DIR_TO_SM) ? 't' : 'r'; -+ chan_name[1] = 'x'; -+ chan_name[2] = '0' + args->sm; -+ chan_name[3] = '\0'; -+ -+ dma->chan = dma_request_chan(dev, chan_name); -+ if (IS_ERR(dma->chan)) -+ return PTR_ERR(dma->chan); -+ -+ /* Alloc and map bounce buffers */ -+ for (dma->buf_count = 0; dma->buf_count < args->buf_count; dma->buf_count++) { -+ struct dma_buf_info *dbi = &dma->bufs[dma->buf_count]; -+ -+ dbi->buf = dma_alloc_coherent(dma->chan->device->dev, buf_size, -+ &dbi->phys, GFP_KERNEL); -+ if (!dbi->buf) { -+ ret = -ENOMEM; -+ goto err_dma_free; -+ } -+ sg_init_table(&dbi->sgl, 1); -+ sg_dma_address(&dbi->sgl) = dbi->phys; -+ } -+ -+ fifo_addr = pio->phys_addr; -+ fifo_addr += args->sm * (RP1_PIO_FIFO_TX1 - RP1_PIO_FIFO_TX0); -+ fifo_addr += (args->dir == RP1_PIO_DIR_TO_SM) ? RP1_PIO_FIFO_TX0 : RP1_PIO_FIFO_RX0; -+ -+ config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; -+ config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; -+ config.src_addr = fifo_addr; -+ config.dst_addr = fifo_addr; -+ config.direction = (args->dir == RP1_PIO_DIR_TO_SM) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; -+ -+ ret = dmaengine_slave_config(dma->chan, &config); -+ if (ret) -+ goto err_dma_free; -+ -+ set_dmactrl_args.sm = args->sm; -+ set_dmactrl_args.is_tx = (args->dir == RP1_PIO_DIR_TO_SM); -+ set_dmactrl_args.ctrl = RP1_PIO_DMACTRL_DEFAULT; -+ if (args->dir == RP1_PIO_DIR_FROM_SM) -+ set_dmactrl_args.ctrl = (RP1_PIO_DMACTRL_DEFAULT & ~0x1f) | 1; -+ -+ ret = rp1_pio_sm_set_dmactrl(client, &set_dmactrl_args); -+ if (ret) -+ goto err_dma_free; -+ -+ return 0; -+ -+err_dma_free: -+ rp1_pio_sm_dma_free(dev, dma); -+ -+ spin_lock(&pio->lock); -+ client->claimed_dmas &= ~dma_mask; -+ pio->claimed_dmas &= ~dma_mask; -+ spin_unlock(&pio->lock); -+ -+ return ret; -+} -+ -+static int rp1_pio_sm_tx_user(struct rp1_pio_device *pio, struct dma_info *dma, -+ const void __user *userbuf, size_t bytes) -+{ -+ struct platform_device *pdev = pio->pdev; -+ struct dma_async_tx_descriptor *desc; -+ struct device *dev = &pdev->dev; -+ int ret = 0; -+ -+ // Clean the slate - we're running synchronously -+ dma->head_idx = 0; -+ dma->tail_idx = 0; -+ -+ while (bytes > 0) { -+ size_t copy_bytes = min(bytes, dma->buf_size); -+ struct dma_buf_info *dbi; -+ -+ /* grab the next free buffer, waiting if they're all full */ -+ if (dma->head_idx - dma->tail_idx == dma->buf_count) { -+ if (down_timeout(&dma->buf_sem, -+ msecs_to_jiffies(1000))) { -+ dev_err(dev, "DMA bounce timed out\n"); -+ break; -+ } -+ dma->tail_idx++; -+ } -+ -+ dbi = &dma->bufs[dma->head_idx % dma->buf_count]; -+ -+ sg_dma_len(&dbi->sgl) = copy_bytes; -+ -+ ret = copy_from_user(dbi->buf, userbuf, copy_bytes); -+ if (ret < 0) -+ break; -+ -+ userbuf += copy_bytes; -+ -+ desc = dmaengine_prep_slave_sg(dma->chan, &dbi->sgl, 1, -+ DMA_MEM_TO_DEV, -+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK | -+ DMA_PREP_FENCE); -+ if (!desc) { -+ dev_err(dev, "DMA preparation failedzn"); -+ ret = -EIO; -+ break; -+ } -+ -+ desc->callback = rp1_pio_sm_dma_callback; -+ desc->callback_param = dma; -+ -+ /* Submit the buffer - the callback will kick the semaphore */ -+ ret = dmaengine_submit(desc); -+ if (ret < 0) -+ break; -+ ret = 0; -+ -+ dma_async_issue_pending(dma->chan); -+ -+ dma->head_idx++; -+ bytes -= copy_bytes; -+ } -+ -+ // Block for completion -+ while (dma->tail_idx != dma->head_idx) { -+ if (down_timeout(&dma->buf_sem, msecs_to_jiffies(1000))) { -+ dev_err(dev, "DMA wait timed out\n"); -+ ret = -ETIMEDOUT; -+ break; -+ } -+ dma->tail_idx++; -+ } -+ -+ return ret; -+} -+ -+static int rp1_pio_sm_rx_user(struct rp1_pio_device *pio, struct dma_info *dma, -+ void __user *userbuf, size_t bytes) -+{ -+ struct platform_device *pdev = pio->pdev; -+ struct dma_async_tx_descriptor *desc; -+ struct device *dev = &pdev->dev; -+ int ret = 0; -+ -+ /* Clean the slate - we're running synchronously */ -+ dma->head_idx = 0; -+ dma->tail_idx = 0; -+ -+ while (bytes || dma->tail_idx != dma->head_idx) { -+ size_t copy_bytes = min(bytes, dma->buf_size); -+ struct dma_buf_info *dbi; -+ -+ /* -+ * wait for the next RX to complete if all the buffers are -+ * outstanding or we're finishing up. -+ */ -+ if (!bytes || dma->head_idx - dma->tail_idx == dma->buf_count) { -+ if (down_timeout(&dma->buf_sem, -+ msecs_to_jiffies(1000))) { -+ dev_err(dev, "DMA wait timed out"); -+ ret = -ETIMEDOUT; -+ break; -+ } -+ -+ dbi = &dma->bufs[dma->tail_idx++ % dma->buf_count]; -+ ret = copy_to_user(userbuf, dbi->buf, sg_dma_len(&dbi->sgl)); -+ if (ret < 0) -+ break; -+ userbuf += sg_dma_len(&dbi->sgl); -+ -+ if (!bytes) -+ continue; -+ } -+ -+ dbi = &dma->bufs[dma->head_idx % dma->buf_count]; -+ sg_dma_len(&dbi->sgl) = copy_bytes; -+ desc = dmaengine_prep_slave_sg(dma->chan, &dbi->sgl, 1, -+ DMA_DEV_TO_MEM, -+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK | -+ DMA_PREP_FENCE); -+ if (!desc) { -+ dev_err(dev, "DMA preparation failed"); -+ ret = -EIO; -+ break; -+ } -+ -+ desc->callback = rp1_pio_sm_dma_callback; -+ desc->callback_param = dma; -+ -+ // Submit the buffer - the callback will kick the semaphore -+ -+ ret = dmaengine_submit(desc); -+ if (ret < 0) -+ break; -+ -+ dma_async_issue_pending(dma->chan); -+ -+ dma->head_idx++; -+ bytes -= copy_bytes; -+ } -+ -+ return ret; -+} -+ -+static int rp1_pio_sm_xfer_data(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_xfer_data_args *args = param; -+ struct rp1_pio_device *pio = client->pio; -+ struct dma_info *dma; -+ -+ if (args->sm >= RP1_PIO_SMS_COUNT || args->dir >= RP1_PIO_DIR_COUNT || -+ !args->data_bytes || !args->data) -+ return -EINVAL; -+ -+ dma = &pio->dma_configs[args->sm][args->dir]; -+ -+ if (args->dir == RP1_PIO_DIR_TO_SM) -+ return rp1_pio_sm_tx_user(pio, dma, args->data, args->data_bytes); -+ else -+ return rp1_pio_sm_rx_user(pio, dma, args->data, args->data_bytes); -+} -+ -+struct handler_info { -+ const char *name; -+ int (*func)(struct rp1_pio_client *client, void *param); -+ int argsize; -+} ioctl_handlers[] = { -+ HANDLER(SM_CONFIG_XFER, sm_config_xfer), -+ HANDLER(SM_XFER_DATA, sm_xfer_data), -+ -+ HANDLER(CAN_ADD_PROGRAM, can_add_program), -+ HANDLER(ADD_PROGRAM, add_program), -+ HANDLER(REMOVE_PROGRAM, remove_program), -+ HANDLER(CLEAR_INSTR_MEM, clear_instr_mem), -+ -+ HANDLER(SM_CLAIM, sm_claim), -+ HANDLER(SM_UNCLAIM, sm_unclaim), -+ HANDLER(SM_IS_CLAIMED, sm_is_claimed), -+ -+ HANDLER(SM_INIT, sm_init), -+ HANDLER(SM_SET_CONFIG, sm_set_config), -+ HANDLER(SM_EXEC, sm_exec), -+ HANDLER(SM_CLEAR_FIFOS, sm_clear_fifos), -+ HANDLER(SM_SET_CLKDIV, sm_set_clkdiv), -+ HANDLER(SM_SET_PINS, sm_set_pins), -+ HANDLER(SM_SET_PINDIRS, sm_set_pindirs), -+ HANDLER(SM_SET_ENABLED, sm_set_enabled), -+ HANDLER(SM_RESTART, sm_restart), -+ HANDLER(SM_CLKDIV_RESTART, sm_clkdiv_restart), -+ HANDLER(SM_ENABLE_SYNC, sm_enable_sync), -+ HANDLER(SM_PUT, sm_put), -+ HANDLER(SM_GET, sm_get), -+ HANDLER(SM_SET_DMACTRL, sm_set_dmactrl), -+ -+ HANDLER(GPIO_INIT, gpio_init), -+ HANDLER(GPIO_SET_FUNCTION, gpio_set_function), -+ HANDLER(GPIO_SET_PULLS, gpio_set_pulls), -+ HANDLER(GPIO_SET_OUTOVER, gpio_set_outover), -+ HANDLER(GPIO_SET_INOVER, gpio_set_inover), -+ HANDLER(GPIO_SET_OEOVER, gpio_set_oeover), -+ HANDLER(GPIO_SET_INPUT_ENABLED, gpio_set_input_enabled), -+ HANDLER(GPIO_SET_DRIVE_STRENGTH, gpio_set_drive_strength), -+ -+ HANDLER(READ_HW, read_hw), -+ HANDLER(WRITE_HW, write_hw), -+}; -+ -+static int rp1_pio_open(struct inode *inode, struct file *filp) -+{ -+ struct rp1_pio_device *pio = g_pio; -+ struct rp1_pio_client *client; -+ -+ client = kzalloc(sizeof(*client), GFP_KERNEL); -+ -+ client->pio = pio; -+ filp->private_data = client; -+ -+ return 0; -+} -+ -+static int rp1_pio_release(struct inode *inode, struct file *filp) -+{ -+ struct rp1_pio_client *client = filp->private_data; -+ struct rp1_pio_device *pio = client->pio; -+ uint claimed_dmas = client->claimed_dmas; -+ int i; -+ -+ /* Free any allocated resources */ -+ -+ for (i = 0; claimed_dmas; i++) { -+ uint mask = (1 << i); -+ -+ if (claimed_dmas & mask) { -+ struct dma_info *dma = &pio->dma_configs[i >> 1][i & 1]; -+ -+ claimed_dmas &= ~mask; -+ rp1_pio_sm_dma_free(&pio->pdev->dev, dma); -+ } -+ } -+ -+ spin_lock(&pio->lock); -+ pio->claimed_dmas &= ~client->claimed_dmas; -+ spin_unlock(&pio->lock); -+ -+ if (client->claimed_sms) { -+ struct rp1_pio_sm_set_enabled_args se_args = { -+ .mask = client->claimed_sms, .enable = 0 -+ }; -+ struct rp1_pio_sm_claim_args uc_args = { -+ .mask = client->claimed_sms -+ }; -+ -+ rp1_pio_sm_set_enabled(client, &se_args); -+ rp1_pio_sm_unclaim(client, &uc_args); -+ } -+ -+ if (client->claimed_instrs) -+ rp1_pio_remove_instrs(pio, client->claimed_instrs); -+ -+ /* Reinitialise the SM? */ -+ -+ kfree(client); -+ -+ return 0; -+} -+ -+static long rp1_pio_ioctl(struct file *filp, unsigned int ioctl_num, -+ unsigned long ioctl_param) -+{ -+ struct rp1_pio_client *client = filp->private_data; -+ struct device *dev = &client->pio->pdev->dev; -+ void __user *argp = (void __user *)ioctl_param; -+ int nr = _IOC_NR(ioctl_num); -+ int sz = _IOC_SIZE(ioctl_num); -+ struct handler_info *hdlr = &ioctl_handlers[nr]; -+ uint32_t argbuf[MAX_ARG_SIZE/sizeof(uint32_t)]; -+ int ret; -+ -+ if (nr >= ARRAY_SIZE(ioctl_handlers) || !hdlr->func) { -+ dev_err(dev, "unknown ioctl: %x\n", ioctl_num); -+ return -EOPNOTSUPP; -+ } -+ -+ if (sz != hdlr->argsize) { -+ dev_err(dev, "wrong %s argsize (expected %d, got %d)\n", -+ hdlr->name, hdlr->argsize, sz); -+ return -EINVAL; -+ } -+ -+ if (copy_from_user(argbuf, argp, sz)) -+ return -EFAULT; -+ -+ ret = (hdlr->func)(client, argbuf); -+ dev_dbg(dev, "%s: %s -> %d\n", __func__, hdlr->name, ret); -+ if (ret > 0) { -+ if (copy_to_user(argp, argbuf, ret)) -+ ret = -EFAULT; -+ } -+ -+ return ret; -+} -+ -+const struct file_operations rp1_pio_fops = { -+ .owner = THIS_MODULE, -+ .open = rp1_pio_open, -+ .release = rp1_pio_release, -+ .unlocked_ioctl = rp1_pio_ioctl, -+}; -+ -+static int rp1_pio_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct resource *ioresource; -+ struct rp1_pio_device *pio; -+ struct rp1_firmware *fw; -+ uint32_t op_count = 0; -+ uint32_t op_base = 0; -+ struct device *cdev; -+ char dev_name[16]; -+ void *p; -+ int ret; -+ int i; -+ -+ /* Run-time check for a build-time misconfiguration */ -+ for (i = 0; i < ARRAY_SIZE(ioctl_handlers); i++) { -+ struct handler_info *hdlr = &ioctl_handlers[i]; -+ -+ if (WARN_ON(hdlr->argsize > MAX_ARG_SIZE)) -+ return -EINVAL; -+ } -+ -+ fw = devm_rp1_firmware_get(dev, dev->of_node); -+ if (IS_ERR(fw)) -+ return PTR_ERR(fw); -+ -+ ret = rp1_firmware_get_feature(fw, FOURCC_PIO, &op_base, &op_count); -+ if (ret < 0) -+ return ret; -+ -+ pio = devm_kzalloc(&pdev->dev, sizeof(*pio), GFP_KERNEL); -+ if (!pio) -+ return -ENOMEM; -+ -+ platform_set_drvdata(pdev, pio); -+ pio->fw_pio_base = op_base; -+ pio->fw_pio_count = op_count; -+ pio->pdev = pdev; -+ pio->fw = fw; -+ spin_lock_init(&pio->lock); -+ mutex_init(&pio->instr_mutex); -+ -+ p = devm_platform_get_and_ioremap_resource(pdev, 0, &ioresource); -+ if (IS_ERR(p)) -+ return PTR_ERR(p); -+ -+ pio->phys_addr = ioresource->start; -+ -+ ret = alloc_chrdev_region(&pio->dev_num, 0, 1, DRIVER_NAME); -+ if (ret < 0) { -+ dev_err(dev, "alloc_chrdev_region failed (rc=%d)\n", ret); -+ goto out_err; -+ } -+ -+ cdev_init(&pio->cdev, &rp1_pio_fops); -+ ret = cdev_add(&pio->cdev, pio->dev_num, 1); -+ if (ret) { -+ dev_err(dev, "cdev_add failed (err %d)\n", ret); -+ goto out_unregister; -+ } -+ -+ pio->dev_class = class_create(DRIVER_NAME); -+ if (IS_ERR(pio->dev_class)) { -+ ret = PTR_ERR(pio->dev_class); -+ dev_err(dev, "class_create failed (err %d)\n", ret); -+ goto out_cdev_del; -+ } -+ pdev->id = of_alias_get_id(pdev->dev.of_node, "pio"); -+ if (pdev->id < 0) { -+ dev_err(dev, "alias is missing\n"); -+ return -EINVAL; -+ goto out_class_destroy; -+ } -+ sprintf(dev_name, "pio%d", pdev->id); -+ cdev = device_create(pio->dev_class, NULL, pio->dev_num, NULL, dev_name); -+ if (IS_ERR(cdev)) { -+ ret = PTR_ERR(cdev); -+ dev_err(dev, "%s: device_create failed (err %d)\n", __func__, ret); -+ goto out_class_destroy; -+ } -+ -+ g_pio = pio; -+ -+ dev_info(dev, "Created instance as %s\n", dev_name); -+ return 0; -+ -+out_class_destroy: -+ class_destroy(pio->dev_class); -+ -+out_cdev_del: -+ cdev_del(&pio->cdev); -+ -+out_unregister: -+ unregister_chrdev_region(pio->dev_num, 1); -+ -+out_err: -+ return ret; -+} -+ -+static void rp1_pio_remove(struct platform_device *pdev) -+{ -+ struct rp1_pio_device *pio = platform_get_drvdata(pdev); -+ -+ /* There should be no clients */ -+ -+ if (g_pio == pio) -+ g_pio = NULL; -+} -+ -+static const struct of_device_id rp1_pio_ids[] = { -+ { .compatible = "raspberrypi,rp1-pio" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, rp1_pio_ids); -+ -+static struct platform_driver rp1_pio_driver = { -+ .driver = { -+ .name = "rp1-pio", -+ .of_match_table = of_match_ptr(rp1_pio_ids), -+ }, -+ .probe = rp1_pio_probe, -+ .remove_new = rp1_pio_remove, -+ .shutdown = rp1_pio_remove, -+}; -+ -+module_platform_driver(rp1_pio_driver); -+ -+MODULE_DESCRIPTION("PIO controller driver for Raspberry Pi RP1"); -+MODULE_AUTHOR("Phil Elwell"); -+MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/include/uapi/misc/rp1_pio_if.h -@@ -0,0 +1,212 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (c) 2023-24 Raspberry Pi Ltd. -+ * All rights reserved. -+ */ -+#ifndef _PIO_RP1_IF_H -+#define _PIO_RP1_IF_H -+ -+#include -+ -+#define RP1_PIO_INSTRUCTION_COUNT 32 -+#define RP1_PIO_SM_COUNT 4 -+#define RP1_PIO_GPIO_COUNT 28 -+#define RP1_GPIO_FUNC_PIO 7 -+ -+#define RP1_PIO_ORIGIN_ANY ((uint16_t)(~0)) -+ -+#define RP1_PIO_DIR_TO_SM 0 -+#define RP1_PIO_DIR_FROM_SM 1 -+#define RP1_PIO_DIR_COUNT 2 -+ -+typedef struct { -+ uint32_t clkdiv; -+ uint32_t execctrl; -+ uint32_t shiftctrl; -+ uint32_t pinctrl; -+} rp1_pio_sm_config; -+ -+struct rp1_pio_add_program_args { -+ uint16_t num_instrs; -+ uint16_t origin; -+ uint16_t instrs[RP1_PIO_INSTRUCTION_COUNT]; -+}; -+ -+struct rp1_pio_remove_program_args { -+ uint16_t num_instrs; -+ uint16_t origin; -+}; -+ -+struct rp1_pio_sm_claim_args { -+ uint16_t mask; -+}; -+ -+struct rp1_pio_sm_init_args { -+ uint16_t sm; -+ uint16_t initial_pc; -+ rp1_pio_sm_config config; -+}; -+ -+struct rp1_pio_sm_set_config_args { -+ uint16_t sm; -+ uint16_t rsvd; -+ rp1_pio_sm_config config; -+}; -+ -+struct rp1_pio_sm_exec_args { -+ uint16_t sm; -+ uint16_t instr; -+ uint8_t blocking; -+ uint8_t rsvd; -+}; -+ -+struct rp1_pio_sm_clear_fifos_args { -+ uint16_t sm; -+}; -+ -+struct rp1_pio_sm_set_clkdiv_args { -+ uint16_t sm; -+ uint16_t div_int; -+ uint8_t div_frac; -+ uint8_t rsvd; -+}; -+ -+struct rp1_pio_sm_set_pins_args { -+ uint16_t sm; -+ uint16_t rsvd; -+ uint32_t values; -+ uint32_t mask; -+}; -+ -+struct rp1_pio_sm_set_pindirs_args { -+ uint16_t sm; -+ uint16_t rsvd; -+ uint32_t dirs; -+ uint32_t mask; -+}; -+ -+struct rp1_pio_sm_set_enabled_args { -+ uint16_t mask; -+ uint8_t enable; -+ uint8_t rsvd; -+}; -+ -+struct rp1_pio_sm_restart_args { -+ uint16_t mask; -+}; -+ -+struct rp1_pio_sm_clkdiv_restart_args { -+ uint16_t mask; -+}; -+ -+struct rp1_pio_sm_enable_sync_args { -+ uint16_t mask; -+}; -+ -+struct rp1_pio_sm_put_args { -+ uint16_t sm; -+ uint8_t blocking; -+ uint8_t rsvd; -+ uint32_t data; -+}; -+ -+struct rp1_pio_sm_get_args { -+ uint16_t sm; -+ uint8_t blocking; -+ uint8_t rsvd; -+ uint32_t data; /* IN/OUT */ -+}; -+ -+struct rp1_pio_sm_set_dmactrl_args { -+ uint16_t sm; -+ uint8_t is_tx; -+ uint8_t rsvd; -+ uint32_t ctrl; -+}; -+ -+struct rp1_gpio_init_args { -+ uint16_t gpio; -+}; -+ -+struct rp1_gpio_set_function_args { -+ uint16_t gpio; -+ uint16_t fn; -+}; -+ -+struct rp1_gpio_set_pulls_args { -+ uint16_t gpio; -+ uint8_t up; -+ uint8_t down; -+}; -+ -+struct rp1_gpio_set_args { -+ uint16_t gpio; -+ uint16_t value; -+}; -+ -+struct rp1_pio_sm_config_xfer_args { -+ uint16_t sm; -+ uint16_t dir; -+ uint16_t buf_size; -+ uint16_t buf_count; -+}; -+ -+struct rp1_pio_sm_xfer_data_args { -+ uint16_t sm; -+ uint16_t dir; -+ uint16_t data_bytes; -+ void *data; -+}; -+ -+struct rp1_access_hw_args { -+ uint32_t addr; -+ uint32_t len; -+ void *data; -+}; -+ -+#define PIO_IOC_MAGIC 102 -+ -+#define PIO_IOC_SM_CONFIG_XFER _IOW(PIO_IOC_MAGIC, 0, struct rp1_pio_sm_config_xfer_args) -+#define PIO_IOC_SM_XFER_DATA _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args) -+ -+#ifdef CONFIG_COMPAT -+//XXX #define PIO_IOC_SM_XFER_DATA32 _IOW(PIO_IOC_MAGIC, 2, struct pio_sm_xfer_data_args) -+#endif -+ -+#define PIO_IOC_READ_HW _IOW(PIO_IOC_MAGIC, 8, struct rp1_access_hw_args) -+#define PIO_IOC_WRITE_HW _IOW(PIO_IOC_MAGIC, 9, struct rp1_access_hw_args) -+ -+#define PIO_IOC_CAN_ADD_PROGRAM _IOW(PIO_IOC_MAGIC, 10, struct rp1_pio_add_program_args) -+#define PIO_IOC_ADD_PROGRAM _IOW(PIO_IOC_MAGIC, 11, struct rp1_pio_add_program_args) -+#define PIO_IOC_REMOVE_PROGRAM _IOW(PIO_IOC_MAGIC, 12, struct rp1_pio_remove_program_args) -+#define PIO_IOC_CLEAR_INSTR_MEM _IO(PIO_IOC_MAGIC, 13) -+ -+#define PIO_IOC_SM_CLAIM _IOW(PIO_IOC_MAGIC, 20, struct rp1_pio_sm_claim_args) -+#define PIO_IOC_SM_UNCLAIM _IOW(PIO_IOC_MAGIC, 21, struct rp1_pio_sm_claim_args) -+#define PIO_IOC_SM_IS_CLAIMED _IOW(PIO_IOC_MAGIC, 22, struct rp1_pio_sm_claim_args) -+ -+#define PIO_IOC_SM_INIT _IOW(PIO_IOC_MAGIC, 30, struct rp1_pio_sm_init_args) -+#define PIO_IOC_SM_SET_CONFIG _IOW(PIO_IOC_MAGIC, 31, struct rp1_pio_sm_set_config_args) -+#define PIO_IOC_SM_EXEC _IOW(PIO_IOC_MAGIC, 32, struct rp1_pio_sm_exec_args) -+#define PIO_IOC_SM_CLEAR_FIFOS _IOW(PIO_IOC_MAGIC, 33, struct rp1_pio_sm_clear_fifos_args) -+#define PIO_IOC_SM_SET_CLKDIV _IOW(PIO_IOC_MAGIC, 34, struct rp1_pio_sm_set_clkdiv_args) -+#define PIO_IOC_SM_SET_PINS _IOW(PIO_IOC_MAGIC, 35, struct rp1_pio_sm_set_pins_args) -+#define PIO_IOC_SM_SET_PINDIRS _IOW(PIO_IOC_MAGIC, 36, struct rp1_pio_sm_set_pindirs_args) -+#define PIO_IOC_SM_SET_ENABLED _IOW(PIO_IOC_MAGIC, 37, struct rp1_pio_sm_set_enabled_args) -+#define PIO_IOC_SM_RESTART _IOW(PIO_IOC_MAGIC, 38, struct rp1_pio_sm_restart_args) -+#define PIO_IOC_SM_CLKDIV_RESTART _IOW(PIO_IOC_MAGIC, 39, struct rp1_pio_sm_restart_args) -+#define PIO_IOC_SM_ENABLE_SYNC _IOW(PIO_IOC_MAGIC, 40, struct rp1_pio_sm_enable_sync_args) -+#define PIO_IOC_SM_PUT _IOW(PIO_IOC_MAGIC, 41, struct rp1_pio_sm_put_args) -+#define PIO_IOC_SM_GET _IOWR(PIO_IOC_MAGIC, 42, struct rp1_pio_sm_get_args) -+#define PIO_IOC_SM_SET_DMACTRL _IOW(PIO_IOC_MAGIC, 43, struct rp1_pio_sm_set_dmactrl_args) -+ -+#define PIO_IOC_GPIO_INIT _IOW(PIO_IOC_MAGIC, 50, struct rp1_gpio_init_args) -+#define PIO_IOC_GPIO_SET_FUNCTION _IOW(PIO_IOC_MAGIC, 51, struct rp1_gpio_set_function_args) -+#define PIO_IOC_GPIO_SET_PULLS _IOW(PIO_IOC_MAGIC, 52, struct rp1_gpio_set_pulls_args) -+#define PIO_IOC_GPIO_SET_OUTOVER _IOW(PIO_IOC_MAGIC, 53, struct rp1_gpio_set_args) -+#define PIO_IOC_GPIO_SET_INOVER _IOW(PIO_IOC_MAGIC, 54, struct rp1_gpio_set_args) -+#define PIO_IOC_GPIO_SET_OEOVER _IOW(PIO_IOC_MAGIC, 55, struct rp1_gpio_set_args) -+#define PIO_IOC_GPIO_SET_INPUT_ENABLED _IOW(PIO_IOC_MAGIC, 56, struct rp1_gpio_set_args) -+#define PIO_IOC_GPIO_SET_DRIVE_STRENGTH _IOW(PIO_IOC_MAGIC, 57, struct rp1_gpio_set_args) -+ -+#endif diff --git a/target/linux/bcm27xx/patches-6.6/950-1353-pwm-Add-GPIO-PWM-driver.patch b/target/linux/bcm27xx/patches-6.6/950-1353-pwm-Add-GPIO-PWM-driver.patch new file mode 100644 index 0000000000..5b89f60bca --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1353-pwm-Add-GPIO-PWM-driver.patch @@ -0,0 +1,330 @@ +From 3ab72fc21ea8576e59f6aad10bd6b1a0eae6e5eb Mon Sep 17 00:00:00 2001 +From: Vincent Whitchurch +Date: Tue, 4 Jun 2024 23:00:41 +0200 +Subject: [PATCH] pwm: Add GPIO PWM driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +commit 7f61257cd6e1ad4769b4b819668cab00f68f2556 upstream. + +Add a software PWM which toggles a GPIO from a high-resolution timer. + +This will naturally not be as accurate or as efficient as a hardware +PWM, but it is useful in some cases. I have for example used it for +evaluating LED brightness handling (via leds-pwm) on a board where the +LED was just hooked up to a GPIO, and for a simple verification of the +timer frequency on another platform. + +Since high-resolution timers are used, sleeping GPIO chips are not +supported and are rejected in the probe function. + +Signed-off-by: Vincent Whitchurch +Co-developed-by: Stefan Wahren +Signed-off-by: Stefan Wahren +Co-developed-by: Linus Walleij +Reviewed-by: Andy Shevchenko +Signed-off-by: Linus Walleij +Reviewed-by: Dhruva Gole +Link: https://lore.kernel.org/r/20240604-pwm-gpio-v7-2-6b67cf60db92@linaro.org +Signed-off-by: Uwe Kleine-König +Signed-off-by: Tim Gover + +pwm: Backport pwm-gpio.c to rpi-6.6.y +--- + .../driver-api/gpio/drivers-on-gpio.rst | 7 +- + drivers/pwm/Kconfig | 11 + + drivers/pwm/Makefile | 1 + + drivers/pwm/pwm-gpio.c | 240 ++++++++++++++++++ + 4 files changed, 258 insertions(+), 1 deletion(-) + create mode 100644 drivers/pwm/pwm-gpio.c + +--- a/Documentation/driver-api/gpio/drivers-on-gpio.rst ++++ b/Documentation/driver-api/gpio/drivers-on-gpio.rst +@@ -27,7 +27,12 @@ hardware descriptions such as device tre + to the lines for a more permanent solution of this type. + + - gpio-beeper: drivers/input/misc/gpio-beeper.c is used to provide a beep from +- an external speaker connected to a GPIO line. ++ an external speaker connected to a GPIO line. (If the beep is controlled by ++ off/on, for an actual PWM waveform, see pwm-gpio below.) ++ ++- pwm-gpio: drivers/pwm/pwm-gpio.c is used to toggle a GPIO with a high ++ resolution timer producing a PWM waveform on the GPIO line, as well as ++ Linux high resolution timers can do. + + - extcon-gpio: drivers/extcon/extcon-gpio.c is used when you need to read an + external connector status, such as a headset line for an audio driver or an +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -217,6 +217,17 @@ config PWM_FSL_FTM + To compile this driver as a module, choose M here: the module + will be called pwm-fsl-ftm. + ++config PWM_GPIO ++ tristate "GPIO PWM support" ++ depends on GPIOLIB ++ depends on HIGH_RES_TIMERS ++ help ++ Generic PWM framework driver for software PWM toggling a GPIO pin ++ from kernel high-resolution timers. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-gpio. ++ + config PWM_HIBVT + tristate "HiSilicon BVT PWM support" + depends on ARCH_HISI || COMPILE_TEST +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -18,6 +18,7 @@ obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec + obj-$(CONFIG_PWM_DWC) += pwm-dwc.o + obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o + obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o ++obj-$(CONFIG_PWM_GPIO) += pwm-gpio.o + obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o + obj-$(CONFIG_PWM_IMG) += pwm-img.o + obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o +--- /dev/null ++++ b/drivers/pwm/pwm-gpio.c +@@ -0,0 +1,240 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Generic software PWM for modulating GPIOs ++ * ++ * Copyright (C) 2020 Axis Communications AB ++ * Copyright (C) 2020 Nicola Di Lieto ++ * Copyright (C) 2024 Stefan Wahren ++ * Copyright (C) 2024 Linus Walleij ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct pwm_gpio { ++ struct hrtimer gpio_timer; ++ struct gpio_desc *gpio; ++ struct pwm_state state; ++ struct pwm_state next_state; ++ ++ /* Protect internal state between pwm_ops and hrtimer */ ++ spinlock_t lock; ++ ++ bool changing; ++ bool running; ++ bool level; ++ struct pwm_chip chip; ++}; ++ ++static void pwm_gpio_round(struct pwm_state *dest, const struct pwm_state *src) ++{ ++ u64 dividend; ++ u32 remainder; ++ ++ *dest = *src; ++ ++ /* Round down to hrtimer resolution */ ++ dividend = dest->period; ++ remainder = do_div(dividend, hrtimer_resolution); ++ dest->period -= remainder; ++ ++ dividend = dest->duty_cycle; ++ remainder = do_div(dividend, hrtimer_resolution); ++ dest->duty_cycle -= remainder; ++} ++ ++static u64 pwm_gpio_toggle(struct pwm_gpio *gpwm, bool level) ++{ ++ const struct pwm_state *state = &gpwm->state; ++ bool invert = state->polarity == PWM_POLARITY_INVERSED; ++ ++ gpwm->level = level; ++ gpiod_set_value(gpwm->gpio, gpwm->level ^ invert); ++ ++ if (!state->duty_cycle || state->duty_cycle == state->period) { ++ gpwm->running = false; ++ return 0; ++ } ++ ++ gpwm->running = true; ++ return level ? state->duty_cycle : state->period - state->duty_cycle; ++} ++ ++static enum hrtimer_restart pwm_gpio_timer(struct hrtimer *gpio_timer) ++{ ++ struct pwm_gpio *gpwm = container_of(gpio_timer, struct pwm_gpio, ++ gpio_timer); ++ u64 next_toggle; ++ bool new_level; ++ ++ guard(spinlock_irqsave)(&gpwm->lock); ++ ++ /* Apply new state at end of current period */ ++ if (!gpwm->level && gpwm->changing) { ++ gpwm->changing = false; ++ gpwm->state = gpwm->next_state; ++ new_level = !!gpwm->state.duty_cycle; ++ } else { ++ new_level = !gpwm->level; ++ } ++ ++ next_toggle = pwm_gpio_toggle(gpwm, new_level); ++ if (next_toggle) ++ hrtimer_forward(gpio_timer, hrtimer_get_expires(gpio_timer), ++ ns_to_ktime(next_toggle)); ++ ++ return next_toggle ? HRTIMER_RESTART : HRTIMER_NORESTART; ++} ++ ++static int pwm_gpio_apply(struct pwm_chip *chip, struct pwm_device *pwm, ++ const struct pwm_state *state) ++{ ++ struct pwm_gpio *gpwm = container_of(chip, struct pwm_gpio, chip); ++ bool invert = state->polarity == PWM_POLARITY_INVERSED; ++ ++ if (state->duty_cycle && state->duty_cycle < hrtimer_resolution) ++ return -EINVAL; ++ ++ if (state->duty_cycle != state->period && ++ (state->period - state->duty_cycle < hrtimer_resolution)) ++ return -EINVAL; ++ ++ if (!state->enabled) { ++ hrtimer_cancel(&gpwm->gpio_timer); ++ } else if (!gpwm->running) { ++ int ret; ++ ++ /* ++ * This just enables the output, but pwm_gpio_toggle() ++ * really starts the duty cycle. ++ */ ++ ret = gpiod_direction_output(gpwm->gpio, invert); ++ if (ret) ++ return ret; ++ } ++ ++ guard(spinlock_irqsave)(&gpwm->lock); ++ ++ if (!state->enabled) { ++ pwm_gpio_round(&gpwm->state, state); ++ gpwm->running = false; ++ gpwm->changing = false; ++ ++ gpiod_set_value(gpwm->gpio, invert); ++ } else if (gpwm->running) { ++ pwm_gpio_round(&gpwm->next_state, state); ++ gpwm->changing = true; ++ } else { ++ unsigned long next_toggle; ++ ++ pwm_gpio_round(&gpwm->state, state); ++ gpwm->changing = false; ++ ++ next_toggle = pwm_gpio_toggle(gpwm, !!state->duty_cycle); ++ if (next_toggle) ++ hrtimer_start(&gpwm->gpio_timer, next_toggle, ++ HRTIMER_MODE_REL); ++ } ++ ++ return 0; ++} ++ ++static int pwm_gpio_get_state(struct pwm_chip *chip, struct pwm_device *pwm, ++ struct pwm_state *state) ++{ ++ struct pwm_gpio *gpwm = container_of(chip, struct pwm_gpio, chip); ++ ++ guard(spinlock_irqsave)(&gpwm->lock); ++ ++ if (gpwm->changing) ++ *state = gpwm->next_state; ++ else ++ *state = gpwm->state; ++ ++ return 0; ++} ++ ++static const struct pwm_ops pwm_gpio_ops = { ++ .apply = pwm_gpio_apply, ++ .get_state = pwm_gpio_get_state, ++}; ++ ++static void pwm_gpio_disable_hrtimer(void *data) ++{ ++ struct pwm_gpio *gpwm = data; ++ ++ hrtimer_cancel(&gpwm->gpio_timer); ++} ++ ++static int pwm_gpio_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct pwm_chip *chip; ++ struct pwm_gpio *gpwm; ++ int ret; ++ ++ gpwm = devm_kzalloc(&pdev->dev, sizeof(*gpwm), GFP_KERNEL); ++ if (IS_ERR(gpwm)) ++ return PTR_ERR(gpwm); ++ ++ chip = &gpwm->chip; ++ ++ spin_lock_init(&gpwm->lock); ++ ++ gpwm->gpio = devm_gpiod_get(dev, NULL, GPIOD_ASIS); ++ if (IS_ERR(gpwm->gpio)) ++ return dev_err_probe(dev, PTR_ERR(gpwm->gpio), ++ "%pfw: could not get gpio\n", ++ dev_fwnode(dev)); ++ ++ if (gpiod_cansleep(gpwm->gpio)) ++ return dev_err_probe(dev, -EINVAL, ++ "%pfw: sleeping GPIO not supported\n", ++ dev_fwnode(dev)); ++ ++ chip->dev = dev; ++ chip->ops = &pwm_gpio_ops; ++ chip->atomic = true; ++ chip->npwm = 1; ++ ++ hrtimer_init(&gpwm->gpio_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ ret = devm_add_action_or_reset(dev, pwm_gpio_disable_hrtimer, gpwm); ++ if (ret) ++ return ret; ++ ++ gpwm->gpio_timer.function = pwm_gpio_timer; ++ ++ return devm_pwmchip_add(dev, chip); ++} ++ ++static const struct of_device_id pwm_gpio_dt_ids[] = { ++ { .compatible = "pwm-gpio" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, pwm_gpio_dt_ids); ++ ++static struct platform_driver pwm_gpio_driver = { ++ .driver = { ++ .name = "pwm-gpio", ++ .of_match_table = pwm_gpio_dt_ids, ++ }, ++ .probe = pwm_gpio_probe, ++}; ++module_platform_driver(pwm_gpio_driver); ++ ++MODULE_DESCRIPTION("PWM GPIO driver"); ++MODULE_AUTHOR("Vincent Whitchurch"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/bcm27xx/patches-6.6/950-1354-fixup-musc-add-RP1-PIO-driver.patch b/target/linux/bcm27xx/patches-6.6/950-1354-fixup-musc-add-RP1-PIO-driver.patch deleted file mode 100644 index cd197f7a7b..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-1354-fixup-musc-add-RP1-PIO-driver.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 1b5acd42281ad102b79f4e1794f0a0cccdafda05 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Sat, 16 Nov 2024 16:53:31 +0000 -Subject: [PATCH] fixup! misc: Add RP1 PIO driver - -Signed-off-by: Phil Elwell ---- - include/uapi/misc/rp1_pio_if.h | 6 +----- - 1 file changed, 1 insertion(+), 5 deletions(-) - ---- a/include/uapi/misc/rp1_pio_if.h -+++ b/include/uapi/misc/rp1_pio_if.h -@@ -1,4 +1,4 @@ --/* SPDX-License-Identifier: GPL-2.0 */ -+/* SPDX-License-Identifier: GPL-2.0 + WITH Linux-syscall-note */ - /* - * Copyright (c) 2023-24 Raspberry Pi Ltd. - * All rights reserved. -@@ -169,10 +169,6 @@ struct rp1_access_hw_args { - #define PIO_IOC_SM_CONFIG_XFER _IOW(PIO_IOC_MAGIC, 0, struct rp1_pio_sm_config_xfer_args) - #define PIO_IOC_SM_XFER_DATA _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args) - --#ifdef CONFIG_COMPAT --//XXX #define PIO_IOC_SM_XFER_DATA32 _IOW(PIO_IOC_MAGIC, 2, struct pio_sm_xfer_data_args) --#endif -- - #define PIO_IOC_READ_HW _IOW(PIO_IOC_MAGIC, 8, struct rp1_access_hw_args) - #define PIO_IOC_WRITE_HW _IOW(PIO_IOC_MAGIC, 9, struct rp1_access_hw_args) - diff --git a/target/linux/bcm27xx/patches-6.6/950-1355-dtoverlay-Add-a-dtoverlay-for-pwm-gpio.patch b/target/linux/bcm27xx/patches-6.6/950-1355-dtoverlay-Add-a-dtoverlay-for-pwm-gpio.patch new file mode 100644 index 0000000000..65c6be6f3f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1355-dtoverlay-Add-a-dtoverlay-for-pwm-gpio.patch @@ -0,0 +1,79 @@ +From ff0fe12ab875d587348b6f2b9e73ae928049ebee Mon Sep 17 00:00:00 2001 +From: Tim Gover +Date: Thu, 31 Oct 2024 16:12:54 +0000 +Subject: [PATCH] dtoverlay: Add a dtoverlay for pwm-gpio + +Signed-off-by: Tim Gover +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 6 +++ + .../boot/dts/overlays/pwm-gpio-overlay.dts | 38 +++++++++++++++++++ + 3 files changed, 45 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/pwm-gpio-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -217,6 +217,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + proto-codec.dtbo \ + pwm.dtbo \ + pwm-2chan.dtbo \ ++ pwm-gpio.dtbo \ + pwm-ir-tx.dtbo \ + pwm1.dtbo \ + qca7000.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -3903,6 +3903,12 @@ Params: pin Output p + clock PWM clock frequency (informational) + + ++Name: pwm-gpio ++Info: Configures the software PWM GPIO driver ++Load: dtoverlay=pwm-gpio,= ++Params: gpio Output pin (default 4) ++ ++ + Name: pwm-ir-tx + Info: Use GPIO pin as pwm-assisted infrared transmitter output. + This is an alternative to "gpio-ir-tx". pwm-ir-tx makes use +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pwm-gpio-overlay.dts +@@ -0,0 +1,38 @@ ++// Device tree overlay for software GPIO PWM. ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ pwm_gpio_pins: pwm_gpio_pins@4 { ++ brcm,pins = <4>; /* gpio 4 */ ++ brcm,function = <1>; /* output */ ++ brcm,pull = <0>; /* pull-none */ ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target-path = "/"; ++ __overlay__ { ++ pwm_gpio: pwm_gpio@4 { ++ compatible = "pwm-gpio"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_gpio_pins>; ++ gpios = <&gpio 4 0>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ gpio = <&pwm_gpio>,"gpios:4", ++ <&pwm_gpio_pins>,"brcm,pins:0", ++ /* modify reg values to allow multiple instantiation */ ++ <&pwm_gpio>,"reg:0", ++ <&pwm_gpio_pins>,"reg:0"; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-6.6/950-1355-misc-rp1-pio-Add-an-in-kernel.patch b/target/linux/bcm27xx/patches-6.6/950-1355-misc-rp1-pio-Add-an-in-kernel.patch deleted file mode 100644 index 6accaf000e..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-1355-misc-rp1-pio-Add-an-in-kernel.patch +++ /dev/null @@ -1,1797 +0,0 @@ -From 2819a61eb000c207589c97eef9d69a237c6cfdf3 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Fri, 8 Nov 2024 09:31:38 +0000 -Subject: [PATCH] misc: rp1-pio: Add an in-kernel API - -The header file linux/pio_rp1.h adds a pico-sdk-like interface to the -RP1 PIO subsystem for other drivers. - -Signed-off-by: Phil Elwell ---- - drivers/misc/rp1-pio.c | 169 ++++-- - include/linux/pio_instructions.h | 481 +++++++++++++++++ - include/linux/pio_rp1.h | 873 +++++++++++++++++++++++++++++++ - 3 files changed, 1474 insertions(+), 49 deletions(-) - create mode 100644 include/linux/pio_instructions.h - create mode 100644 include/linux/pio_rp1.h - ---- a/drivers/misc/rp1-pio.c -+++ b/drivers/misc/rp1-pio.c -@@ -1,15 +1,17 @@ - // SPDX-License-Identifier: GPL-2.0 --// PIO driver for RP1 --// --// Copyright (C) 2023-2024 Raspberry Pi Ltd. --// --// Parts of this driver are based on: --// - vcio.c, by Noralf Trønnes --// Copyright (C) 2010 Broadcom --// Copyright (C) 2015 Noralf Trønnes --// Copyright (C) 2021 Raspberry Pi (Trading) Ltd. --// - bcm2835_smi.c & bcm2835_smi_dev.c by Luke Wren --// Copyright (c) 2015 Raspberry Pi (Trading) Ltd. -+/* -+ * PIO driver for RP1 -+ * -+ * Copyright (C) 2023-2024 Raspberry Pi Ltd. -+ * -+ * Parts of this driver are based on: -+ * - vcio.c, by Noralf Trønnes -+ * Copyright (C) 2010 Broadcom -+ * Copyright (C) 2015 Noralf Trønnes -+ * Copyright (C) 2021 Raspberry Pi (Trading) Ltd. -+ * - bcm2835_smi.c & bcm2835_smi_dev.c by Luke Wren -+ * Copyright (c) 2015 Raspberry Pi (Trading) Ltd. -+ */ - - #include - #include -@@ -97,6 +99,7 @@ struct rp1_pio_client { - uint32_t claimed_sms; - uint32_t claimed_instrs; - uint32_t claimed_dmas; -+ int error; - }; - - static struct rp1_pio_device *g_pio; -@@ -195,7 +198,7 @@ static int rp1_pio_find_program(struct r - return -1; - } - --static int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param) -+int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_add_program_args *args = param; - struct rp1_pio_device *pio = client->pio; -@@ -217,8 +220,9 @@ static int rp1_pio_can_add_program(struc - return rp1_pio_message(pio, PIO_CAN_ADD_PROGRAM, args, - offsetof(struct rp1_pio_add_program_args, instrs)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_can_add_program); - --static int rp1_pio_add_program(struct rp1_pio_client *client, void *param) -+int rp1_pio_add_program(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_add_program_args *args = param; - struct rp1_pio_device *pio = client->pio; -@@ -254,6 +258,7 @@ static int rp1_pio_add_program(struct rp - mutex_unlock(&pio->instr_mutex); - return offset; - } -+EXPORT_SYMBOL_GPL(rp1_pio_add_program); - - static void rp1_pio_remove_instrs(struct rp1_pio_device *pio, uint32_t mask) - { -@@ -277,7 +282,7 @@ static void rp1_pio_remove_instrs(struct - mutex_unlock(&pio->instr_mutex); - } - --static int rp1_pio_remove_program(struct rp1_pio_client *client, void *param) -+int rp1_pio_remove_program(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_remove_program_args *args = param; - uint32_t used_mask; -@@ -296,8 +301,9 @@ static int rp1_pio_remove_program(struct - } - return ret; - } -+EXPORT_SYMBOL_GPL(rp1_pio_remove_program); - --static int rp1_pio_clear_instr_mem(struct rp1_pio_client *client, void *param) -+int rp1_pio_clear_instr_mem(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_device *pio = client->pio; - -@@ -309,8 +315,9 @@ static int rp1_pio_clear_instr_mem(struc - mutex_unlock(&pio->instr_mutex); - return 0; - } -+EXPORT_SYMBOL_GPL(rp1_pio_clear_instr_mem); - --static int rp1_pio_sm_claim(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_claim(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_claim_args *args = param; - struct rp1_pio_device *pio = client->pio; -@@ -328,8 +335,9 @@ static int rp1_pio_sm_claim(struct rp1_p - mutex_unlock(&pio->instr_mutex); - return ret; - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_claim); - --static int rp1_pio_sm_unclaim(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_unclaim(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_claim_args *args = param; - struct rp1_pio_device *pio = client->pio; -@@ -341,99 +349,113 @@ static int rp1_pio_sm_unclaim(struct rp1 - mutex_unlock(&pio->instr_mutex); - return 0; - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_unclaim); - --static int rp1_pio_sm_is_claimed(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_is_claimed(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_claim_args *args = param; - - return rp1_pio_message(client->pio, PIO_SM_IS_CLAIMED, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_is_claimed); - --static int rp1_pio_sm_init(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_init(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_init_args *args = param; - - return rp1_pio_message(client->pio, PIO_SM_INIT, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_init); - --static int rp1_pio_sm_set_config(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_set_config(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_set_config_args *args = param; - - return rp1_pio_message(client->pio, PIO_SM_SET_CONFIG, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_set_config); - --static int rp1_pio_sm_exec(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_exec(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_exec_args *args = param; - - return rp1_pio_message(client->pio, PIO_SM_EXEC, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_exec); - --static int rp1_pio_sm_clear_fifos(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_clear_fifos(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_clear_fifos_args *args = param; - - return rp1_pio_message(client->pio, PIO_SM_CLEAR_FIFOS, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_clear_fifos); - --static int rp1_pio_sm_set_clkdiv(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_set_clkdiv(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_set_clkdiv_args *args = param; - - return rp1_pio_message(client->pio, PIO_SM_SET_CLKDIV, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_set_clkdiv); - --static int rp1_pio_sm_set_pins(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_set_pins(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_set_pins_args *args = param; - - return rp1_pio_message(client->pio, PIO_SM_SET_PINS, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_set_pins); - --static int rp1_pio_sm_set_pindirs(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_set_pindirs(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_set_pindirs_args *args = param; - - return rp1_pio_message(client->pio, PIO_SM_SET_PINDIRS, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_set_pindirs); - --static int rp1_pio_sm_set_enabled(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_set_enabled(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_set_enabled_args *args = param; - - return rp1_pio_message(client->pio, PIO_SM_SET_ENABLED, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_set_enabled); - --static int rp1_pio_sm_restart(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_restart(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_restart_args *args = param; - - return rp1_pio_message(client->pio, PIO_SM_RESTART, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_restart); - --static int rp1_pio_sm_clkdiv_restart(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_clkdiv_restart(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_restart_args *args = param; - - return rp1_pio_message(client->pio, PIO_SM_CLKDIV_RESTART, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_clkdiv_restart); - --static int rp1_pio_sm_enable_sync(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_enable_sync(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_enable_sync_args *args = param; - - return rp1_pio_message(client->pio, PIO_SM_ENABLE_SYNC, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_enable_sync); - --static int rp1_pio_sm_put(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_put(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_put_args *args = param; - - return rp1_pio_message(client->pio, PIO_SM_PUT, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_put); - --static int rp1_pio_sm_get(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_get(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_get_args *args = param; - int ret; -@@ -444,69 +466,79 @@ static int rp1_pio_sm_get(struct rp1_pio - return offsetof(struct rp1_pio_sm_get_args, data) + ret; - return ret; - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_get); - --static int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param) -+int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param) - { - struct rp1_pio_sm_set_dmactrl_args *args = param; - - return rp1_pio_message(client->pio, PIO_SM_SET_DMACTRL, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_sm_set_dmactrl); - --static int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param) -+int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param) - { - struct rp1_gpio_init_args *args = param; - - return rp1_pio_message(client->pio, GPIO_INIT, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_gpio_init); - --static int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param) -+int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param) - { - struct rp1_gpio_set_function_args *args = param; - - return rp1_pio_message(client->pio, GPIO_SET_FUNCTION, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_function); - --static int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param) -+int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param) - { - struct rp1_gpio_set_pulls_args *args = param; - - return rp1_pio_message(client->pio, GPIO_SET_PULLS, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_pulls); - --static int rp1_pio_gpio_set_outover(struct rp1_pio_client *client, void *param) -+int rp1_pio_gpio_set_outover(struct rp1_pio_client *client, void *param) - { - struct rp1_gpio_set_args *args = param; - - return rp1_pio_message(client->pio, GPIO_SET_OUTOVER, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_outover); - --static int rp1_pio_gpio_set_inover(struct rp1_pio_client *client, void *param) -+int rp1_pio_gpio_set_inover(struct rp1_pio_client *client, void *param) - { - struct rp1_gpio_set_args *args = param; - - return rp1_pio_message(client->pio, GPIO_SET_INOVER, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_inover); - --static int rp1_pio_gpio_set_oeover(struct rp1_pio_client *client, void *param) -+int rp1_pio_gpio_set_oeover(struct rp1_pio_client *client, void *param) - { - struct rp1_gpio_set_args *args = param; - - return rp1_pio_message(client->pio, GPIO_SET_OEOVER, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_oeover); - --static int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param) -+int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param) - { - struct rp1_gpio_set_args *args = param; - - return rp1_pio_message(client->pio, GPIO_SET_INPUT_ENABLED, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_input_enabled); - --static int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param) -+int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param) - { - struct rp1_gpio_set_args *args = param; - - return rp1_pio_message(client->pio, GPIO_SET_DRIVE_STRENGTH, args, sizeof(*args)); - } -+EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_drive_strength); - - static void rp1_pio_sm_dma_callback(void *param) - { -@@ -633,7 +665,7 @@ static int rp1_pio_sm_tx_user(struct rp1 - struct device *dev = &pdev->dev; - int ret = 0; - -- // Clean the slate - we're running synchronously -+ /* Clean the slate - we're running synchronously */ - dma->head_idx = 0; - dma->tail_idx = 0; - -@@ -686,7 +718,7 @@ static int rp1_pio_sm_tx_user(struct rp1 - bytes -= copy_bytes; - } - -- // Block for completion -+ /* Block for completion */ - while (dma->tail_idx != dma->head_idx) { - if (down_timeout(&dma->buf_sem, msecs_to_jiffies(1000))) { - dev_err(dev, "DMA wait timed out\n"); -@@ -830,22 +862,22 @@ struct handler_info { - HANDLER(WRITE_HW, write_hw), - }; - --static int rp1_pio_open(struct inode *inode, struct file *filp) -+struct rp1_pio_client *pio_open(void) - { -- struct rp1_pio_device *pio = g_pio; - struct rp1_pio_client *client; - - client = kzalloc(sizeof(*client), GFP_KERNEL); -+ if (!client) -+ return ERR_PTR(-ENOMEM); - -- client->pio = pio; -- filp->private_data = client; -+ client->pio = g_pio; - -- return 0; -+ return client; - } -+EXPORT_SYMBOL_GPL(pio_open); - --static int rp1_pio_release(struct inode *inode, struct file *filp) -+void pio_close(struct rp1_pio_client *client) - { -- struct rp1_pio_client *client = filp->private_data; - struct rp1_pio_device *pio = client->pio; - uint claimed_dmas = client->claimed_dmas; - int i; -@@ -885,6 +917,45 @@ static int rp1_pio_release(struct inode - /* Reinitialise the SM? */ - - kfree(client); -+} -+EXPORT_SYMBOL_GPL(pio_close); -+ -+void pio_set_error(struct rp1_pio_client *client, int err) -+{ -+ client->error = err; -+} -+EXPORT_SYMBOL_GPL(pio_set_error); -+ -+int pio_get_error(const struct rp1_pio_client *client) -+{ -+ return client->error; -+} -+EXPORT_SYMBOL_GPL(pio_get_error); -+ -+void pio_clear_error(struct rp1_pio_client *client) -+{ -+ client->error = 0; -+} -+EXPORT_SYMBOL_GPL(pio_clear_error); -+ -+static int rp1_pio_open(struct inode *inode, struct file *filp) -+{ -+ struct rp1_pio_client *client; -+ -+ client = pio_open(); -+ if (IS_ERR(client)) -+ return PTR_ERR(client); -+ -+ filp->private_data = client; -+ -+ return 0; -+} -+ -+static int rp1_pio_release(struct inode *inode, struct file *filp) -+{ -+ struct rp1_pio_client *client = filp->private_data; -+ -+ pio_close(client); - - return 0; - } ---- /dev/null -+++ b/include/linux/pio_instructions.h -@@ -0,0 +1,481 @@ -+/* SPDX-License-Identifier: BSD-3-Clause */ -+/* -+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. -+ */ -+ -+#ifndef _HARDWARE_PIO_INSTRUCTIONS_H -+#define _HARDWARE_PIO_INSTRUCTIONS_H -+ -+/** \brief PIO instruction encoding -+ * \defgroup pio_instructions pio_instructions -+ * \ingroup hardware_pio -+ * -+ * Functions for generating PIO instruction encodings programmatically. In debug builds -+ *`PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` can be set to 1 to enable validation of encoding function -+ * parameters. -+ * -+ * For fuller descriptions of the instructions in question see the "RP2040 Datasheet" -+ */ -+ -+// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS, Enable/disable assertions in the PIO instructions, type=bool, default=0, group=pio_instructions -+#ifndef PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS -+#define PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS 0 -+#endif -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+enum pio_instr_bits { -+ pio_instr_bits_jmp = 0x0000, -+ pio_instr_bits_wait = 0x2000, -+ pio_instr_bits_in = 0x4000, -+ pio_instr_bits_out = 0x6000, -+ pio_instr_bits_push = 0x8000, -+ pio_instr_bits_pull = 0x8080, -+ pio_instr_bits_mov = 0xa000, -+ pio_instr_bits_irq = 0xc000, -+ pio_instr_bits_set = 0xe000, -+}; -+ -+#ifndef NDEBUG -+#define _PIO_INVALID_IN_SRC 0x08u -+#define _PIO_INVALID_OUT_DEST 0x10u -+#define _PIO_INVALID_SET_DEST 0x20u -+#define _PIO_INVALID_MOV_SRC 0x40u -+#define _PIO_INVALID_MOV_DEST 0x80u -+#else -+#define _PIO_INVALID_IN_SRC 0u -+#define _PIO_INVALID_OUT_DEST 0u -+#define _PIO_INVALID_SET_DEST 0u -+#define _PIO_INVALID_MOV_SRC 0u -+#define _PIO_INVALID_MOV_DEST 0u -+#endif -+ -+/*! \brief Enumeration of values to pass for source/destination args for instruction encoding functions -+ * \ingroup pio_instructions -+ * -+ * \note Not all values are suitable for all functions. Validity is only checked in debug mode when -+ * `PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` is 1 -+ */ -+enum pio_src_dest { -+ pio_pins = 0u, -+ pio_x = 1u, -+ pio_y = 2u, -+ pio_null = 3u | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, -+ pio_pindirs = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, -+ pio_exec_mov = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, -+ pio_status = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, -+ pio_pc = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, -+ pio_isr = 6u | _PIO_INVALID_SET_DEST, -+ pio_osr = 7u | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST, -+ pio_exec_out = 7u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, -+}; -+ -+static inline uint _pio_major_instr_bits(uint instr) { -+ return instr & 0xe000u; -+} -+ -+static inline uint _pio_encode_instr_and_args(enum pio_instr_bits instr_bits, uint arg1, uint arg2) { -+ valid_params_if(PIO_INSTRUCTIONS, arg1 <= 0x7); -+#if PARAM_ASSERTIONS_ENABLED(PIO_INSTRUCTIONS) -+ uint32_t major = _pio_major_instr_bits(instr_bits); -+ if (major == pio_instr_bits_in || major == pio_instr_bits_out) { -+ assert(arg2 && arg2 <= 32); -+ } else { -+ assert(arg2 <= 31); -+ } -+#endif -+ return instr_bits | (arg1 << 5u) | (arg2 & 0x1fu); -+} -+ -+static inline uint _pio_encode_instr_and_src_dest(enum pio_instr_bits instr_bits, enum pio_src_dest dest, uint value) { -+ return _pio_encode_instr_and_args(instr_bits, dest & 7u, value); -+} -+ -+/*! \brief Encode just the delay slot bits of an instruction -+ * \ingroup pio_instructions -+ * -+ * \note This function does not return a valid instruction encoding; instead it returns an encoding of the delay -+ * slot suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when -+ * combining the results of this function with the results of \ref pio_encode_sideset and \ref pio_encode_sideset_opt -+ * as they share the same bits within the instruction encoding. -+ * -+ * \param cycles the number of cycles 0-31 (or less if side set is being used) -+ * \return the delay slot bits to be ORed with an instruction encoding -+ */ -+static inline uint pio_encode_delay(uint cycles) { -+ // note that the maximum cycles will be smaller if sideset_bit_count > 0 -+ valid_params_if(PIO_INSTRUCTIONS, cycles <= 0x1f); -+ return cycles << 8u; -+} -+ -+/*! \brief Encode just the side set bits of an instruction (in non optional side set mode) -+ * \ingroup pio_instructions -+ * -+ * \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits -+ * suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when -+ * combining the results of this function with the results of \ref pio_encode_delay as they share the same bits -+ * within the instruction encoding. -+ * -+ * \param sideset_bit_count number of side set bits as would be specified via `.sideset` in pioasm -+ * \param value the value to sideset on the pins -+ * \return the side set bits to be ORed with an instruction encoding -+ */ -+static inline uint pio_encode_sideset(uint sideset_bit_count, uint value) { -+ valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 5); -+ valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1)); -+ return value << (13u - sideset_bit_count); -+} -+ -+/*! \brief Encode just the side set bits of an instruction (in optional -`opt` side set mode) -+ * \ingroup pio_instructions -+ * -+ * \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits -+ * suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when -+ * combining the results of this function with the results of \ref pio_encode_delay as they share the same bits -+ * within the instruction encoding. -+ * -+ * \param sideset_bit_count number of side set bits as would be specified via `.sideset opt` in pioasm -+ * \param value the value to sideset on the pins -+ * \return the side set bits to be ORed with an instruction encoding -+ */ -+static inline uint pio_encode_sideset_opt(uint sideset_bit_count, uint value) { -+ valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 4); -+ valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1)); -+ return 0x1000u | value << (12u - sideset_bit_count); -+} -+ -+/*! \brief Encode an unconditional JMP instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `JMP ` -+ * -+ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_jmp(uint addr) { -+ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 0, addr); -+} -+ -+/*! \brief Encode a conditional JMP if scratch X zero instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `JMP !X ` -+ * -+ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_jmp_not_x(uint addr) { -+ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 1, addr); -+} -+ -+/*! \brief Encode a conditional JMP if scratch X non-zero (and post-decrement X) instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `JMP X-- ` -+ * -+ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_jmp_x_dec(uint addr) { -+ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 2, addr); -+} -+ -+/*! \brief Encode a conditional JMP if scratch Y zero instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `JMP !Y ` -+ * -+ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_jmp_not_y(uint addr) { -+ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 3, addr); -+} -+ -+/*! \brief Encode a conditional JMP if scratch Y non-zero (and post-decrement Y) instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `JMP Y-- ` -+ * -+ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_jmp_y_dec(uint addr) { -+ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 4, addr); -+} -+ -+/*! \brief Encode a conditional JMP if scratch X not equal scratch Y instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `JMP X!=Y ` -+ * -+ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_jmp_x_ne_y(uint addr) { -+ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 5, addr); -+} -+ -+/*! \brief Encode a conditional JMP if input pin high instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `JMP PIN ` -+ * -+ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_jmp_pin(uint addr) { -+ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 6, addr); -+} -+ -+/*! \brief Encode a conditional JMP if output shift register not empty instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `JMP !OSRE ` -+ * -+ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_jmp_not_osre(uint addr) { -+ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 7, addr); -+} -+ -+static inline uint _pio_encode_irq(bool relative, uint irq) { -+ valid_params_if(PIO_INSTRUCTIONS, irq <= 7); -+ return (relative ? 0x10u : 0x0u) | irq; -+} -+ -+/*! \brief Encode a WAIT for GPIO pin instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `WAIT GPIO ` -+ * -+ * \param polarity true for `WAIT 1`, false for `WAIT 0` -+ * \param gpio The real GPIO number 0-31 -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_wait_gpio(bool polarity, uint gpio) { -+ return _pio_encode_instr_and_args(pio_instr_bits_wait, 0u | (polarity ? 4u : 0u), gpio); -+} -+ -+/*! \brief Encode a WAIT for pin instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `WAIT PIN ` -+ * -+ * \param polarity true for `WAIT 1`, false for `WAIT 0` -+ * \param pin The pin number 0-31 relative to the executing SM's input pin mapping -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_wait_pin(bool polarity, uint pin) { -+ return _pio_encode_instr_and_args(pio_instr_bits_wait, 1u | (polarity ? 4u : 0u), pin); -+} -+ -+/*! \brief Encode a WAIT for IRQ instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `WAIT IRQ ` -+ * -+ * \param polarity true for `WAIT 1`, false for `WAIT 0` -+ * \param relative true for a `WAIT IRQ REL`, false for regular `WAIT IRQ ` -+ * \param irq the irq number 0-7 -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_wait_irq(bool polarity, bool relative, uint irq) { -+ valid_params_if(PIO_INSTRUCTIONS, irq <= 7); -+ return _pio_encode_instr_and_args(pio_instr_bits_wait, 2u | (polarity ? 4u : 0u), _pio_encode_irq(relative, irq)); -+} -+ -+/*! \brief Encode an IN instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `IN , ` -+ * -+ * \param src The source to take data from -+ * \param count The number of bits 1-32 -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_in(enum pio_src_dest src, uint count) { -+ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_IN_SRC)); -+ return _pio_encode_instr_and_src_dest(pio_instr_bits_in, src, count); -+} -+ -+/*! \brief Encode an OUT instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `OUT , ` -+ * -+ * \param dest The destination to write data to -+ * \param count The number of bits 1-32 -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_out(enum pio_src_dest dest, uint count) { -+ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_OUT_DEST)); -+ return _pio_encode_instr_and_src_dest(pio_instr_bits_out, dest, count); -+} -+ -+/*! \brief Encode a PUSH instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `PUSH , ` -+ * -+ * \param if_full true for `PUSH IF_FULL ...`, false for `PUSH ...` -+ * \param block true for `PUSH ... BLOCK`, false for `PUSH ...` -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_push(bool if_full, bool block) { -+ return _pio_encode_instr_and_args(pio_instr_bits_push, (if_full ? 2u : 0u) | (block ? 1u : 0u), 0); -+} -+ -+/*! \brief Encode a PULL instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `PULL , ` -+ * -+ * \param if_empty true for `PULL IF_EMPTY ...`, false for `PULL ...` -+ * \param block true for `PULL ... BLOCK`, false for `PULL ...` -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_pull(bool if_empty, bool block) { -+ return _pio_encode_instr_and_args(pio_instr_bits_pull, (if_empty ? 2u : 0u) | (block ? 1u : 0u), 0); -+} -+ -+/*! \brief Encode a MOV instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `MOV , ` -+ * -+ * \param dest The destination to write data to -+ * \param src The source to take data from -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_mov(enum pio_src_dest dest, enum pio_src_dest src) { -+ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); -+ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); -+ return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, src & 7u); -+} -+ -+/*! \brief Encode a MOV instruction with bit invert -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `MOV , ~` -+ * -+ * \param dest The destination to write inverted data to -+ * \param src The source to take data from -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_mov_not(enum pio_src_dest dest, enum pio_src_dest src) { -+ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); -+ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); -+ return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (1u << 3u) | (src & 7u)); -+} -+ -+/*! \brief Encode a MOV instruction with bit reverse -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `MOV , ::` -+ * -+ * \param dest The destination to write bit reversed data to -+ * \param src The source to take data from -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_mov_reverse(enum pio_src_dest dest, enum pio_src_dest src) { -+ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); -+ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); -+ return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (2u << 3u) | (src & 7u)); -+} -+ -+/*! \brief Encode a IRQ SET instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `IRQ SET ` -+ * -+ * \param relative true for a `IRQ SET REL`, false for regular `IRQ SET ` -+ * \param irq the irq number 0-7 -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_irq_set(bool relative, uint irq) { -+ return _pio_encode_instr_and_args(pio_instr_bits_irq, 0, _pio_encode_irq(relative, irq)); -+} -+ -+/*! \brief Encode a IRQ WAIT instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `IRQ WAIT ` -+ * -+ * \param relative true for a `IRQ WAIT REL`, false for regular `IRQ WAIT ` -+ * \param irq the irq number 0-7 -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_irq_wait(bool relative, uint irq) { -+ return _pio_encode_instr_and_args(pio_instr_bits_irq, 1, _pio_encode_irq(relative, irq)); -+} -+ -+/*! \brief Encode a IRQ CLEAR instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `IRQ CLEAR ` -+ * -+ * \param relative true for a `IRQ CLEAR REL`, false for regular `IRQ CLEAR ` -+ * \param irq the irq number 0-7 -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_irq_clear(bool relative, uint irq) { -+ return _pio_encode_instr_and_args(pio_instr_bits_irq, 2, _pio_encode_irq(relative, irq)); -+} -+ -+/*! \brief Encode a SET instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `SET , ` -+ * -+ * \param dest The destination to apply the value to -+ * \param value The value 0-31 -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_set(enum pio_src_dest dest, uint value) { -+ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_SET_DEST)); -+ return _pio_encode_instr_and_src_dest(pio_instr_bits_set, dest, value); -+} -+ -+/*! \brief Encode a NOP instruction -+ * \ingroup pio_instructions -+ * -+ * This is the equivalent of `NOP` which is itself encoded as `MOV y, y` -+ * -+ * \return The instruction encoding with 0 delay and no side set value -+ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt -+ */ -+static inline uint pio_encode_nop(void) { -+ return pio_encode_mov(pio_y, pio_y); -+} -+ -+#ifdef __cplusplus -+} -+#endif -+ -+#endif ---- /dev/null -+++ b/include/linux/pio_rp1.h -@@ -0,0 +1,873 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (c) 2024 Raspberry Pi Ltd. -+ * All rights reserved. -+ */ -+ -+#ifndef _PIO_RP1_H -+#define _PIO_RP1_H -+ -+#include -+ -+#define PARAM_WARNINGS_ENABLED 1 -+ -+#ifdef DEBUG -+#define PARAM_WARNINGS_ENABLED 1 -+#endif -+ -+#ifndef PARAM_WARNINGS_ENABLED -+#define PARAM_WARNINGS_ENABLED 0 -+#endif -+ -+#define bad_params_if(client, test) \ -+ ({ bool f = (test); if (f) pio_set_error(client, -EINVAL); \ -+ if (f && PARAM_WARNINGS_ENABLED) WARN_ON((test)); \ -+ f; }) -+ -+#ifndef PARAM_ASSERTIONS_ENABLE_ALL -+#define PARAM_ASSERTIONS_ENABLE_ALL 0 -+#endif -+ -+#ifndef PARAM_ASSERTIONS_DISABLE_ALL -+#define PARAM_ASSERTIONS_DISABLE_ALL 0 -+#endif -+ -+#define PARAM_ASSERTIONS_ENABLED(x) \ -+ ((PARAM_ASSERTIONS_ENABLED_ ## x || PARAM_ASSERTIONS_ENABLE_ALL) && \ -+ !PARAM_ASSERTIONS_DISABLE_ALL) -+#define valid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) WARN_ON(test); }) -+ -+#include -+ -+#define NUM_PIO_STATE_MACHINES 4 -+#define PIO_INSTRUCTION_COUNT 32 -+#define PIO_ORIGIN_ANY ((uint)(~0)) -+#define GPIOS_MASK ((1 << RP1_PIO_GPIO_COUNT) - 1) -+ -+#define PICO_NO_HARDWARE 0 -+ -+#define pio0 pio_open_helper(0) -+ -+#define PROC_PIO_SM0_PINCTRL_OUT_BASE_BITS 0x0000001f -+#define PROC_PIO_SM0_PINCTRL_OUT_BASE_LSB 0 -+#define PROC_PIO_SM0_PINCTRL_OUT_COUNT_BITS 0x03f00000 -+#define PROC_PIO_SM0_PINCTRL_OUT_COUNT_LSB 20 -+#define PROC_PIO_SM0_PINCTRL_SET_BASE_BITS 0x000003e0 -+#define PROC_PIO_SM0_PINCTRL_SET_BASE_LSB 5 -+#define PROC_PIO_SM0_PINCTRL_SET_COUNT_BITS 0x1c000000 -+#define PROC_PIO_SM0_PINCTRL_SET_COUNT_LSB 26 -+#define PROC_PIO_SM0_PINCTRL_IN_BASE_BITS 0x000f8000 -+#define PROC_PIO_SM0_PINCTRL_IN_BASE_LSB 15 -+#define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_BITS 0x00007c00 -+#define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_LSB 10 -+#define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_BITS 0xe0000000 -+#define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_LSB 29 -+#define PROC_PIO_SM0_EXECCTRL_SIDE_EN_BITS 0x40000000 -+#define PROC_PIO_SM0_EXECCTRL_SIDE_EN_LSB 30 -+#define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_BITS 0x20000000 -+#define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_LSB 29 -+#define PROC_PIO_SM0_CLKDIV_INT_LSB 16 -+#define PROC_PIO_SM0_CLKDIV_FRAC_LSB 8 -+#define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_BITS 0x0001f000 -+#define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_LSB 12 -+#define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS 0x00000f80 -+#define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB 7 -+#define PROC_PIO_SM0_EXECCTRL_JMP_PIN_BITS 0x1f000000 -+#define PROC_PIO_SM0_EXECCTRL_JMP_PIN_LSB 24 -+#define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_BITS 0x00040000 -+#define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_LSB 18 -+#define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_BITS 0x00020000 -+#define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_LSB 17 -+#define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS 0x00010000 -+#define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_LSB 16 -+#define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_BITS 0x01f00000 -+#define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB 20 -+#define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS 0x00080000 -+#define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB 19 -+#define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS 0x3e000000 -+#define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB 25 -+#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS 0x40000000 -+#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB 30 -+#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS 0x80000000 -+#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_LSB 31 -+#define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_BITS 0x00020000 -+#define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_LSB 17 -+#define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_BITS 0x00040000 -+#define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_LSB 18 -+#define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS 0x00f80000 -+#define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_LSB 19 -+#define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS 0x00000020 -+#define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_LSB 5 -+#define PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS 0x0000001f -+#define PROC_PIO_SM0_EXECCTRL_STATUS_N_LSB 0 -+ -+enum pio_fifo_join { -+ PIO_FIFO_JOIN_NONE = 0, -+ PIO_FIFO_JOIN_TX = 1, -+ PIO_FIFO_JOIN_RX = 2, -+}; -+ -+enum pio_mov_status_type { -+ STATUS_TX_LESSTHAN = 0, -+ STATUS_RX_LESSTHAN = 1 -+}; -+ -+enum pio_xfer_dir { -+ PIO_DIR_TO_SM, -+ PIO_DIR_FROM_SM, -+ PIO_DIR_COUNT -+}; -+ -+enum clock_index { -+ clk_sys = 5 -+}; -+ -+typedef struct pio_program { -+ const uint16_t *instructions; -+ uint8_t length; -+ int8_t origin; // required instruction memory origin or -1 -+} pio_program_t; -+ -+enum gpio_function { -+ GPIO_FUNC_FSEL0 = 0, -+ GPIO_FUNC_FSEL1 = 1, -+ GPIO_FUNC_FSEL2 = 2, -+ GPIO_FUNC_FSEL3 = 3, -+ GPIO_FUNC_FSEL4 = 4, -+ GPIO_FUNC_FSEL5 = 5, -+ GPIO_FUNC_FSEL6 = 6, -+ GPIO_FUNC_FSEL7 = 7, -+ GPIO_FUNC_FSEL8 = 8, -+ GPIO_FUNC_NULL = 0x1f, -+ -+ // Name a few -+ GPIO_FUNC_SYS_RIO = 5, -+ GPIO_FUNC_PROC_RIO = 6, -+ GPIO_FUNC_PIO = 7, -+}; -+ -+enum gpio_irq_level { -+ GPIO_IRQ_LEVEL_LOW = 0x1u, -+ GPIO_IRQ_LEVEL_HIGH = 0x2u, -+ GPIO_IRQ_EDGE_FALL = 0x4u, -+ GPIO_IRQ_EDGE_RISE = 0x8u, -+}; -+ -+enum gpio_override { -+ GPIO_OVERRIDE_NORMAL = 0, -+ GPIO_OVERRIDE_INVERT = 1, -+ GPIO_OVERRIDE_LOW = 2, -+ GPIO_OVERRIDE_HIGH = 3, -+}; -+enum gpio_slew_rate { -+ GPIO_SLEW_RATE_SLOW = 0, -+ GPIO_SLEW_RATE_FAST = 1 -+}; -+ -+enum gpio_drive_strength { -+ GPIO_DRIVE_STRENGTH_2MA = 0, -+ GPIO_DRIVE_STRENGTH_4MA = 1, -+ GPIO_DRIVE_STRENGTH_8MA = 2, -+ GPIO_DRIVE_STRENGTH_12MA = 3 -+}; -+ -+typedef rp1_pio_sm_config pio_sm_config; -+ -+typedef struct rp1_pio_client *PIO; -+ -+void pio_set_error(struct rp1_pio_client *client, int err); -+int pio_get_error(struct rp1_pio_client *client); -+void pio_clear_error(struct rp1_pio_client *client); -+ -+int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param); -+int rp1_pio_add_program(struct rp1_pio_client *client, void *param); -+int rp1_pio_remove_program(struct rp1_pio_client *client, void *param); -+int rp1_pio_clear_instr_mem(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_claim(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_unclaim(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_is_claimed(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_init(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_set_config(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_exec(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_clear_fifos(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_set_clkdiv(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_set_pins(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_set_pindirs(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_set_enabled(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_restart(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_clkdiv_restart(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_enable_sync(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_put(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_get(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param); -+int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param); -+int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param); -+int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param); -+int rp1_pio_gpio_set_outover(struct rp1_pio_client *client, void *param); -+int rp1_pio_gpio_set_inover(struct rp1_pio_client *client, void *param); -+int rp1_pio_gpio_set_oeover(struct rp1_pio_client *client, void *param); -+int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param); -+int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param); -+ -+int pio_init(void); -+PIO pio_open(void); -+void pio_close(PIO pio); -+ -+int pio_sm_config_xfer(PIO pio, uint sm, uint dir, uint buf_size, uint buf_count); -+int pio_sm_xfer_data(PIO pio, uint sm, uint dir, uint data_bytes, void *data); -+ -+static inline bool pio_can_add_program(struct rp1_pio_client *client, -+ const pio_program_t *program) -+{ -+ struct rp1_pio_add_program_args args; -+ -+ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT)) -+ return false; -+ args.origin = (program->origin == -1) ? PIO_ORIGIN_ANY : program->origin; -+ args.num_instrs = program->length; -+ -+ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); -+ return rp1_pio_can_add_program(client, &args); -+} -+ -+static inline bool pio_can_add_program_at_offset(struct rp1_pio_client *client, -+ const pio_program_t *program, uint offset) -+{ -+ struct rp1_pio_add_program_args args; -+ -+ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT || -+ offset >= PIO_INSTRUCTION_COUNT)) -+ return false; -+ args.origin = offset; -+ args.num_instrs = program->length; -+ -+ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); -+ return !rp1_pio_can_add_program(client, &args); -+} -+ -+uint pio_add_program(struct rp1_pio_client *client, const pio_program_t *program) -+{ -+ struct rp1_pio_add_program_args args; -+ int offset; -+ -+ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT)) -+ return PIO_ORIGIN_ANY; -+ args.origin = (program->origin == -1) ? PIO_ORIGIN_ANY : program->origin; -+ args.num_instrs = program->length; -+ -+ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); -+ offset = rp1_pio_add_program(client, &args); -+ return (offset >= 0) ? offset : PIO_ORIGIN_ANY; -+} -+ -+static inline int pio_add_program_at_offset(struct rp1_pio_client *client, -+ const pio_program_t *program, uint offset) -+{ -+ struct rp1_pio_add_program_args args; -+ -+ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT || -+ offset >= PIO_INSTRUCTION_COUNT)) -+ return -EINVAL; -+ args.origin = offset; -+ args.num_instrs = program->length; -+ -+ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); -+ return rp1_pio_add_program(client, &args); -+} -+ -+static inline int pio_remove_program(struct rp1_pio_client *client, const pio_program_t *program, -+ uint loaded_offset) -+{ -+ struct rp1_pio_remove_program_args args; -+ -+ args.origin = loaded_offset; -+ args.num_instrs = program->length; -+ -+ return rp1_pio_remove_program(client, &args); -+} -+ -+static inline int pio_clear_instruction_memory(struct rp1_pio_client *client) -+{ -+ return rp1_pio_clear_instr_mem(client, NULL); -+} -+ -+static inline int pio_sm_claim(struct rp1_pio_client *client, uint sm) -+{ -+ struct rp1_pio_sm_claim_args args = { .mask = 1 << sm }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ -+ return rp1_pio_sm_claim(client, &args); -+} -+ -+static inline int pio_claim_sm_mask(struct rp1_pio_client *client, uint mask) -+{ -+ struct rp1_pio_sm_claim_args args = { .mask = mask }; -+ -+ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) -+ return -EINVAL; -+ -+ return rp1_pio_sm_claim(client, &args); -+} -+ -+static inline int pio_sm_unclaim(struct rp1_pio_client *client, uint sm) -+{ -+ struct rp1_pio_sm_claim_args args = { .mask = 1 << sm }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ -+ return rp1_pio_sm_claim(client, &args); -+} -+ -+static inline int pio_claim_unused_sm(struct rp1_pio_client *client, bool required) -+{ -+ struct rp1_pio_sm_claim_args args = { .mask = 0 }; -+ int sm; -+ -+ sm = rp1_pio_sm_claim(client, &args); -+ if (sm < 0 && required) -+ WARN_ON("No PIO state machines are available"); -+ return sm; -+} -+ -+static inline bool pio_sm_is_claimed(struct rp1_pio_client *client, uint sm) -+{ -+ struct rp1_pio_sm_claim_args args = { .mask = (1 << sm) }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return true; -+ return rp1_pio_sm_is_claimed(client, &args); -+} -+ -+static inline int pio_sm_init(struct rp1_pio_client *client, uint sm, uint initial_pc, -+ const pio_sm_config *config) -+{ -+ struct rp1_pio_sm_init_args args = { .sm = sm, .initial_pc = initial_pc, -+ .config = *config }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || -+ initial_pc >= PIO_INSTRUCTION_COUNT)) -+ return -EINVAL; -+ -+ return rp1_pio_sm_init(client, &args); -+} -+ -+static inline int pio_sm_set_config(struct rp1_pio_client *client, uint sm, -+ const pio_sm_config *config) -+{ -+ struct rp1_pio_sm_init_args args = { .sm = sm, .config = *config }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ -+ return rp1_pio_sm_set_config(client, &args); -+} -+ -+int pio_sm_exec(struct rp1_pio_client *client, uint sm, uint instr) -+{ -+ struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = false }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || instr > (uint16_t)~0)) -+ return -EINVAL; -+ -+ return rp1_pio_sm_exec(client, &args); -+} -+ -+int pio_sm_exec_wait_blocking(struct rp1_pio_client *client, uint sm, uint instr) -+{ -+ struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = true }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || instr > (uint16_t)~0)) -+ return -EINVAL; -+ -+ return rp1_pio_sm_exec(client, &args); -+} -+ -+static inline int pio_sm_clear_fifos(struct rp1_pio_client *client, uint sm) -+{ -+ struct rp1_pio_sm_clear_fifos_args args = { .sm = sm }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ return rp1_pio_sm_clear_fifos(client, &args); -+} -+ -+static inline bool pio_calculate_clkdiv_from_float(float div, uint16_t *div_int, -+ uint8_t *div_frac) -+{ -+ if (bad_params_if(NULL, div < 1 || div > 65536)) -+ return false; -+ *div_int = (uint16_t)div; -+ if (*div_int == 0) -+ *div_frac = 0; -+ else -+ *div_frac = (uint8_t)((div - (float)*div_int) * (1u << 8u)); -+ return true; -+} -+ -+static inline int pio_sm_set_clkdiv_int_frac(struct rp1_pio_client *client, uint sm, -+ uint16_t div_int, uint8_t div_frac) -+{ -+ struct rp1_pio_sm_set_clkdiv_args args = { .sm = sm, .div_int = div_int, -+ .div_frac = div_frac }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || -+ (div_int == 0 && div_frac != 0))) -+ return -EINVAL; -+ return rp1_pio_sm_set_clkdiv(client, &args); -+} -+ -+static inline int pio_sm_set_clkdiv(struct rp1_pio_client *client, uint sm, float div) -+{ -+ struct rp1_pio_sm_set_clkdiv_args args = { .sm = sm }; -+ -+ if (!pio_calculate_clkdiv_from_float(div, &args.div_int, &args.div_frac)) -+ return -EINVAL; -+ return rp1_pio_sm_set_clkdiv(client, &args); -+} -+ -+static inline int pio_sm_set_pins(struct rp1_pio_client *client, uint sm, uint32_t pin_values) -+{ -+ struct rp1_pio_sm_set_pins_args args = { .sm = sm, .values = pin_values, -+ .mask = GPIOS_MASK }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ return rp1_pio_sm_set_pins(client, &args); -+} -+ -+static inline int pio_sm_set_pins_with_mask(struct rp1_pio_client *client, uint sm, -+ uint32_t pin_values, uint32_t pin_mask) -+{ -+ struct rp1_pio_sm_set_pins_args args = { .sm = sm, .values = pin_values, -+ .mask = pin_mask }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ return rp1_pio_sm_set_pins(client, &args); -+} -+ -+static inline int pio_sm_set_pindirs_with_mask(struct rp1_pio_client *client, uint sm, -+ uint32_t pin_dirs, uint32_t pin_mask) -+{ -+ struct rp1_pio_sm_set_pindirs_args args = { .sm = sm, .dirs = pin_dirs, -+ .mask = pin_mask }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || -+ (pin_dirs & GPIOS_MASK) != pin_dirs || -+ (pin_mask & pin_mask) != pin_mask)) -+ return -EINVAL; -+ return rp1_pio_sm_set_pindirs(client, &args); -+} -+ -+static inline int pio_sm_set_consecutive_pindirs(struct rp1_pio_client *client, uint sm, -+ uint pin_base, uint pin_count, bool is_out) -+{ -+ uint32_t mask = ((1 << pin_count) - 1) << pin_base; -+ struct rp1_pio_sm_set_pindirs_args args = { .sm = sm, .dirs = is_out ? mask : 0, -+ .mask = mask }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || -+ pin_base >= RP1_PIO_GPIO_COUNT || -+ pin_count > RP1_PIO_GPIO_COUNT || -+ (pin_base + pin_count) > RP1_PIO_GPIO_COUNT)) -+ return -EINVAL; -+ return rp1_pio_sm_set_pindirs(client, &args); -+} -+ -+static inline int pio_sm_set_enabled(struct rp1_pio_client *client, uint sm, bool enabled) -+{ -+ struct rp1_pio_sm_set_enabled_args args = { .mask = (1 << sm), .enable = enabled }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ return rp1_pio_sm_set_enabled(client, &args); -+} -+ -+static inline int pio_set_sm_mask_enabled(struct rp1_pio_client *client, uint32_t mask, -+ bool enabled) -+{ -+ struct rp1_pio_sm_set_enabled_args args = { .mask = mask, .enable = enabled }; -+ -+ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) -+ return -EINVAL; -+ return rp1_pio_sm_set_enabled(client, &args); -+} -+ -+static inline int pio_sm_restart(struct rp1_pio_client *client, uint sm) -+{ -+ struct rp1_pio_sm_restart_args args = { .mask = (1 << sm) }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ return rp1_pio_sm_restart(client, &args); -+} -+ -+static inline int pio_restart_sm_mask(struct rp1_pio_client *client, uint32_t mask) -+{ -+ struct rp1_pio_sm_restart_args args = { .mask = (uint16_t)mask }; -+ -+ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) -+ return -EINVAL; -+ return rp1_pio_sm_restart(client, &args); -+} -+ -+static inline int pio_sm_clkdiv_restart(struct rp1_pio_client *client, uint sm) -+{ -+ struct rp1_pio_sm_restart_args args = { .mask = (1 << sm) }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ return rp1_pio_sm_clkdiv_restart(client, &args); -+} -+ -+static inline int pio_clkdiv_restart_sm_mask(struct rp1_pio_client *client, uint32_t mask) -+{ -+ struct rp1_pio_sm_restart_args args = { .mask = (uint16_t)mask }; -+ -+ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) -+ return -EINVAL; -+ return rp1_pio_sm_clkdiv_restart(client, &args); -+} -+ -+static inline int pio_enable_sm_in_sync_mask(struct rp1_pio_client *client, uint32_t mask) -+{ -+ struct rp1_pio_sm_enable_sync_args args = { .mask = (uint16_t)mask }; -+ -+ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) -+ return -EINVAL; -+ return rp1_pio_sm_enable_sync(client, &args); -+} -+ -+static inline int pio_sm_set_dmactrl(struct rp1_pio_client *client, uint sm, bool is_tx, -+ uint32_t ctrl) -+{ -+ struct rp1_pio_sm_set_dmactrl_args args = { .sm = sm, .is_tx = is_tx, .ctrl = ctrl }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ return rp1_pio_sm_set_dmactrl(client, &args); -+}; -+ -+static inline int pio_sm_put(struct rp1_pio_client *client, uint sm, uint32_t data) -+{ -+ struct rp1_pio_sm_put_args args = { .sm = (uint16_t)sm, .blocking = false, .data = data }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ return rp1_pio_sm_put(client, &args); -+} -+ -+static inline int pio_sm_put_blocking(struct rp1_pio_client *client, uint sm, uint32_t data) -+{ -+ struct rp1_pio_sm_put_args args = { .sm = (uint16_t)sm, .blocking = true, .data = data }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ return rp1_pio_sm_put(client, &args); -+} -+ -+static inline uint32_t pio_sm_get(struct rp1_pio_client *client, uint sm) -+{ -+ struct rp1_pio_sm_get_args args = { .sm = (uint16_t)sm, .blocking = false }; -+ -+ if (!bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ rp1_pio_sm_get(client, &args); -+ return args.data; -+} -+ -+static inline uint32_t pio_sm_get_blocking(struct rp1_pio_client *client, uint sm) -+{ -+ struct rp1_pio_sm_get_args args = { .sm = (uint16_t)sm, .blocking = true }; -+ -+ if (!bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ rp1_pio_sm_get(client, &args); -+ return args.data; -+} -+ -+static inline void sm_config_set_out_pins(pio_sm_config *c, uint out_base, uint out_count) -+{ -+ if (bad_params_if(NULL, out_base >= RP1_PIO_GPIO_COUNT || -+ out_count > RP1_PIO_GPIO_COUNT)) -+ return; -+ -+ c->pinctrl = (c->pinctrl & ~(PROC_PIO_SM0_PINCTRL_OUT_BASE_BITS | -+ PROC_PIO_SM0_PINCTRL_OUT_COUNT_BITS)) | -+ (out_base << PROC_PIO_SM0_PINCTRL_OUT_BASE_LSB) | -+ (out_count << PROC_PIO_SM0_PINCTRL_OUT_COUNT_LSB); -+} -+ -+static inline void sm_config_set_set_pins(pio_sm_config *c, uint set_base, uint set_count) -+{ -+ if (bad_params_if(NULL, set_base >= RP1_PIO_GPIO_COUNT || -+ set_count > 5)) -+ return; -+ -+ c->pinctrl = (c->pinctrl & ~(PROC_PIO_SM0_PINCTRL_SET_BASE_BITS | -+ PROC_PIO_SM0_PINCTRL_SET_COUNT_BITS)) | -+ (set_base << PROC_PIO_SM0_PINCTRL_SET_BASE_LSB) | -+ (set_count << PROC_PIO_SM0_PINCTRL_SET_COUNT_LSB); -+} -+ -+ -+static inline void sm_config_set_in_pins(pio_sm_config *c, uint in_base) -+{ -+ if (bad_params_if(NULL, in_base >= RP1_PIO_GPIO_COUNT)) -+ return; -+ -+ c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_IN_BASE_BITS) | -+ (in_base << PROC_PIO_SM0_PINCTRL_IN_BASE_LSB); -+} -+ -+static inline void sm_config_set_sideset_pins(pio_sm_config *c, uint sideset_base) -+{ -+ if (bad_params_if(NULL, sideset_base >= RP1_PIO_GPIO_COUNT)) -+ return; -+ -+ c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_SIDESET_BASE_BITS) | -+ (sideset_base << PROC_PIO_SM0_PINCTRL_SIDESET_BASE_LSB); -+} -+ -+static inline void sm_config_set_sideset(pio_sm_config *c, uint bit_count, bool optional, -+ bool pindirs) -+{ -+ if (bad_params_if(NULL, bit_count > 5 || -+ (optional && (bit_count == 0)))) -+ return; -+ c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_BITS) | -+ (bit_count << PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_LSB); -+ -+ c->execctrl = (c->execctrl & ~(PROC_PIO_SM0_EXECCTRL_SIDE_EN_BITS | -+ PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_BITS)) | -+ (optional << PROC_PIO_SM0_EXECCTRL_SIDE_EN_LSB) | -+ (pindirs << PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_LSB); -+} -+ -+static inline void sm_config_set_clkdiv_int_frac(pio_sm_config *c, uint16_t div_int, -+ uint8_t div_frac) -+{ -+ if (bad_params_if(NULL, div_int == 0 && div_frac != 0)) -+ return; -+ -+ c->clkdiv = -+ (((uint)div_frac) << PROC_PIO_SM0_CLKDIV_FRAC_LSB) | -+ (((uint)div_int) << PROC_PIO_SM0_CLKDIV_INT_LSB); -+} -+ -+static inline void sm_config_set_clkdiv(pio_sm_config *c, float div) -+{ -+ uint16_t div_int; -+ uint8_t div_frac; -+ -+ pio_calculate_clkdiv_from_float(div, &div_int, &div_frac); -+ sm_config_set_clkdiv_int_frac(c, div_int, div_frac); -+} -+ -+static inline void sm_config_set_wrap(pio_sm_config *c, uint wrap_target, uint wrap) -+{ -+ if (bad_params_if(NULL, wrap >= PIO_INSTRUCTION_COUNT || -+ wrap_target >= PIO_INSTRUCTION_COUNT)) -+ return; -+ -+ c->execctrl = (c->execctrl & ~(PROC_PIO_SM0_EXECCTRL_WRAP_TOP_BITS | -+ PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS)) | -+ (wrap_target << PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB) | -+ (wrap << PROC_PIO_SM0_EXECCTRL_WRAP_TOP_LSB); -+} -+ -+static inline void sm_config_set_jmp_pin(pio_sm_config *c, uint pin) -+{ -+ if (bad_params_if(NULL, pin >= RP1_PIO_GPIO_COUNT)) -+ return; -+ -+ c->execctrl = (c->execctrl & ~PROC_PIO_SM0_EXECCTRL_JMP_PIN_BITS) | -+ (pin << PROC_PIO_SM0_EXECCTRL_JMP_PIN_LSB); -+} -+ -+static inline void sm_config_set_in_shift(pio_sm_config *c, bool shift_right, bool autopush, -+ uint push_threshold) -+{ -+ if (bad_params_if(NULL, push_threshold > 32)) -+ return; -+ -+ c->shiftctrl = (c->shiftctrl & -+ ~(PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_BITS | -+ PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS | -+ PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_BITS)) | -+ (shift_right << PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_LSB) | -+ (autopush << PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_LSB) | -+ ((push_threshold & 0x1fu) << PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB); -+} -+ -+static inline void sm_config_set_out_shift(pio_sm_config *c, bool shift_right, bool autopull, -+ uint pull_threshold) -+{ -+ if (bad_params_if(NULL, pull_threshold > 32)) -+ return; -+ -+ c->shiftctrl = (c->shiftctrl & -+ ~(PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS | -+ PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_BITS | -+ PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS)) | -+ (shift_right << PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB) | -+ (autopull << PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_LSB) | -+ ((pull_threshold & 0x1fu) << PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB); -+} -+ -+static inline void sm_config_set_fifo_join(pio_sm_config *c, enum pio_fifo_join join) -+{ -+ if (bad_params_if(NULL, join != PIO_FIFO_JOIN_NONE && -+ join != PIO_FIFO_JOIN_TX && -+ join != PIO_FIFO_JOIN_RX)) -+ return; -+ -+ c->shiftctrl = (c->shiftctrl & (uint)~(PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS | -+ PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS)) | -+ (((uint)join) << PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB); -+} -+ -+static inline void sm_config_set_out_special(pio_sm_config *c, bool sticky, bool has_enable_pin, -+ uint enable_pin_index) -+{ -+ c->execctrl = (c->execctrl & -+ (uint)~(PROC_PIO_SM0_EXECCTRL_OUT_STICKY_BITS | -+ PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_BITS | -+ PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS)) | -+ (sticky << PROC_PIO_SM0_EXECCTRL_OUT_STICKY_LSB) | -+ (has_enable_pin << PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_LSB) | -+ ((enable_pin_index << PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_LSB) & -+ PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS); -+} -+ -+static inline void sm_config_set_mov_status(pio_sm_config *c, enum pio_mov_status_type status_sel, -+ uint status_n) -+{ -+ if (bad_params_if(NULL, status_sel != STATUS_TX_LESSTHAN && -+ status_sel != STATUS_RX_LESSTHAN)) -+ return; -+ -+ c->execctrl = (c->execctrl -+ & ~(PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS | PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS)) -+ | ((((uint)status_sel) << PROC_PIO_SM0_EXECCTRL_STATUS_SEL_LSB) & -+ PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS) -+ | ((status_n << PROC_PIO_SM0_EXECCTRL_STATUS_N_LSB) & -+ PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS); -+} -+ -+static inline pio_sm_config pio_get_default_sm_config(void) -+{ -+ pio_sm_config c = { 0 }; -+ -+ sm_config_set_clkdiv_int_frac(&c, 1, 0); -+ sm_config_set_wrap(&c, 0, 31); -+ sm_config_set_in_shift(&c, true, false, 32); -+ sm_config_set_out_shift(&c, true, false, 32); -+ return c; -+} -+ -+static inline uint32_t clock_get_hz(enum clock_index clk_index) -+{ -+ const uint32_t MHZ = 1000000; -+ -+ if (bad_params_if(NULL, clk_index != clk_sys)) -+ return 0; -+ return 200 * MHZ; -+} -+ -+static inline int pio_gpio_set_function(struct rp1_pio_client *client, uint gpio, -+ enum gpio_function fn) -+{ -+ struct rp1_gpio_set_function_args args = { .gpio = gpio, .fn = fn }; -+ -+ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) -+ return -EINVAL; -+ return rp1_pio_gpio_set_function(client, &args); -+} -+ -+static inline int pio_gpio_init(struct rp1_pio_client *client, uint gpio) -+{ -+ struct rp1_gpio_init_args args = { .gpio = gpio }; -+ int ret; -+ -+ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) -+ return -EINVAL; -+ ret = rp1_pio_gpio_init(client, &args); -+ if (ret) -+ return ret; -+ return pio_gpio_set_function(client, gpio, RP1_GPIO_FUNC_PIO); -+} -+ -+static inline int pio_gpio_set_pulls(struct rp1_pio_client *client, uint gpio, bool up, bool down) -+{ -+ struct rp1_gpio_set_pulls_args args = { .gpio = gpio, .up = up, .down = down }; -+ -+ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) -+ return -EINVAL; -+ return rp1_pio_gpio_set_pulls(client, &args); -+} -+ -+static inline int pio_gpio_set_outover(struct rp1_pio_client *client, uint gpio, uint value) -+{ -+ struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; -+ -+ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) -+ return -EINVAL; -+ return rp1_pio_gpio_set_outover(client, &args); -+} -+ -+static inline int pio_gpio_set_inover(struct rp1_pio_client *client, uint gpio, uint value) -+{ -+ struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; -+ -+ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) -+ return -EINVAL; -+ return rp1_pio_gpio_set_inover(client, &args); -+} -+ -+static inline int pio_gpio_set_oeover(struct rp1_pio_client *client, uint gpio, uint value) -+{ -+ struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; -+ -+ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) -+ return -EINVAL; -+ return rp1_pio_gpio_set_oeover(client, &args); -+} -+ -+static inline int pio_gpio_set_input_enabled(struct rp1_pio_client *client, uint gpio, -+ bool enabled) -+{ -+ struct rp1_gpio_set_args args = { .gpio = gpio, .value = enabled }; -+ -+ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) -+ return -EINVAL; -+ return rp1_pio_gpio_set_input_enabled(client, &args); -+} -+ -+static inline int pio_gpio_set_drive_strength(struct rp1_pio_client *client, uint gpio, -+ enum gpio_drive_strength drive) -+{ -+ struct rp1_gpio_set_args args = { .gpio = gpio, .value = drive }; -+ -+ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) -+ return -EINVAL; -+ return rp1_pio_gpio_set_drive_strength(client, &args); -+} -+ -+static inline int pio_gpio_pull_up(struct rp1_pio_client *client, uint gpio) -+{ -+ return pio_gpio_set_pulls(client, gpio, true, false); -+} -+ -+static inline int pio_gpio_pull_down(struct rp1_pio_client *client, uint gpio) -+{ -+ return pio_gpio_set_pulls(client, gpio, false, true); -+} -+ -+static inline int pio_gpio_disable_pulls(struct rp1_pio_client *client, uint gpio) -+{ -+ return pio_gpio_set_pulls(client, gpio, false, false); -+} -+ -+#endif diff --git a/target/linux/bcm27xx/patches-6.6/950-1356-dts-2712-Drop-some-numa-options-from-bootargs.patch b/target/linux/bcm27xx/patches-6.6/950-1356-dts-2712-Drop-some-numa-options-from-bootargs.patch new file mode 100644 index 0000000000..245c575e2d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1356-dts-2712-Drop-some-numa-options-from-bootargs.patch @@ -0,0 +1,29 @@ +From 624eb357e1a16385b3d6171e9194e4c5f8d4fd5f Mon Sep 17 00:00:00 2001 +From: Dom Cobley +Date: Wed, 23 Oct 2024 19:09:18 +0100 +Subject: [PATCH] dts: 2712: Drop some numa options from bootargs + +iommu_dma_numa_policy=interleave is not valid in the current tree +It generates an unknown setting will be passed to usespace warning + +system_heap.max_order=0 is wanted when numa is enabled, but may not +be when it is disabled. + +Add it on firmware side when we know if numa=fake= is used. + +Signed-off-by: Dom Cobley +--- + arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi +@@ -99,7 +99,7 @@ + + / { + chosen: chosen { +- bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe cgroup_disable=memory numa_policy=interleave iommu_dma_numa_policy=interleave system_heap.max_order=0"; ++ bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe cgroup_disable=memory numa_policy=interleave"; + stdout-path = "serial10:115200n8"; + }; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1356-pwm-Add-pwm-pio-rp1-driver.patch b/target/linux/bcm27xx/patches-6.6/950-1356-pwm-Add-pwm-pio-rp1-driver.patch deleted file mode 100644 index 3f787de9d9..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-1356-pwm-Add-pwm-pio-rp1-driver.patch +++ /dev/null @@ -1,299 +0,0 @@ -From 4d20aadc3188ecfb62b309a9924ee9696a94fc33 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Fri, 8 Nov 2024 09:37:58 +0000 -Subject: [PATCH] pwm: Add pwm-pio-rp1 driver - -Use the PIO hardware on RP1 to implement a PWM interface. - -Signed-off-by: Phil Elwell ---- - drivers/pwm/Kconfig | 11 ++ - drivers/pwm/Makefile | 1 + - drivers/pwm/pwm-pio-rp1.c | 251 ++++++++++++++++++++++++++++++++++++++ - 3 files changed, 263 insertions(+) - create mode 100644 drivers/pwm/pwm-pio-rp1.c - ---- a/drivers/pwm/Kconfig -+++ b/drivers/pwm/Kconfig -@@ -454,6 +454,17 @@ config PWM_PCA9685 - To compile this driver as a module, choose M here: the module - will be called pwm-pca9685. - -+config PWM_PIO_RP1 -+ tristate "RP1 PIO PWM support" -+ depends on FIRMWARE_RP1 || COMPILE_TEST -+ help -+ This is a PWM framework driver for Raspberry Pi 5, using the PIO -+ hardware of RP1 to provide PWM functionality. Supports up to 4 -+ instances on GPIOs in bank 0. -+ -+ To compile this driver as a module, choose M here: the module -+ will be called pwm-pio-rp1. -+ - config PWM_PXA - tristate "PXA PWM support" - depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST ---- a/drivers/pwm/Makefile -+++ b/drivers/pwm/Makefile -@@ -41,6 +41,7 @@ obj-$(CONFIG_PWM_MXS) += pwm-mxs.o - obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o - obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o - obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o -+obj-$(CONFIG_PWM_PIO_RP1) += pwm-pio-rp1.o - obj-$(CONFIG_PWM_PXA) += pwm-pxa.o - obj-$(CONFIG_PWM_RASPBERRYPI_POE) += pwm-raspberrypi-poe.o - obj-$(CONFIG_PWM_RP1) += pwm-rp1.o ---- /dev/null -+++ b/drivers/pwm/pwm-pio-rp1.c -@@ -0,0 +1,251 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Raspberry Pi PIO PWM. -+ * -+ * Copyright (C) 2024 Raspberry Pi Ltd. -+ * -+ * Author: Phil Elwell (phil@raspberrypi.com) -+ * -+ * Based on the pwm-rp1 driver by: -+ * Naushir Patuck -+ * and on the pwm-gpio driver by: -+ * Vincent Whitchurch -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+struct pwm_pio_rp1 { -+ struct pwm_chip chip; -+ struct device *dev; -+ struct gpio_desc *gpiod; -+ struct mutex mutex; -+ PIO pio; -+ uint sm; -+ uint offset; -+ uint gpio; -+ uint32_t period; /* In SM cycles */ -+ uint32_t duty_cycle; /* In SM cycles */ -+ enum pwm_polarity polarity; -+ bool enabled; -+}; -+ -+/* Generated from pwm.pio by pioasm */ -+#define pwm_wrap_target 0 -+#define pwm_wrap 6 -+#define pwm_loop_ticks 3 -+ -+static const uint16_t pwm_program_instructions[] = { -+ // .wrap_target -+ 0x9080, // 0: pull noblock side 0 -+ 0xa027, // 1: mov x, osr -+ 0xa046, // 2: mov y, isr -+ 0x00a5, // 3: jmp x != y, 5 -+ 0x1806, // 4: jmp 6 side 1 -+ 0xa042, // 5: nop -+ 0x0083, // 6: jmp y--, 3 -+ // .wrap -+}; -+ -+static const struct pio_program pwm_program = { -+ .instructions = pwm_program_instructions, -+ .length = 7, -+ .origin = -1, -+}; -+ -+static unsigned int pwm_pio_resolution __read_mostly; -+ -+static inline pio_sm_config pwm_program_get_default_config(uint offset) -+{ -+ pio_sm_config c = pio_get_default_sm_config(); -+ -+ sm_config_set_wrap(&c, offset + pwm_wrap_target, offset + pwm_wrap); -+ sm_config_set_sideset(&c, 2, true, false); -+ return c; -+} -+ -+static inline void pwm_program_init(PIO pio, uint sm, uint offset, uint pin) -+{ -+ pio_gpio_init(pio, pin); -+ -+ pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); -+ pio_sm_config c = pwm_program_get_default_config(offset); -+ -+ sm_config_set_sideset_pins(&c, pin); -+ pio_sm_init(pio, sm, offset, &c); -+} -+ -+/* Write `period` to the input shift register - must be disabled */ -+static void pio_pwm_set_period(PIO pio, uint sm, uint32_t period) -+{ -+ pio_sm_put_blocking(pio, sm, period); -+ pio_sm_exec(pio, sm, pio_encode_pull(false, false)); -+ pio_sm_exec(pio, sm, pio_encode_out(pio_isr, 32)); -+} -+ -+/* Write `level` to TX FIFO. State machine will copy this into X. */ -+static void pio_pwm_set_level(PIO pio, uint sm, uint32_t level) -+{ -+ pio_sm_put_blocking(pio, sm, level); -+} -+ -+static int pwm_pio_rp1_apply(struct pwm_chip *chip, struct pwm_device *pwm, -+ const struct pwm_state *state) -+{ -+ struct pwm_pio_rp1 *ppwm = container_of(chip, struct pwm_pio_rp1, chip); -+ uint32_t new_duty_cycle; -+ uint32_t new_period; -+ -+ if (state->duty_cycle && state->duty_cycle < pwm_pio_resolution) -+ return -EINVAL; -+ -+ if (state->duty_cycle != state->period && -+ (state->period - state->duty_cycle < pwm_pio_resolution)) -+ return -EINVAL; -+ -+ new_period = state->period / pwm_pio_resolution; -+ new_duty_cycle = state->duty_cycle / pwm_pio_resolution; -+ -+ mutex_lock(&ppwm->mutex); -+ -+ if ((ppwm->enabled && !state->enabled) || new_period != ppwm->period) { -+ pio_sm_set_enabled(ppwm->pio, ppwm->sm, false); -+ ppwm->enabled = false; -+ } -+ -+ if (new_period != ppwm->period) { -+ pio_pwm_set_period(ppwm->pio, ppwm->sm, new_period); -+ ppwm->period = new_period; -+ } -+ -+ if (state->enabled && new_duty_cycle != ppwm->duty_cycle) { -+ pio_pwm_set_level(ppwm->pio, ppwm->sm, new_duty_cycle); -+ ppwm->duty_cycle = new_duty_cycle; -+ } -+ -+ if (state->polarity != ppwm->polarity) { -+ pio_gpio_set_outover(ppwm->pio, ppwm->gpio, -+ (state->polarity == PWM_POLARITY_INVERSED) ? -+ GPIO_OVERRIDE_INVERT : GPIO_OVERRIDE_NORMAL); -+ ppwm->polarity = state->polarity; -+ } -+ -+ if (!ppwm->enabled && state->enabled) { -+ pio_sm_set_enabled(ppwm->pio, ppwm->sm, true); -+ ppwm->enabled = true; -+ } -+ -+ mutex_unlock(&ppwm->mutex); -+ -+ return 0; -+} -+ -+static const struct pwm_ops pwm_pio_rp1_ops = { -+ .apply = pwm_pio_rp1_apply, -+}; -+ -+static int pwm_pio_rp1_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ struct of_phandle_args of_args = { 0 }; -+ struct device *dev = &pdev->dev; -+ struct pwm_pio_rp1 *ppwm; -+ struct pwm_chip *chip; -+ bool is_rp1; -+ -+ ppwm = devm_kzalloc(dev, sizeof(*ppwm), GFP_KERNEL); -+ if (IS_ERR(ppwm)) -+ return PTR_ERR(ppwm); -+ -+ chip = &ppwm->chip; -+ -+ mutex_init(&ppwm->mutex); -+ -+ ppwm->gpiod = devm_gpiod_get(dev, NULL, GPIOD_ASIS); -+ /* Need to check that this is an RP1 GPIO in the first bank, and retrieve the offset */ -+ /* Unfortunately I think this has to be done by parsing the gpios property */ -+ if (IS_ERR(ppwm->gpiod)) -+ return dev_err_probe(dev, PTR_ERR(ppwm->gpiod), -+ "could not get a gpio\n"); -+ -+ /* This really shouldn't fail, given that we have a gpiod */ -+ if (of_parse_phandle_with_args(np, "gpios", "#gpio-cells", 0, &of_args)) -+ return dev_err_probe(dev, -EINVAL, -+ "can't find gpio declaration\n"); -+ -+ is_rp1 = of_device_is_compatible(of_args.np, "raspberrypi,rp1-gpio"); -+ of_node_put(of_args.np); -+ if (!is_rp1 || of_args.args_count != 2) -+ return dev_err_probe(dev, -EINVAL, -+ "not an RP1 gpio\n"); -+ -+ ppwm->gpio = of_args.args[0]; -+ -+ ppwm->pio = pio_open(); -+ if (IS_ERR(ppwm->pio)) -+ return dev_err_probe(dev, PTR_ERR(ppwm->pio), -+ "%pfw: could not open PIO\n", -+ dev_fwnode(dev)); -+ -+ ppwm->sm = pio_claim_unused_sm(ppwm->pio, false); -+ if ((int)ppwm->sm < 0) { -+ pio_close(ppwm->pio); -+ return dev_err_probe(dev, -EBUSY, -+ "%pfw: no free PIO SM\n", -+ dev_fwnode(dev)); -+ } -+ -+ ppwm->offset = pio_add_program(ppwm->pio, &pwm_program); -+ if (ppwm->offset == PIO_ORIGIN_ANY) { -+ pio_close(ppwm->pio); -+ return dev_err_probe(dev, -EBUSY, -+ "%pfw: not enough PIO program space\n", -+ dev_fwnode(dev)); -+ } -+ -+ pwm_program_init(ppwm->pio, ppwm->sm, ppwm->offset, ppwm->gpio); -+ -+ pwm_pio_resolution = (1000u * 1000 * 1000 * pwm_loop_ticks) / clock_get_hz(clk_sys); -+ -+ chip->dev = dev; -+ chip->ops = &pwm_pio_rp1_ops; -+ chip->atomic = true; -+ chip->npwm = 1; -+ -+ platform_set_drvdata(pdev, ppwm); -+ -+ return devm_pwmchip_add(dev, chip); -+} -+ -+static void pwm_pio_rp1_remove(struct platform_device *pdev) -+{ -+ struct pwm_pio_rp1 *ppwm = platform_get_drvdata(pdev); -+ -+ pio_close(ppwm->pio); -+} -+ -+static const struct of_device_id pwm_pio_rp1_dt_ids[] = { -+ { .compatible = "raspberrypi,pwm-pio-rp1" }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, pwm_pio_rp1_dt_ids); -+ -+static struct platform_driver pwm_pio_rp1_driver = { -+ .driver = { -+ .name = "pwm-pio-rp1", -+ .of_match_table = pwm_pio_rp1_dt_ids, -+ }, -+ .probe = pwm_pio_rp1_probe, -+ .remove_new = pwm_pio_rp1_remove, -+}; -+module_platform_driver(pwm_pio_rp1_driver); -+ -+MODULE_DESCRIPTION("PWM PIO RP1 driver"); -+MODULE_AUTHOR("Phil Elwell"); -+MODULE_LICENSE("GPL"); diff --git a/target/linux/bcm27xx/patches-6.6/950-1357-misc-rp1-pio-Fix-copy-paste-error-in-pio_rp1.h.patch b/target/linux/bcm27xx/patches-6.6/950-1357-misc-rp1-pio-Fix-copy-paste-error-in-pio_rp1.h.patch deleted file mode 100644 index 0746e3c32b..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-1357-misc-rp1-pio-Fix-copy-paste-error-in-pio_rp1.h.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 99a0201bb0abc946dc431214b638b2cc6b01dda5 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 25 Nov 2024 16:19:55 +0000 -Subject: [PATCH] misc/rp1-pio: Fix copy/paste error in pio_rp1.h - -As per the subject, there was a copy/paste error that caused -pio_sm_unclaim from a driver to result in a call to -pio_sm_claim. Fix it. - -Signed-off-by: Phil Elwell ---- - include/linux/pio_rp1.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/include/linux/pio_rp1.h -+++ b/include/linux/pio_rp1.h -@@ -318,7 +318,7 @@ static inline int pio_sm_unclaim(struct - if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) - return -EINVAL; - -- return rp1_pio_sm_claim(client, &args); -+ return rp1_pio_sm_unclaim(client, &args); - } - - static inline int pio_claim_unused_sm(struct rp1_pio_client *client, bool required) diff --git a/target/linux/bcm27xx/patches-6.6/950-1357-mmc-quirks-add-more-broken-Kingston-Canvas-Go-SD-car.patch b/target/linux/bcm27xx/patches-6.6/950-1357-mmc-quirks-add-more-broken-Kingston-Canvas-Go-SD-car.patch new file mode 100644 index 0000000000..ef5a5a5dcb --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1357-mmc-quirks-add-more-broken-Kingston-Canvas-Go-SD-car.patch @@ -0,0 +1,42 @@ +From 74f3ca5e39586ea26201fe6eaf1b9e6793b101b7 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Tue, 29 Oct 2024 13:33:21 +0000 +Subject: [PATCH] mmc: quirks: add more broken Kingston Canvas Go! SD card date + ranges + +A user has reported that a card of this model from late 2021 doesn't +work, so extend the date range and make it match on all card sizes. + +Signed-off-by: Jonathan Bell +--- + drivers/mmc/core/quirks.h | 18 +++++++++++++++--- + 1 file changed, 15 insertions(+), 3 deletions(-) + +--- a/drivers/mmc/core/quirks.h ++++ b/drivers/mmc/core/quirks.h +@@ -18,10 +18,22 @@ + static const struct mmc_fixup __maybe_unused mmc_sd_fixups[] = { + /* + * Kingston Canvas Go! Plus microSD cards never finish SD cache flush. +- * This has so far only been observed on cards from 11/2019, while new +- * cards from 2023/05 do not exhibit this behavior. ++ * This has been observed on cards from 2019/11 and 2021/11, while new ++ * cards from 2023/05 and 2024/08 do not exhibit this behavior. + */ +- _FIXUP_EXT("SD64G", CID_MANFID_KINGSTON_SD, 0x5449, 2019, 11, ++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2019, CID_MONTH_ANY, ++ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, ++ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), ++ ++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2020, CID_MONTH_ANY, ++ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, ++ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), ++ ++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2021, CID_MONTH_ANY, ++ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, ++ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), ++ ++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2022, CID_MONTH_ANY, + 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, + MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), + diff --git a/target/linux/bcm27xx/patches-6.6/950-1357-rpi-pio-add-missing.patch b/target/linux/bcm27xx/patches-6.6/950-1357-rpi-pio-add-missing.patch deleted file mode 100644 index c871fa681f..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-1357-rpi-pio-add-missing.patch +++ /dev/null @@ -1,42 +0,0 @@ -From df8a2f6dc114b2c5c7685a069f717f2b06186b74 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Wed, 20 Nov 2024 16:23:06 +0000 -Subject: [PATCH] rp1-pio: Add missing 'static inline's - -Avoid some duplicate symbol errors by adding some missing -'static inline' decorations. - -Signed-off-by: Phil Elwell ---- - include/linux/pio_rp1.h | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - ---- a/include/linux/pio_rp1.h -+++ b/include/linux/pio_rp1.h -@@ -245,7 +245,7 @@ static inline bool pio_can_add_program_a - return !rp1_pio_can_add_program(client, &args); - } - --uint pio_add_program(struct rp1_pio_client *client, const pio_program_t *program) -+static inline uint pio_add_program(struct rp1_pio_client *client, const pio_program_t *program) - { - struct rp1_pio_add_program_args args; - int offset; -@@ -365,7 +365,7 @@ static inline int pio_sm_set_config(stru - return rp1_pio_sm_set_config(client, &args); - } - --int pio_sm_exec(struct rp1_pio_client *client, uint sm, uint instr) -+static inline int pio_sm_exec(struct rp1_pio_client *client, uint sm, uint instr) - { - struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = false }; - -@@ -375,7 +375,7 @@ int pio_sm_exec(struct rp1_pio_client *c - return rp1_pio_sm_exec(client, &args); - } - --int pio_sm_exec_wait_blocking(struct rp1_pio_client *client, uint sm, uint instr) -+static inline int pio_sm_exec_wait_blocking(struct rp1_pio_client *client, uint sm, uint instr) - { - struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = true }; - diff --git a/target/linux/bcm27xx/patches-6.6/950-1358-dt-bindings-usb-snps-dwc3-add-FS-HS-periodic-NAK-pol.patch b/target/linux/bcm27xx/patches-6.6/950-1358-dt-bindings-usb-snps-dwc3-add-FS-HS-periodic-NAK-pol.patch new file mode 100644 index 0000000000..eaa340f249 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1358-dt-bindings-usb-snps-dwc3-add-FS-HS-periodic-NAK-pol.patch @@ -0,0 +1,35 @@ +From 6c0f34fb0f83741f7f03f6bfd3fcbc89cb2c7cde Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Wed, 6 Nov 2024 10:26:55 +0000 +Subject: [PATCH] dt-bindings: usb: snps,dwc3: add FS/HS periodic NAK polling + quirk + +Add two quirk properties that control whether or not the controller +issues many more handshakes to FS/HS Async endpoints in a single +(micro)frame. Enabling these can significantly increase throughput for +endpoints that frequently respond with NAKs. + +Signed-off-by: Jonathan Bell +--- + Documentation/devicetree/bindings/usb/snps,dwc3.yaml | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml ++++ b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml +@@ -231,6 +231,16 @@ properties: + description: When set, disable u2mac linestate check during HS transmit + type: boolean + ++ snps,enhanced-nak-fs-quirk: ++ description: ++ When set, the controller schedules many more handshakes to Async FS ++ endpoints, improving throughput when they frequently respond with NAKs. ++ ++ snps,enhanced-nak-hs-quirk: ++ description: ++ When set, the controller schedules many more handshakes to Async HS ++ endpoints, improving throughput when they frequently respond with NAKs. ++ + snps,parkmode-disable-ss-quirk: + description: + When set, disable park mode for all Superspeed bus instances. diff --git a/target/linux/bcm27xx/patches-6.6/950-1358-misc-rpi-pio-back-port-some.patch b/target/linux/bcm27xx/patches-6.6/950-1358-misc-rpi-pio-back-port-some.patch deleted file mode 100644 index 92bde521d2..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-1358-misc-rpi-pio-back-port-some.patch +++ /dev/null @@ -1,39 +0,0 @@ -From d1f0c94e974a5f26d210b1d13a6ef9543bee4984 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Thu, 21 Nov 2024 11:11:48 +0000 -Subject: [PATCH] misc: rp1-pio: Back-port some 6.11 build fixes - -Porting rp1-pio to rpi-6.11.y uncovered a few missing #includes and a -difference of const-ness. Although not needed here, back-porting the -resulting changes makes the driver more "correct" and may prevent a -future merge conflict. - -Signed-off-by: Phil Elwell ---- - drivers/misc/rp1-pio.c | 3 +++ - include/linux/pio_rp1.h | 2 +- - 2 files changed, 4 insertions(+), 1 deletion(-) - ---- a/drivers/misc/rp1-pio.c -+++ b/drivers/misc/rp1-pio.c -@@ -22,6 +22,9 @@ - #include - #include - #include -+#include -+#include -+#include - #include - #include - #include ---- a/include/linux/pio_rp1.h -+++ b/include/linux/pio_rp1.h -@@ -176,7 +176,7 @@ typedef rp1_pio_sm_config pio_sm_config; - typedef struct rp1_pio_client *PIO; - - void pio_set_error(struct rp1_pio_client *client, int err); --int pio_get_error(struct rp1_pio_client *client); -+int pio_get_error(const struct rp1_pio_client *client); - void pio_clear_error(struct rp1_pio_client *client); - - int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param); diff --git a/target/linux/bcm27xx/patches-6.6/950-1359-misc-rp1-pio-Add-FIFO.patch b/target/linux/bcm27xx/patches-6.6/950-1359-misc-rp1-pio-Add-FIFO.patch deleted file mode 100644 index fff033f5a6..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-1359-misc-rp1-pio-Add-FIFO.patch +++ /dev/null @@ -1,215 +0,0 @@ -From dd2394360860d15146c96635796a75b05bb32b61 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Tue, 19 Nov 2024 09:25:34 +0000 -Subject: [PATCH] misc: rp1-pio: Add FIFO-related methods - -Add support for querying the FIFO status and clearing the TX FIFO. - -Signed-off-by: Phil Elwell ---- - drivers/misc/rp1-fw-pio.h | 3 ++ - drivers/misc/rp1-pio.c | 24 +++++++++ - include/linux/pio_rp1.h | 89 ++++++++++++++++++++++++++++++++++ - include/uapi/misc/rp1_pio_if.h | 13 ++++- - 4 files changed, 128 insertions(+), 1 deletion(-) - ---- a/drivers/misc/rp1-fw-pio.h -+++ b/drivers/misc/rp1-fw-pio.h -@@ -47,6 +47,9 @@ enum rp1_pio_ops { - READ_HW, // src address, len -> data bytes - WRITE_HW, // dst address, data - -+ PIO_SM_FIFO_STATE, // u16 sm, u8 tx -> u16 level, u8 empty, u8 full -+ PIO_SM_DRAIN_TX, // u16 sm -+ - PIO_COUNT - }; - ---- a/drivers/misc/rp1-pio.c -+++ b/drivers/misc/rp1-pio.c -@@ -479,6 +479,28 @@ int rp1_pio_sm_set_dmactrl(struct rp1_pi - } - EXPORT_SYMBOL_GPL(rp1_pio_sm_set_dmactrl); - -+int rp1_pio_sm_fifo_state(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_fifo_state_args *args = param; -+ const int level_offset = offsetof(struct rp1_pio_sm_fifo_state_args, level); -+ int ret; -+ -+ ret = rp1_pio_message_resp(client->pio, PIO_SM_FIFO_STATE, args, sizeof(*args), -+ &args->level, NULL, sizeof(*args) - level_offset); -+ if (ret >= 0) -+ return level_offset + ret; -+ return ret; -+} -+EXPORT_SYMBOL_GPL(rp1_pio_sm_fifo_state); -+ -+int rp1_pio_sm_drain_tx(struct rp1_pio_client *client, void *param) -+{ -+ struct rp1_pio_sm_clear_fifos_args *args = param; -+ -+ return rp1_pio_message(client->pio, PIO_SM_DRAIN_TX, args, sizeof(*args)); -+} -+EXPORT_SYMBOL_GPL(rp1_pio_sm_drain_tx); -+ - int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param) - { - struct rp1_gpio_init_args *args = param; -@@ -851,6 +873,8 @@ struct handler_info { - HANDLER(SM_PUT, sm_put), - HANDLER(SM_GET, sm_get), - HANDLER(SM_SET_DMACTRL, sm_set_dmactrl), -+ HANDLER(SM_FIFO_STATE, sm_fifo_state), -+ HANDLER(SM_DRAIN_TX, sm_drain_tx), - - HANDLER(GPIO_INIT, gpio_init), - HANDLER(GPIO_SET_FUNCTION, gpio_set_function), ---- a/include/linux/pio_rp1.h -+++ b/include/linux/pio_rp1.h -@@ -200,6 +200,8 @@ int rp1_pio_sm_enable_sync(struct rp1_pi - int rp1_pio_sm_put(struct rp1_pio_client *client, void *param); - int rp1_pio_sm_get(struct rp1_pio_client *client, void *param); - int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_fifo_state(struct rp1_pio_client *client, void *param); -+int rp1_pio_sm_drain_tx(struct rp1_pio_client *client, void *param); - int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param); - int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param); - int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param); -@@ -551,6 +553,15 @@ static inline int pio_sm_set_dmactrl(str - return rp1_pio_sm_set_dmactrl(client, &args); - }; - -+static inline int pio_sm_drain_tx_fifo(struct rp1_pio_client *client, uint sm) -+{ -+ struct rp1_pio_sm_clear_fifos_args args = { .sm = sm }; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ return rp1_pio_sm_drain_tx(client, &args); -+}; -+ - static inline int pio_sm_put(struct rp1_pio_client *client, uint sm, uint32_t data) - { - struct rp1_pio_sm_put_args args = { .sm = (uint16_t)sm, .blocking = false, .data = data }; -@@ -587,6 +598,84 @@ static inline uint32_t pio_sm_get_blocki - return args.data; - } - -+static inline int pio_sm_is_rx_fifo_empty(struct rp1_pio_client *client, uint sm) -+{ -+ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; -+ int ret; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ ret = rp1_pio_sm_fifo_state(client, &args); -+ if (ret == sizeof(args)) -+ ret = args.empty; -+ return ret; -+}; -+ -+static inline int pio_sm_is_rx_fifo_full(struct rp1_pio_client *client, uint sm) -+{ -+ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; -+ int ret; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ ret = rp1_pio_sm_fifo_state(client, &args); -+ if (ret == sizeof(args)) -+ ret = args.full; -+ return ret; -+}; -+ -+static inline int pio_sm_rx_fifo_level(struct rp1_pio_client *client, uint sm) -+{ -+ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; -+ int ret; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ ret = rp1_pio_sm_fifo_state(client, &args); -+ if (ret == sizeof(args)) -+ ret = args.level; -+ return ret; -+}; -+ -+static inline int pio_sm_is_tx_fifo_empty(struct rp1_pio_client *client, uint sm) -+{ -+ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; -+ int ret; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ ret = rp1_pio_sm_fifo_state(client, &args); -+ if (ret == sizeof(args)) -+ ret = args.empty; -+ return ret; -+}; -+ -+static inline int pio_sm_is_tx_fifo_full(struct rp1_pio_client *client, uint sm) -+{ -+ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; -+ int ret; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ ret = rp1_pio_sm_fifo_state(client, &args); -+ if (ret == sizeof(args)) -+ ret = args.full; -+ return ret; -+}; -+ -+static inline int pio_sm_tx_fifo_level(struct rp1_pio_client *client, uint sm) -+{ -+ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; -+ int ret; -+ -+ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) -+ return -EINVAL; -+ ret = rp1_pio_sm_fifo_state(client, &args); -+ if (ret == sizeof(args)) -+ ret = args.level; -+ return ret; -+}; -+ - static inline void sm_config_set_out_pins(pio_sm_config *c, uint out_base, uint out_count) - { - if (bad_params_if(NULL, out_base >= RP1_PIO_GPIO_COUNT || ---- a/include/uapi/misc/rp1_pio_if.h -+++ b/include/uapi/misc/rp1_pio_if.h -@@ -114,7 +114,7 @@ struct rp1_pio_sm_get_args { - uint16_t sm; - uint8_t blocking; - uint8_t rsvd; -- uint32_t data; /* IN/OUT */ -+ uint32_t data; /* OUT */ - }; - - struct rp1_pio_sm_set_dmactrl_args { -@@ -124,6 +124,15 @@ struct rp1_pio_sm_set_dmactrl_args { - uint32_t ctrl; - }; - -+struct rp1_pio_sm_fifo_state_args { -+ uint16_t sm; -+ uint8_t tx; -+ uint8_t rsvd; -+ uint16_t level; /* OUT */ -+ uint8_t empty; /* OUT */ -+ uint8_t full; /* OUT */ -+}; -+ - struct rp1_gpio_init_args { - uint16_t gpio; - }; -@@ -195,6 +204,8 @@ struct rp1_access_hw_args { - #define PIO_IOC_SM_PUT _IOW(PIO_IOC_MAGIC, 41, struct rp1_pio_sm_put_args) - #define PIO_IOC_SM_GET _IOWR(PIO_IOC_MAGIC, 42, struct rp1_pio_sm_get_args) - #define PIO_IOC_SM_SET_DMACTRL _IOW(PIO_IOC_MAGIC, 43, struct rp1_pio_sm_set_dmactrl_args) -+#define PIO_IOC_SM_FIFO_STATE _IOW(PIO_IOC_MAGIC, 44, struct rp1_pio_sm_fifo_state_args) -+#define PIO_IOC_SM_DRAIN_TX _IOW(PIO_IOC_MAGIC, 45, struct rp1_pio_sm_clear_fifos_args) - - #define PIO_IOC_GPIO_INIT _IOW(PIO_IOC_MAGIC, 50, struct rp1_gpio_init_args) - #define PIO_IOC_GPIO_SET_FUNCTION _IOW(PIO_IOC_MAGIC, 51, struct rp1_gpio_set_function_args) diff --git a/target/linux/bcm27xx/patches-6.6/950-1359-usb-dwc3-core-add-support-for-setting-NAK-enhancemen.patch b/target/linux/bcm27xx/patches-6.6/950-1359-usb-dwc3-core-add-support-for-setting-NAK-enhancemen.patch new file mode 100644 index 0000000000..ed7dbb0e6c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1359-usb-dwc3-core-add-support-for-setting-NAK-enhancemen.patch @@ -0,0 +1,77 @@ +From bb53ca75f9e3631e753f397ccab704a8f975658b Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Wed, 6 Nov 2024 10:45:24 +0000 +Subject: [PATCH] usb: dwc3: core: add support for setting NAK enhancement bits + for FS/HS + +If a device frequently NAKs, it can exhaust the scheduled handshakes in +a frame. It will then not get polled by the controller until the next +frame interval. This is most noticeable on FS devices as the controller +schedules a small set of transactions only once per full-speed frame. + +Setting the ENH_PER_NAK_FS/LS bits in the GUCTL1 register increases the +number of transactions that can be scheduled to Async (Control/Bulk) +endpoints in the respective frame time. In the FS case, this only +applies to FS devices directly connected to root ports. + +Signed-off-by: Jonathan Bell +--- + drivers/usb/dwc3/core.c | 10 ++++++++++ + drivers/usb/dwc3/core.h | 6 ++++++ + 2 files changed, 16 insertions(+) + +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -1366,6 +1366,12 @@ static int dwc3_core_init(struct dwc3 *d + if (dwc->dis_tx_ipgap_linecheck_quirk) + reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS; + ++ if (dwc->enh_nak_fs_quirk) ++ reg |= DWC3_GUCTL1_NAK_PER_ENH_FS; ++ ++ if (dwc->enh_nak_hs_quirk) ++ reg |= DWC3_GUCTL1_NAK_PER_ENH_HS; ++ + if (dwc->parkmode_disable_ss_quirk) + reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS; + +@@ -1669,6 +1675,10 @@ static void dwc3_get_properties(struct d + "snps,resume-hs-terminations"); + dwc->ulpi_ext_vbus_drv = device_property_read_bool(dev, + "snps,ulpi-ext-vbus-drv"); ++ dwc->enh_nak_fs_quirk = device_property_read_bool(dev, ++ "snps,enhanced-nak-fs-quirk"); ++ dwc->enh_nak_hs_quirk = device_property_read_bool(dev, ++ "snps,enhanced-nak-hs-quirk"); + dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev, + "snps,parkmode-disable-ss-quirk"); + dwc->parkmode_disable_hs_quirk = device_property_read_bool(dev, +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -269,6 +269,8 @@ + #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28) + #define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK BIT(26) + #define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) ++#define DWC3_GUCTL1_NAK_PER_ENH_FS BIT(19) ++#define DWC3_GUCTL1_NAK_PER_ENH_HS BIT(18) + #define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17) + #define DWC3_GUCTL1_PARKMODE_DISABLE_HS BIT(16) + #define DWC3_GUCTL1_PARKMODE_DISABLE_FSLS BIT(15) +@@ -1118,6 +1120,8 @@ struct dwc3_scratchpad_array { + * generation after resume from suspend. + * @ulpi_ext_vbus_drv: Set to confiure the upli chip to drives CPEN pin + * VBUS with an external supply. ++ * @enh_nak_fs_quirk: Set to schedule more handshakes to Async FS endpoints. ++ * @enh_nak_hs_quirk: Set to schedule more handshakes to Async HS endpoints. + * @parkmode_disable_ss_quirk: If set, disable park mode feature for all + * Superspeed instances. + * @parkmode_disable_hs_quirk: If set, disable park mode feature for all +@@ -1348,6 +1352,8 @@ struct dwc3 { + unsigned dis_tx_ipgap_linecheck_quirk:1; + unsigned resume_hs_terminations:1; + unsigned ulpi_ext_vbus_drv:1; ++ unsigned enh_nak_fs_quirk:1; ++ unsigned enh_nak_hs_quirk:1; + unsigned parkmode_disable_ss_quirk:1; + unsigned parkmode_disable_hs_quirk:1; + unsigned parkmode_disable_fsls_quirk:1; diff --git a/target/linux/bcm27xx/patches-6.6/950-1360-DTS-rp1-set-enhanced-FS-NAK-quirk-for-usb3-controlle.patch b/target/linux/bcm27xx/patches-6.6/950-1360-DTS-rp1-set-enhanced-FS-NAK-quirk-for-usb3-controlle.patch new file mode 100644 index 0000000000..a305dc13fd --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1360-DTS-rp1-set-enhanced-FS-NAK-quirk-for-usb3-controlle.patch @@ -0,0 +1,30 @@ +From 803757627b48bdad9530b50053321fdea6dfcab4 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Wed, 6 Nov 2024 10:54:58 +0000 +Subject: [PATCH] DTS: rp1: set enhanced FS NAK quirk for usb3 controllers + +There seem to be only benefits, and no downsides. + +Signed-off-by: Jonathan Bell +--- + arch/arm64/boot/dts/broadcom/rp1.dtsi | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi ++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi +@@ -1077,6 +1077,7 @@ + usb3-lpm-capable; + snps,axi-pipe-limit = /bits/ 8 <8>; + snps,dis_rxdet_inp3_quirk; ++ snps,enhanced-nak-fs-quirk; + snps,parkmode-disable-ss-quirk; + snps,parkmode-disable-hs-quirk; + snps,parkmode-disable-fsls-quirk; +@@ -1093,6 +1094,7 @@ + usb3-lpm-capable; + snps,axi-pipe-limit = /bits/ 8 <8>; + snps,dis_rxdet_inp3_quirk; ++ snps,enhanced-nak-fs-quirk; + snps,parkmode-disable-ss-quirk; + snps,parkmode-disable-hs-quirk; + snps,parkmode-disable-fsls-quirk; diff --git a/target/linux/bcm27xx/patches-6.6/950-1360-misc-rp1-pio-Fix-parameter-checks-wihout-client.patch b/target/linux/bcm27xx/patches-6.6/950-1360-misc-rp1-pio-Fix-parameter-checks-wihout-client.patch deleted file mode 100644 index e68bd175d8..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-1360-misc-rp1-pio-Fix-parameter-checks-wihout-client.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 3687701e8d252864f440f91f1aedf8ffd58d6ee6 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 25 Nov 2024 21:51:13 +0000 -Subject: [PATCH] misc: rp1-pio: Fix parameter checks wihout client - -Passing bad parameters to an API call without a pio pointer will cause -a NULL pointer exception when the persistent error is set. Guard -against that. - -Signed-off-by: Phil Elwell ---- - include/linux/pio_rp1.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/include/linux/pio_rp1.h -+++ b/include/linux/pio_rp1.h -@@ -20,7 +20,7 @@ - #endif - - #define bad_params_if(client, test) \ -- ({ bool f = (test); if (f) pio_set_error(client, -EINVAL); \ -+ ({ bool f = (test); if (f && client) pio_set_error(client, -EINVAL); \ - if (f && PARAM_WARNINGS_ENABLED) WARN_ON((test)); \ - f; }) - diff --git a/target/linux/bcm27xx/patches-6.6/950-1361-drivers-usb-xhci-prevent-a-theoretical-race-on-non-c.patch b/target/linux/bcm27xx/patches-6.6/950-1361-drivers-usb-xhci-prevent-a-theoretical-race-on-non-c.patch new file mode 100644 index 0000000000..d3ea1041c2 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1361-drivers-usb-xhci-prevent-a-theoretical-race-on-non-c.patch @@ -0,0 +1,50 @@ +From e9e852af347ae3ccee4e7abb01f9ef91387980f9 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Wed, 6 Nov 2024 11:07:55 +0000 +Subject: [PATCH] drivers: usb: xhci: prevent a theoretical race on + non-coherent platforms + +For platforms that have xHCI controllers attached over PCIe, and +non-coherent routes to main memory, a theoretical race exists between +posting new TRBs to a ring, and writing to the doorbell register. + +In a contended system, write traffic from the CPU may be stalled before +the memory controller, whereas the CPU to Endpoint route is separate +and not likely to be contended. Similarly, the DMA route from the +endpoint to main memory may be separate and uncontended. + +Therefore the xHCI can receive a doorbell write and find a stale view +of a transfer ring. In cases where only a single TRB is ping-ponged at +a time, this can cause the endpoint to not get polled at all. + +Adding a readl() before the write forces a round-trip transaction +across PCIe, definitively serialising the CPU along the PCI +producer-consumer ordering rules. + +Signed-off-by: Jonathan Bell +--- + drivers/usb/host/xhci-ring.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -505,6 +505,19 @@ void xhci_ring_ep_doorbell(struct xhci_h + + trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id)); + ++ /* ++ * For non-coherent systems with PCIe DMA (such as Pi 4, Pi 5) there ++ * is a theoretical race between the TRB write and barrier, which ++ * is reported complete as soon as the write leaves the CPU domain, ++ * the doorbell write, which may be reported as complete by the RC ++ * at some arbitrary point, and the visibility of new TRBs in system ++ * RAM by the endpoint DMA engine. ++ * ++ * This read before the write positively serialises the CPU state ++ * by incurring a round-trip across the link. ++ */ ++ readl(db_addr); ++ + writel(DB_VALUE(ep_index, stream_id), db_addr); + /* flush the write */ + readl(db_addr); diff --git a/target/linux/bcm27xx/patches-6.6/950-1362-iio-humidity-dht11-Allow-non-zero-decimals.patch b/target/linux/bcm27xx/patches-6.6/950-1362-iio-humidity-dht11-Allow-non-zero-decimals.patch new file mode 100644 index 0000000000..19935d0ea5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1362-iio-humidity-dht11-Allow-non-zero-decimals.patch @@ -0,0 +1,32 @@ +From ce65ed02cb6707ae5c9f3a304f5b0124f4eed559 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 4 Nov 2024 14:10:53 +0000 +Subject: [PATCH] iio: humidity: dht11: Allow non-zero decimals + +The DHT11 datasheet is pretty cryptic, but it does suggest that after +each integer value (humidity and temperature) there are "decimal" +values. Validate these as integers in the range 0-9 and treat them as +tenths of a unit. + +Link: https://github.com/raspberrypi/linux/issues/6220 + +Signed-off-by: Phil Elwell +--- + drivers/iio/humidity/dht11.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/iio/humidity/dht11.c ++++ b/drivers/iio/humidity/dht11.c +@@ -152,9 +152,9 @@ static int dht11_decode(struct dht11 *dh + dht11->temperature = (((temp_int & 0x7f) << 8) + temp_dec) * + ((temp_int & 0x80) ? -100 : 100); + dht11->humidity = ((hum_int << 8) + hum_dec) * 100; +- } else if (temp_dec == 0 && hum_dec == 0) { /* DHT11 */ +- dht11->temperature = temp_int * 1000; +- dht11->humidity = hum_int * 1000; ++ } else if (temp_dec < 10 && hum_dec < 10) { /* DHT11 */ ++ dht11->temperature = temp_int * 1000 + temp_dec * 100; ++ dht11->humidity = hum_int * 1000 + hum_dec * 100; + } else { + dev_err(dht11->dev, + "Don't know how to decode data: %d %d %d %d\n", diff --git a/target/linux/bcm27xx/patches-6.6/950-1363-drm-vc4-Correct-condition-for-ignoring-a-plane-to-sr.patch b/target/linux/bcm27xx/patches-6.6/950-1363-drm-vc4-Correct-condition-for-ignoring-a-plane-to-sr.patch new file mode 100644 index 0000000000..9b018248a5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1363-drm-vc4-Correct-condition-for-ignoring-a-plane-to-sr.patch @@ -0,0 +1,41 @@ +From c3393ac1098d1f191e37eed73bf366ebc88ac4ee Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 11 Sep 2024 14:49:05 +0100 +Subject: [PATCH] drm/vc4: Correct condition for ignoring a plane to src rect + =0, not <1.0 + +The logic for dropping a plane less than zero didn't account for the +possibility that a plane could be being upscaled with a src_rect with +width/height < 1 pixel, but not 0 subpixels. + +Check for not 0 subpixels, not < 1, in both vc4 and vc6 paths. + +Fixes: dac616899f87 ("drm/vc4: Drop planes that have 0 destination size") +Fixes: f73b18eb0d48 ("drm/vc4: Drop planes that are completely off-screen") +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/vc4/vc4_plane.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -1160,7 +1160,8 @@ static int vc4_plane_mode_set(struct drm + width = vc4_state->src_w[0] >> 16; + height = vc4_state->src_h[0] >> 16; + +- if (!width || !height || !vc4_state->crtc_w || !vc4_state->crtc_h) { ++ if (!vc4_state->src_w[0] || !vc4_state->src_h[0] || ++ !vc4_state->crtc_w || !vc4_state->crtc_h) { + /* 0 source size probably means the plane is offscreen */ + vc4_state->dlist_initialized = 1; + return 0; +@@ -1698,7 +1699,8 @@ static int vc6_plane_mode_set(struct drm + width = vc4_state->src_w[0] >> 16; + height = vc4_state->src_h[0] >> 16; + +- if (!width || !height || !vc4_state->crtc_w || !vc4_state->crtc_h) { ++ if (!vc4_state->src_w[0] || !vc4_state->src_h[0] || ++ !vc4_state->crtc_w || !vc4_state->crtc_h) { + /* 0 source size probably means the plane is offscreen. + * 0 destination size is a redundant plane. + */ diff --git a/target/linux/bcm27xx/patches-6.6/950-1364-drm-vc4-Use-the-TPZ-scaling-filter-for-1x1-source-im.patch b/target/linux/bcm27xx/patches-6.6/950-1364-drm-vc4-Use-the-TPZ-scaling-filter-for-1x1-source-im.patch new file mode 100644 index 0000000000..5a7b3e4c1e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1364-drm-vc4-Use-the-TPZ-scaling-filter-for-1x1-source-im.patch @@ -0,0 +1,59 @@ +From ca621585c573cae54dc1235d90822e8bcef2f73d Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 11 Sep 2024 15:23:33 +0100 +Subject: [PATCH] drm/vc4: Use the TPZ scaling filter for 1x1 source images + +The documentation says that the TPZ filter can not upscale, +and requesting a scaling factor > 1:1 will output the original +image in the top left, and repeat the right/bottom most pixels +thereafter. +That fits perfectly with upscaling a 1x1 image which is done +a fair amount by some compositors to give solid colour, and it +saves a large amount of LBM (TPZ is based on src size, whilst +PPF is based on dest size). + +Select TPZ filter for images with source rectangle <=1. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/vc4/vc4_plane.c | 21 +++++++++++++++------ + 1 file changed, 15 insertions(+), 6 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -265,7 +265,11 @@ static enum vc4_scaling_mode vc4_get_sca + { + if (dst == src >> 16) + return VC4_SCALING_NONE; +- if (3 * dst >= 2 * (src >> 16)) ++ ++ if (src <= (1 << 16)) ++ /* Source rectangle <= 1 pixel can use TPZ for resize/upscale */ ++ return VC4_SCALING_TPZ; ++ else if (3 * dst >= 2 * (src >> 16)) + return VC4_SCALING_PPF; + else + return VC4_SCALING_TPZ; +@@ -560,12 +564,17 @@ static void vc4_write_tpz(struct vc4_pla + + WARN_ON_ONCE(vc4->gen > VC4_GEN_6); + +- scale = src / dst; ++ if ((dst << 16) < src) { ++ scale = src / dst; + +- /* The specs note that while the reciprocal would be defined +- * as (1<<32)/scale, ~0 is close enough. +- */ +- recip = ~0 / scale; ++ /* The specs note that while the reciprocal would be defined ++ * as (1<<32)/scale, ~0 is close enough. ++ */ ++ recip = ~0 / scale; ++ } else { ++ scale = (1 << 16) + 1; ++ recip = (1 << 16) - 1; ++ } + + vc4_dlist_write(vc4_state, + /* diff --git a/target/linux/bcm27xx/patches-6.6/950-1365-drm-Set-non-desktop-property-to-true-for-writeback-a.patch b/target/linux/bcm27xx/patches-6.6/950-1365-drm-Set-non-desktop-property-to-true-for-writeback-a.patch new file mode 100644 index 0000000000..e8f52cbc43 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1365-drm-Set-non-desktop-property-to-true-for-writeback-a.patch @@ -0,0 +1,30 @@ +From 68b0ff3549148e614e1733d773cee8e689c763c6 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 20 Aug 2024 16:25:10 +0100 +Subject: [PATCH] drm: Set non-desktop property to true for writeback and + virtual connectors + +The non-desktop property "Indicates the output should be ignored for +purposes of displaying a standard desktop environment or console." + +That sounds like it should be true for all writeback and virtual +connectors as you shouldn't render a desktop to them, so set it +by default. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/drm_connector.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/gpu/drm/drm_connector.c ++++ b/drivers/gpu/drm/drm_connector.c +@@ -361,7 +361,8 @@ static int __drm_connector_init(struct d + + drm_object_attach_property(&connector->base, + config->non_desktop_property, +- 0); ++ (connector_type != DRM_MODE_CONNECTOR_VIRTUAL && ++ connector_type != DRM_MODE_CONNECTOR_WRITEBACK) ? 0 : 1; + drm_object_attach_property(&connector->base, + config->tile_property, + 0); diff --git a/target/linux/bcm27xx/patches-6.6/950-1366-drm-Increase-plane_mask-to-64bit.patch b/target/linux/bcm27xx/patches-6.6/950-1366-drm-Increase-plane_mask-to-64bit.patch new file mode 100644 index 0000000000..e27058d44c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1366-drm-Increase-plane_mask-to-64bit.patch @@ -0,0 +1,101 @@ +From 8181e682d6f4ef209845ec24f0a1eb37764d6731 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Fri, 21 Oct 2022 14:26:12 +0100 +Subject: [PATCH] drm: Increase plane_mask to 64bit. + +The limit of 32 planes per DRM device is dictated by the use +of planes_mask returning a u32. + +Change to a u64 such that 64 planes can be supported by a device. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/drm_atomic.c | 2 +- + drivers/gpu/drm/drm_framebuffer.c | 2 +- + drivers/gpu/drm/drm_mode_config.c | 2 +- + drivers/gpu/drm/drm_plane.c | 2 +- + drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c | 2 +- + include/drm/drm_crtc.h | 2 +- + include/drm/drm_plane.h | 4 ++-- + 7 files changed, 8 insertions(+), 8 deletions(-) + +--- a/drivers/gpu/drm/drm_atomic.c ++++ b/drivers/gpu/drm/drm_atomic.c +@@ -451,7 +451,7 @@ static void drm_atomic_crtc_print_state( + drm_printf(p, "\tactive_changed=%d\n", state->active_changed); + drm_printf(p, "\tconnectors_changed=%d\n", state->connectors_changed); + drm_printf(p, "\tcolor_mgmt_changed=%d\n", state->color_mgmt_changed); +- drm_printf(p, "\tplane_mask=%x\n", state->plane_mask); ++ drm_printf(p, "\tplane_mask=%llx\n", state->plane_mask); + drm_printf(p, "\tconnector_mask=%x\n", state->connector_mask); + drm_printf(p, "\tencoder_mask=%x\n", state->encoder_mask); + drm_printf(p, "\tmode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&state->mode)); +--- a/drivers/gpu/drm/drm_framebuffer.c ++++ b/drivers/gpu/drm/drm_framebuffer.c +@@ -959,7 +959,7 @@ static int atomic_remove_fb(struct drm_f + struct drm_connector *conn __maybe_unused; + struct drm_connector_state *conn_state; + int i, ret; +- unsigned plane_mask; ++ u64 plane_mask; + bool disable_crtcs = false; + + retry_disable: +--- a/drivers/gpu/drm/drm_mode_config.c ++++ b/drivers/gpu/drm/drm_mode_config.c +@@ -636,7 +636,7 @@ void drm_mode_config_validate(struct drm + struct drm_encoder *encoder; + struct drm_crtc *crtc; + struct drm_plane *plane; +- u32 primary_with_crtc = 0, cursor_with_crtc = 0; ++ u64 primary_with_crtc = 0, cursor_with_crtc = 0; + unsigned int num_primary = 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) +--- a/drivers/gpu/drm/drm_plane.c ++++ b/drivers/gpu/drm/drm_plane.c +@@ -249,7 +249,7 @@ static int __drm_universal_plane_init(st + int ret; + + /* plane index is used with 32bit bitmasks */ +- if (WARN_ON(config->num_total_plane >= 32)) ++ if (WARN_ON(config->num_total_plane >= 64)) + return -EINVAL; + + /* +--- a/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c ++++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c +@@ -230,7 +230,7 @@ static int ipu_crtc_atomic_check(struct + { + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, + crtc); +- u32 primary_plane_mask = drm_plane_mask(crtc->primary); ++ u64 primary_plane_mask = drm_plane_mask(crtc->primary); + + if (crtc_state->active && (primary_plane_mask & crtc_state->plane_mask) == 0) + return -EINVAL; +--- a/include/drm/drm_crtc.h ++++ b/include/drm/drm_crtc.h +@@ -192,7 +192,7 @@ struct drm_crtc_state { + * @plane_mask: Bitmask of drm_plane_mask(plane) of planes attached to + * this CRTC. + */ +- u32 plane_mask; ++ u64 plane_mask; + + /** + * @connector_mask: Bitmask of drm_connector_mask(connector) of +--- a/include/drm/drm_plane.h ++++ b/include/drm/drm_plane.h +@@ -915,9 +915,9 @@ static inline unsigned int drm_plane_ind + * drm_plane_mask - find the mask of a registered plane + * @plane: plane to find mask for + */ +-static inline u32 drm_plane_mask(const struct drm_plane *plane) ++static inline u64 drm_plane_mask(const struct drm_plane *plane) + { +- return 1 << drm_plane_index(plane); ++ return 1ULL << drm_plane_index(plane); + } + + struct drm_plane * drm_plane_from_index(struct drm_device *dev, int idx); diff --git a/target/linux/bcm27xx/patches-6.6/950-1367-drm-vc4-Increase-number-of-overlay-planes-from-16-to.patch b/target/linux/bcm27xx/patches-6.6/950-1367-drm-vc4-Increase-number-of-overlay-planes-from-16-to.patch new file mode 100644 index 0000000000..6eae6fc2ac --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1367-drm-vc4-Increase-number-of-overlay-planes-from-16-to.patch @@ -0,0 +1,42 @@ +From 5dc4cef7d7fcda4ea59b9e456a835fa54336af6b Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Fri, 21 Oct 2022 14:27:45 +0100 +Subject: [PATCH] drm/vc4: Increase number of overlay planes from 16 to 48 + +The HVS can accept an arbitrary number of planes, provided +that the overall pixel read load is within limits, and +the display list can fit into the dlist memory. + +Now that DRM will support 64 planes per device, increase +the number of overlay planes from 16 to 48 so that the +dlist complexity can be increased (eg 4x4 video wall on +each of 3 displays). + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/drm_connector.c | 2 +- + drivers/gpu/drm/vc4/vc4_plane.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/drm_connector.c ++++ b/drivers/gpu/drm/drm_connector.c +@@ -362,7 +362,7 @@ static int __drm_connector_init(struct d + drm_object_attach_property(&connector->base, + config->non_desktop_property, + (connector_type != DRM_MODE_CONNECTOR_VIRTUAL && +- connector_type != DRM_MODE_CONNECTOR_WRITEBACK) ? 0 : 1; ++ connector_type != DRM_MODE_CONNECTOR_WRITEBACK) ? 0 : 1); + drm_object_attach_property(&connector->base, + config->tile_property, + 0); +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -2517,7 +2517,7 @@ struct drm_plane *vc4_plane_init(struct + return plane; + } + +-#define VC4_NUM_OVERLAY_PLANES 16 ++#define VC4_NUM_OVERLAY_PLANES 48 + + int vc4_plane_create_additional_planes(struct drm_device *drm) + { diff --git a/target/linux/bcm27xx/patches-6.6/950-1368-drm-vc4-Assign-32-overlay-planes-to-writeback-only.patch b/target/linux/bcm27xx/patches-6.6/950-1368-drm-vc4-Assign-32-overlay-planes-to-writeback-only.patch new file mode 100644 index 0000000000..40eff3cc1f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1368-drm-vc4-Assign-32-overlay-planes-to-writeback-only.patch @@ -0,0 +1,71 @@ +From dd340cb082a020fbd42b794493ffd063dd8e15b4 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 15 Aug 2023 15:44:34 +0100 +Subject: [PATCH] drm/vc4: Assign 32 overlay planes to writeback only + +Instead of having 48 generic overlay planes, assign 32 to the +writeback connector so that there is no ambiguity in wlroots +when trying to find a plane for composition using the writeback +connector vs display. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/vc4/vc4_plane.c | 34 +++++++++++++++++++++++++++++++-- + 1 file changed, 32 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -2517,13 +2517,28 @@ struct drm_plane *vc4_plane_init(struct + return plane; + } + +-#define VC4_NUM_OVERLAY_PLANES 48 ++#define VC4_NUM_OVERLAY_PLANES 16 ++#define VC4_NUM_TXP_OVERLAY_PLANES 32 + + int vc4_plane_create_additional_planes(struct drm_device *drm) + { + struct drm_plane *cursor_plane; + struct drm_crtc *crtc; + unsigned int i; ++ struct drm_crtc *txp_crtc; ++ uint32_t non_txp_crtc_mask; ++ ++ drm_for_each_crtc(crtc, drm) { ++ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); ++ ++ if (vc4_crtc->feeds_txp) { ++ txp_crtc = crtc; ++ break; ++ } ++ } ++ ++ non_txp_crtc_mask = GENMASK(drm->mode_config.num_crtc - 1, 0) - ++ drm_crtc_mask(txp_crtc); + + /* Set up some arbitrary number of planes. We're not limited + * by a set number of physical registers, just the space in +@@ -2537,7 +2552,22 @@ int vc4_plane_create_additional_planes(s + for (i = 0; i < VC4_NUM_OVERLAY_PLANES; i++) { + struct drm_plane *plane = + vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY, +- GENMASK(drm->mode_config.num_crtc - 1, 0)); ++ non_txp_crtc_mask); ++ ++ if (IS_ERR(plane)) ++ continue; ++ ++ /* Create zpos property. Max of all the overlays + 1 primary + ++ * 1 cursor plane on a crtc. ++ */ ++ drm_plane_create_zpos_property(plane, i + 1, 1, ++ VC4_NUM_OVERLAY_PLANES + 1); ++ } ++ ++ for (i = 0; i < VC4_NUM_TXP_OVERLAY_PLANES; i++) { ++ struct drm_plane *plane = ++ vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY, ++ drm_crtc_mask(txp_crtc)); + + if (IS_ERR(plane)) + continue; diff --git a/target/linux/bcm27xx/patches-6.6/950-1369-drm-Add-a-DRM_MODE_TRANSPOSE-option-to-the-DRM-rotat.patch b/target/linux/bcm27xx/patches-6.6/950-1369-drm-Add-a-DRM_MODE_TRANSPOSE-option-to-the-DRM-rotat.patch new file mode 100644 index 0000000000..ccda8b05c2 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1369-drm-Add-a-DRM_MODE_TRANSPOSE-option-to-the-DRM-rotat.patch @@ -0,0 +1,47 @@ +From b3b3d12cf0734318a0fed0b33e13d714188369db Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 22 Oct 2024 17:17:31 +0100 +Subject: [PATCH] drm: Add a DRM_MODE_TRANSPOSE option to the DRM rotation + property + +Some hardware will implement transpose as a rotation operation, +which when combined with X and Y reflect can result in a rotation, +but is a discrete operation in its own right. + +Add an option for transpose only. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/drm_blend.c | 3 +++ + include/uapi/drm/drm_mode.h | 1 + + 2 files changed, 4 insertions(+) + +--- a/drivers/gpu/drm/drm_blend.c ++++ b/drivers/gpu/drm/drm_blend.c +@@ -263,6 +263,8 @@ EXPORT_SYMBOL(drm_plane_create_alpha_pro + * "reflect-x" + * DRM_MODE_REFLECT_Y: + * "reflect-y" ++ * DRM_MODE_TRANSPOSE: ++ * "transpose" + * + * Rotation is the specified amount in degrees in counter clockwise direction, + * the X and Y axis are within the source rectangle, i.e. the X/Y axis before +@@ -280,6 +282,7 @@ int drm_plane_create_rotation_property(s + { __builtin_ffs(DRM_MODE_ROTATE_270) - 1, "rotate-270" }, + { __builtin_ffs(DRM_MODE_REFLECT_X) - 1, "reflect-x" }, + { __builtin_ffs(DRM_MODE_REFLECT_Y) - 1, "reflect-y" }, ++ { __builtin_ffs(DRM_MODE_TRANSPOSE) - 1, "transpose" }, + }; + struct drm_property *prop; + +--- a/include/uapi/drm/drm_mode.h ++++ b/include/uapi/drm/drm_mode.h +@@ -203,6 +203,7 @@ extern "C" { + */ + #define DRM_MODE_REFLECT_X (1<<4) + #define DRM_MODE_REFLECT_Y (1<<5) ++#define DRM_MODE_TRANSPOSE (1<<6) + + /* + * DRM_MODE_REFLECT_MASK diff --git a/target/linux/bcm27xx/patches-6.6/950-1370-drm-Add-a-rotation-parameter-to-connectors.patch b/target/linux/bcm27xx/patches-6.6/950-1370-drm-Add-a-rotation-parameter-to-connectors.patch new file mode 100644 index 0000000000..e803a7732f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1370-drm-Add-a-rotation-parameter-to-connectors.patch @@ -0,0 +1,164 @@ +From 8fec3ff870499256f2c18fe7983f6ed3fea4faaf Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 22 Oct 2024 17:22:40 +0100 +Subject: [PATCH] drm: Add a rotation parameter to connectors. + +Some connectors, particularly writeback, can implement flip +or transpose operations as writing back to memory. + +Add a connector rotation property to control this. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/drm_atomic_uapi.c | 4 +++ + drivers/gpu/drm/drm_blend.c | 50 ++++++++++++++++++++++++------- + include/drm/drm_blend.h | 5 ++++ + include/drm/drm_connector.h | 11 +++++++ + 4 files changed, 60 insertions(+), 10 deletions(-) + +--- a/drivers/gpu/drm/drm_atomic_uapi.c ++++ b/drivers/gpu/drm/drm_atomic_uapi.c +@@ -811,6 +811,8 @@ static int drm_atomic_connector_set_prop + state->max_requested_bpc = val; + } else if (property == connector->privacy_screen_sw_state_property) { + state->privacy_screen_sw_state = val; ++ } else if (property == connector->rotation_property) { ++ state->rotation = val; + } else if (connector->funcs->atomic_set_property) { + return connector->funcs->atomic_set_property(connector, + state, property, val); +@@ -900,6 +902,8 @@ drm_atomic_connector_get_property(struct + *val = state->max_requested_bpc; + } else if (property == connector->privacy_screen_sw_state_property) { + *val = state->privacy_screen_sw_state; ++ } else if (property == connector->rotation_property) { ++ *val = state->rotation; + } else if (connector->funcs->atomic_get_property) { + return connector->funcs->atomic_get_property(connector, + state, property, val); +--- a/drivers/gpu/drm/drm_blend.c ++++ b/drivers/gpu/drm/drm_blend.c +@@ -235,6 +235,16 @@ int drm_plane_create_alpha_property(stru + } + EXPORT_SYMBOL(drm_plane_create_alpha_property); + ++static const struct drm_prop_enum_list drm_rotate_props[] = { ++ { __builtin_ffs(DRM_MODE_ROTATE_0) - 1, "rotate-0" }, ++ { __builtin_ffs(DRM_MODE_ROTATE_90) - 1, "rotate-90" }, ++ { __builtin_ffs(DRM_MODE_ROTATE_180) - 1, "rotate-180" }, ++ { __builtin_ffs(DRM_MODE_ROTATE_270) - 1, "rotate-270" }, ++ { __builtin_ffs(DRM_MODE_REFLECT_X) - 1, "reflect-x" }, ++ { __builtin_ffs(DRM_MODE_REFLECT_Y) - 1, "reflect-y" }, ++ { __builtin_ffs(DRM_MODE_TRANSPOSE) - 1, "transpose" }, ++}; ++ + /** + * drm_plane_create_rotation_property - create a new rotation property + * @plane: drm plane +@@ -275,15 +285,6 @@ int drm_plane_create_rotation_property(s + unsigned int rotation, + unsigned int supported_rotations) + { +- static const struct drm_prop_enum_list props[] = { +- { __builtin_ffs(DRM_MODE_ROTATE_0) - 1, "rotate-0" }, +- { __builtin_ffs(DRM_MODE_ROTATE_90) - 1, "rotate-90" }, +- { __builtin_ffs(DRM_MODE_ROTATE_180) - 1, "rotate-180" }, +- { __builtin_ffs(DRM_MODE_ROTATE_270) - 1, "rotate-270" }, +- { __builtin_ffs(DRM_MODE_REFLECT_X) - 1, "reflect-x" }, +- { __builtin_ffs(DRM_MODE_REFLECT_Y) - 1, "reflect-y" }, +- { __builtin_ffs(DRM_MODE_TRANSPOSE) - 1, "transpose" }, +- }; + struct drm_property *prop; + + WARN_ON((supported_rotations & DRM_MODE_ROTATE_MASK) == 0); +@@ -291,7 +292,8 @@ int drm_plane_create_rotation_property(s + WARN_ON(rotation & ~supported_rotations); + + prop = drm_property_create_bitmask(plane->dev, 0, "rotation", +- props, ARRAY_SIZE(props), ++ drm_rotate_props, ++ ARRAY_SIZE(drm_rotate_props), + supported_rotations); + if (!prop) + return -ENOMEM; +@@ -307,6 +309,34 @@ int drm_plane_create_rotation_property(s + } + EXPORT_SYMBOL(drm_plane_create_rotation_property); + ++int drm_connector_create_rotation_property(struct drm_connector *conn, ++ unsigned int rotation, ++ unsigned int supported_rotations) ++{ ++ struct drm_property *prop; ++ ++ WARN_ON((supported_rotations & DRM_MODE_ROTATE_MASK) == 0); ++ WARN_ON(!is_power_of_2(rotation & DRM_MODE_ROTATE_MASK)); ++ WARN_ON(rotation & ~supported_rotations); ++ ++ prop = drm_property_create_bitmask(conn->dev, 0, "rotation", ++ drm_rotate_props, ++ ARRAY_SIZE(drm_rotate_props), ++ supported_rotations); ++ if (!prop) ++ return -ENOMEM; ++ ++ drm_object_attach_property(&conn->base, prop, rotation); ++ ++ if (conn->state) ++ conn->state->rotation = rotation; ++ ++ conn->rotation_property = prop; ++ ++ return 0; ++} ++EXPORT_SYMBOL(drm_connector_create_rotation_property); ++ + /** + * drm_rotation_simplify() - Try to simplify the rotation + * @rotation: Rotation to be simplified +--- a/include/drm/drm_blend.h ++++ b/include/drm/drm_blend.h +@@ -34,6 +34,7 @@ + struct drm_device; + struct drm_atomic_state; + struct drm_plane; ++struct drm_connector; + + static inline bool drm_rotation_90_or_270(unsigned int rotation) + { +@@ -58,4 +59,8 @@ int drm_atomic_normalize_zpos(struct drm + struct drm_atomic_state *state); + int drm_plane_create_blend_mode_property(struct drm_plane *plane, + unsigned int supported_modes); ++ ++int drm_connector_create_rotation_property(struct drm_connector *conn, ++ unsigned int rotation, ++ unsigned int supported_rotations); + #endif +--- a/include/drm/drm_connector.h ++++ b/include/drm/drm_connector.h +@@ -1029,6 +1029,11 @@ struct drm_connector_state { + * DRM blob property for HDR output metadata + */ + struct drm_property_blob *hdr_output_metadata; ++ ++ /** ++ * @rotation: Connector property to rotate the maximum output image. ++ */ ++ u32 rotation; + }; + + /** +@@ -1696,6 +1701,12 @@ struct drm_connector { + */ + struct drm_property *privacy_screen_hw_state_property; + ++ /** ++ * @rotation_property: Optional DRM property controlling rotation of the ++ * output. ++ */ ++ struct drm_property *rotation_property; ++ + #define DRM_CONNECTOR_POLL_HPD (1 << 0) + #define DRM_CONNECTOR_POLL_CONNECT (1 << 1) + #define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2) diff --git a/target/linux/bcm27xx/patches-6.6/950-1371-drm-vc4-txp-Add-a-rotation-property-to-the-writeback.patch b/target/linux/bcm27xx/patches-6.6/950-1371-drm-vc4-txp-Add-a-rotation-property-to-the-writeback.patch new file mode 100644 index 0000000000..b0e5566ac1 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1371-drm-vc4-txp-Add-a-rotation-property-to-the-writeback.patch @@ -0,0 +1,65 @@ +From 8346446098032c62d1de891a97c7f62264b18f81 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 14 Aug 2024 16:41:07 +0100 +Subject: [PATCH] drm/vc4: txp: Add a rotation property to the writeback + connector + +The txp block can implement transpose as it writes out the image +data, so expose that through the new connector rotation property. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/vc4/vc4_txp.c | 21 +++++++++++++++++---- + 1 file changed, 17 insertions(+), 4 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_txp.c ++++ b/drivers/gpu/drm/vc4/vc4_txp.c +@@ -15,6 +15,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -259,10 +260,15 @@ static int vc4_txp_connector_atomic_chec + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + + fb = conn_state->writeback_job->fb; +- if (fb->width != crtc_state->mode.hdisplay || +- fb->height != crtc_state->mode.vdisplay) { +- DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n", +- fb->width, fb->height); ++ if ((conn_state->rotation == DRM_MODE_ROTATE_0 && ++ fb->width != crtc_state->mode.hdisplay && ++ fb->height != crtc_state->mode.vdisplay) || ++ (conn_state->rotation == (DRM_MODE_ROTATE_0 | DRM_MODE_TRANSPOSE) && ++ fb->width != crtc_state->mode.vdisplay && ++ fb->height != crtc_state->mode.hdisplay)) { ++ DRM_DEBUG_KMS("Invalid framebuffer size %ux%u vs mode %ux%u\n", ++ fb->width, fb->height, ++ crtc_state->mode.hdisplay, crtc_state->mode.vdisplay); + return -EINVAL; + } + +@@ -330,6 +336,9 @@ static void vc4_txp_connector_atomic_com + */ + ctrl |= TXP_ALPHA_INVERT; + ++ if (conn_state->rotation & DRM_MODE_TRANSPOSE) ++ ctrl |= TXP_TRANSPOSE; ++ + if (!drm_dev_enter(drm, &idx)) + return; + +@@ -608,6 +617,10 @@ static int vc4_txp_bind(struct device *d + if (ret) + return ret; + ++ drm_connector_create_rotation_property(&txp->connector.base, DRM_MODE_ROTATE_0, ++ DRM_MODE_ROTATE_0 | ++ DRM_MODE_TRANSPOSE); ++ + ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0, + dev_name(dev), txp); + if (ret) diff --git a/target/linux/bcm27xx/patches-6.6/950-1372-dmaengine-dw-axi-dmac-Allow-client-chosen-width.patch b/target/linux/bcm27xx/patches-6.6/950-1372-dmaengine-dw-axi-dmac-Allow-client-chosen-width.patch new file mode 100644 index 0000000000..90688668b8 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1372-dmaengine-dw-axi-dmac-Allow-client-chosen-width.patch @@ -0,0 +1,40 @@ +From a2fa911d90495762047c05dec4241308ae61ca36 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 19 Sep 2024 18:05:00 +0100 +Subject: [PATCH] dmaengine: dw-axi-dmac: Allow client-chosen width + +For devices where transfer lengths are not known upfront, there is a +danger when the destination is wider than the source that partial words +can be lost at the end of a transfer. Ideally the controller would be +able to flush the residue, but it can't - it's not even possible to tell +that there is any. + +Instead, allow the client driver to avoid the problem by setting a +smaller width. + +Signed-off-by: Phil Elwell +--- + drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c ++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +@@ -724,6 +724,18 @@ static int dw_axi_dma_set_hw_desc(struct + case DMA_DEV_TO_MEM: + reg_burst_msize = axi_dma_encode_msize(chan->config.src_maxburst); + reg_width = __ffs(chan->config.src_addr_width); ++ /* ++ * For devices where transfer lengths are not known upfront, ++ * there is a danger when the destination is wider than the ++ * source that partial words can be lost at the end of a transfer. ++ * Ideally the controller would be able to flush the residue, but ++ * it can't - it's not even possible to tell that there is any. ++ * Instead, allow the client driver to avoid the problem by setting ++ * a smaller width. ++ */ ++ if (chan->config.dst_addr_width && ++ (chan->config.dst_addr_width < mem_width)) ++ mem_width = chan->config.dst_addr_width; + device_addr = phys_to_dma(chan->chip->dev, chan->config.src_addr); + ctllo = reg_width << CH_CTL_L_SRC_WIDTH_POS | + mem_width << CH_CTL_L_DST_WIDTH_POS | diff --git a/target/linux/bcm27xx/patches-6.6/950-1373-spi-dw-Let-the-DMAC-set-the-transfer-widths.patch b/target/linux/bcm27xx/patches-6.6/950-1373-spi-dw-Let-the-DMAC-set-the-transfer-widths.patch new file mode 100644 index 0000000000..786af04036 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1373-spi-dw-Let-the-DMAC-set-the-transfer-widths.patch @@ -0,0 +1,31 @@ +From 5cf7209c294a58029984880d4858e2d3c7e46a3c Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 19 Sep 2024 18:12:12 +0100 +Subject: [PATCH] spi: dw: Let the DMAC set the transfer widths + +SPI transfers are of defined length, unlike some UART traffic, so it is +safe to let the DMA controller choose a suitable memory width. + +Signed-off-by: Phil Elwell +--- + drivers/spi/spi-dw-dma.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/drivers/spi/spi-dw-dma.c ++++ b/drivers/spi/spi-dw-dma.c +@@ -330,7 +330,6 @@ static int dw_spi_dma_config_tx(struct d + txconf.direction = DMA_MEM_TO_DEV; + txconf.dst_addr = dws->dma_addr; + txconf.dst_maxburst = dws->txburst; +- txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + txconf.dst_addr_width = dw_spi_dma_convert_width(dws->n_bytes); + txconf.device_fc = false; + +@@ -431,7 +430,6 @@ static int dw_spi_dma_config_rx(struct d + rxconf.direction = DMA_DEV_TO_MEM; + rxconf.src_addr = dws->dma_addr; + rxconf.src_maxburst = dws->rxburst; +- rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + rxconf.src_addr_width = dw_spi_dma_convert_width(dws->n_bytes); + rxconf.device_fc = false; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1374-serial-pl011-Request-a-memory-width-of-1-byte.patch b/target/linux/bcm27xx/patches-6.6/950-1374-serial-pl011-Request-a-memory-width-of-1-byte.patch new file mode 100644 index 0000000000..860d35b2f3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1374-serial-pl011-Request-a-memory-width-of-1-byte.patch @@ -0,0 +1,25 @@ +From 8894298105f4cb41dfa41e0b0d3c40c3f7b92c44 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 19 Sep 2024 18:22:24 +0100 +Subject: [PATCH] serial: pl011: Request a memory width of 1 byte + +In order to avoid losing residue bytes when a receive is terminated +early, set the destination width to single bytes. + +Link: https://github.com/raspberrypi/linux/issues/6365 + +Signed-off-by: Phil Elwell +--- + drivers/tty/serial/amba-pl011.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/tty/serial/amba-pl011.c ++++ b/drivers/tty/serial/amba-pl011.c +@@ -468,6 +468,7 @@ static void pl011_dma_probe(struct uart_ + .src_addr = uap->port.mapbase + + pl011_reg_to_offset(uap, REG_DR), + .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, ++ .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .direction = DMA_DEV_TO_MEM, + .src_maxburst = uap->fifosize >> 2, + .device_fc = false, diff --git a/target/linux/bcm27xx/patches-6.6/950-1377-drivers-usb-xhci-set-HID-bit-in-streaming-endpoint-c.patch b/target/linux/bcm27xx/patches-6.6/950-1377-drivers-usb-xhci-set-HID-bit-in-streaming-endpoint-c.patch new file mode 100644 index 0000000000..230537f505 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1377-drivers-usb-xhci-set-HID-bit-in-streaming-endpoint-c.patch @@ -0,0 +1,56 @@ +From 66aef6ce3557edd9d58d794e4a800c5be49ca0e7 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Mon, 11 Nov 2024 10:30:38 +0000 +Subject: [PATCH] drivers: usb: xhci: set HID bit in streaming endpoint + contexts + +The xHC may commence Host Initiated Data Moves for streaming endpoints - +see USB3.2 spec s8.12.1.4.2.4. However, this behaviour is typically +counterproductive as the submission of UAS URBs in {Status, Data, +Command} order and 1 outstanding IO per stream ID means the device never +enters Move Data after a HIMD for Status or Data stages with the same +stream ID. For OUT transfers this is especially inefficient as the host +will start transmitting multiple bulk packets as a burst, all of which +get NAKed by the device - wasting bandwidth. + +Also, some buggy UAS adapters don't properly handle the EP flow control +state this creates - e.g. RTL9210. + +Set Host Initiated Data Move Disable to always defer stream selection to +the device. xHC implementations may treat this field as "don't care, +forced to 1" anyway - xHCI 1.2 s4.12.1. + +Signed-off-by: Jonathan Bell +--- + drivers/usb/host/xhci-mem.c | 8 ++++++++ + drivers/usb/host/xhci.h | 2 ++ + 2 files changed, 10 insertions(+) + +--- a/drivers/usb/host/xhci-mem.c ++++ b/drivers/usb/host/xhci-mem.c +@@ -716,6 +716,14 @@ void xhci_setup_streams_ep_input_ctx(str + ep_ctx->ep_info &= cpu_to_le32(~EP_MAXPSTREAMS_MASK); + ep_ctx->ep_info |= cpu_to_le32(EP_MAXPSTREAMS(max_primary_streams) + | EP_HAS_LSA); ++ ++ /* ++ * Set Host Initiated Data Move Disable to always defer stream ++ * selection to the device. xHC implementations may treat this ++ * field as "don't care, forced to 1" anyway - xHCI 1.2 s4.12.1. ++ */ ++ ep_ctx->ep_info2 |= EP_HID; ++ + ep_ctx->deq = cpu_to_le64(stream_info->ctx_array_dma); + } + +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -492,6 +492,8 @@ struct xhci_ep_ctx { + #define CTX_TO_EP_MAXPSTREAMS(p) (((p) & EP_MAXPSTREAMS_MASK) >> 10) + /* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */ + #define EP_HAS_LSA (1 << 15) ++/* Host initiated data move disable in info2 */ ++#define EP_HID (1 << 7) + /* hosts with LEC=1 use bits 31:24 as ESIT high bits. */ + #define CTX_TO_MAX_ESIT_PAYLOAD_HI(p) (((p) >> 24) & 0xff) + diff --git a/target/linux/bcm27xx/patches-6.6/950-1379-media-i2c-imx477-Add-options-for-slightly-modifying-.patch b/target/linux/bcm27xx/patches-6.6/950-1379-media-i2c-imx477-Add-options-for-slightly-modifying-.patch new file mode 100644 index 0000000000..ee06051957 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1379-media-i2c-imx477-Add-options-for-slightly-modifying-.patch @@ -0,0 +1,199 @@ +From 35e50ee3d66e014d869f0d7a3468bef964d26d32 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Thu, 14 Nov 2024 13:14:02 +0000 +Subject: [PATCH] media: i2c: imx477: Add options for slightly modifying the + link freq + +The default link frequency of 450MHz has been noted to interfere +with GPS if they are in close proximty. +Add the option for 453 and 456MHz to move the signal slightly out +of the band. (447MHz can not be offered as corruption is then observed +on the 133x992 10bit mode). + +Signed-off-by: Dave Stevenson + +fixup imx477 gps +--- + drivers/media/i2c/imx477.c | 86 +++++++++++++++++++++++++++++--------- + 1 file changed, 67 insertions(+), 19 deletions(-) + +--- a/drivers/media/i2c/imx477.c ++++ b/drivers/media/i2c/imx477.c +@@ -164,8 +164,48 @@ struct imx477_mode { + struct imx477_reg_list reg_list; + }; + +-static const s64 imx477_link_freq_menu[] = { +- IMX477_DEFAULT_LINK_FREQ, ++/* Link frequency setup */ ++enum { ++ IMX477_LINK_FREQ_450MHZ, ++ IMX477_LINK_FREQ_453MHZ, ++ IMX477_LINK_FREQ_456MHZ, ++}; ++ ++static const s64 link_freqs[] = { ++ [IMX477_LINK_FREQ_450MHZ] = 450000000, ++ [IMX477_LINK_FREQ_453MHZ] = 453000000, ++ [IMX477_LINK_FREQ_456MHZ] = 456000000, ++}; ++ ++/* 450MHz is the nominal "default" link frequency */ ++static const struct imx477_reg link_450Mhz_regs[] = { ++ {0x030E, 0x00}, ++ {0x030F, 0x96}, ++}; ++ ++static const struct imx477_reg link_453Mhz_regs[] = { ++ {0x030E, 0x00}, ++ {0x030F, 0x97}, ++}; ++ ++static const struct imx477_reg link_456Mhz_regs[] = { ++ {0x030E, 0x00}, ++ {0x030F, 0x98}, ++}; ++ ++static const struct imx477_reg_list link_freq_regs[] = { ++ [IMX477_LINK_FREQ_450MHZ] = { ++ .regs = link_450Mhz_regs, ++ .num_of_regs = ARRAY_SIZE(link_450Mhz_regs) ++ }, ++ [IMX477_LINK_FREQ_453MHZ] = { ++ .regs = link_453Mhz_regs, ++ .num_of_regs = ARRAY_SIZE(link_453Mhz_regs) ++ }, ++ [IMX477_LINK_FREQ_456MHZ] = { ++ .regs = link_456Mhz_regs, ++ .num_of_regs = ARRAY_SIZE(link_456Mhz_regs) ++ }, + }; + + static const struct imx477_reg mode_common_regs[] = { +@@ -558,8 +598,6 @@ static const struct imx477_reg mode_4056 + {0x0309, 0x0c}, + {0x030b, 0x02}, + {0x030d, 0x02}, +- {0x030e, 0x00}, +- {0x030f, 0x96}, + {0x0310, 0x01}, + {0x0820, 0x07}, + {0x0821, 0x08}, +@@ -659,8 +697,6 @@ static const struct imx477_reg mode_2028 + {0x0309, 0x0c}, + {0x030b, 0x02}, + {0x030d, 0x02}, +- {0x030e, 0x00}, +- {0x030f, 0x96}, + {0x0310, 0x01}, + {0x0820, 0x07}, + {0x0821, 0x08}, +@@ -760,8 +796,6 @@ static const struct imx477_reg mode_2028 + {0x0309, 0x0c}, + {0x030b, 0x02}, + {0x030d, 0x02}, +- {0x030e, 0x00}, +- {0x030f, 0x96}, + {0x0310, 0x01}, + {0x0820, 0x07}, + {0x0821, 0x08}, +@@ -890,8 +924,6 @@ static const struct imx477_reg mode_1332 + {0x0309, 0x0a}, + {0x030b, 0x02}, + {0x030d, 0x02}, +- {0x030e, 0x00}, +- {0x030f, 0x96}, + {0x0310, 0x01}, + {0x0820, 0x07}, + {0x0821, 0x08}, +@@ -1121,6 +1153,8 @@ struct imx477 { + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + ++ unsigned int link_freq_idx; ++ + /* Current mode */ + const struct imx477_mode *mode; + +@@ -1712,7 +1746,7 @@ static int imx477_get_selection(struct v + static int imx477_start_streaming(struct imx477 *imx477) + { + struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); +- const struct imx477_reg_list *reg_list; ++ const struct imx477_reg_list *reg_list, *freq_regs; + const struct imx477_reg_list *extra_regs; + int ret, tm; + +@@ -1725,6 +1759,13 @@ static int imx477_start_streaming(struct + extra_regs->num_of_regs); + } + ++ if (!ret) { ++ /* Update the link frequency registers */ ++ freq_regs = &link_freq_regs[imx477->link_freq_idx]; ++ ret = imx477_write_regs(imx477, freq_regs->regs, ++ freq_regs->num_of_regs); ++ } ++ + if (ret) { + dev_err(&client->dev, "%s failed to set common settings\n", + __func__); +@@ -2010,9 +2051,8 @@ static int imx477_init_controls(struct i + /* LINK_FREQ is also read only */ + imx477->link_freq = + v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx477_ctrl_ops, +- V4L2_CID_LINK_FREQ, +- ARRAY_SIZE(imx477_link_freq_menu) - 1, 0, +- imx477_link_freq_menu); ++ V4L2_CID_LINK_FREQ, 1, 0, ++ &link_freqs[imx477->link_freq_idx]); + if (imx477->link_freq) + imx477->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + +@@ -2110,13 +2150,14 @@ static void imx477_free_controls(struct + mutex_destroy(&imx477->mutex); + } + +-static int imx477_check_hwcfg(struct device *dev) ++static int imx477_check_hwcfg(struct device *dev, struct imx477 *imx477) + { + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret = -EINVAL; ++ int i; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { +@@ -2141,11 +2182,18 @@ static int imx477_check_hwcfg(struct dev + goto error_out; + } + +- if (ep_cfg.nr_of_link_frequencies != 1 || +- ep_cfg.link_frequencies[0] != IMX477_DEFAULT_LINK_FREQ) { ++ for (i = 0; i < ARRAY_SIZE(link_freqs); i++) { ++ if (link_freqs[i] == ep_cfg.link_frequencies[0]) { ++ imx477->link_freq_idx = i; ++ break; ++ } ++ } ++ ++ if (i == ARRAY_SIZE(link_freqs)) { + dev_err(dev, "Link frequency not supported: %lld\n", + ep_cfg.link_frequencies[0]); +- goto error_out; ++ ret = -EINVAL; ++ goto error_out; + } + + ret = 0; +@@ -2206,7 +2254,7 @@ static int imx477_probe(struct i2c_clien + (const struct imx477_compatible_data *)match->data; + + /* Check the hardware configuration in device tree */ +- if (imx477_check_hwcfg(dev)) ++ if (imx477_check_hwcfg(dev, imx477)) + return -EINVAL; + + /* Default the trigger mode from OF to -1, which means invalid */ diff --git a/target/linux/bcm27xx/patches-6.6/950-1380-dtoverlays-Add-link-frequency-override-to-imx477-378.patch b/target/linux/bcm27xx/patches-6.6/950-1380-dtoverlays-Add-link-frequency-override-to-imx477-378.patch new file mode 100644 index 0000000000..47d3d39200 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1380-dtoverlays-Add-link-frequency-override-to-imx477-378.patch @@ -0,0 +1,43 @@ +From 7e253a062d5a14de13ccfb410570975099c238be Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Thu, 14 Nov 2024 13:15:24 +0000 +Subject: [PATCH] dtoverlays: Add link-frequency override to imx477/378 overlay + +Copy of the imx708 change. + +Signed-off-by: Dave Stevenson +--- + arch/arm/boot/dts/overlays/README | 4 ++++ + arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi | 1 + + 2 files changed, 5 insertions(+) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -2780,6 +2780,8 @@ Params: rotation Mounting + camera clamping I/Os such as XVS to 0V. + sync-source Configure as vsync source + sync-sink Configure as vsync sink ++ link-frequency Allowable link frequency values to use in Hz: ++ 450000000 (default), 453000000, 456000000. + + + Name: imx462 +@@ -2822,6 +2824,8 @@ Params: rotation Mounting + camera clamping I/Os such as XVS to 0V. + sync-source Configure as vsync source + sync-sink Configure as vsync sink ++ link-frequency Allowable link frequency values to use in Hz: ++ 450000000 (default), 453000000, 456000000. + + + Name: imx500 +--- a/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi ++++ b/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi +@@ -80,6 +80,7 @@ + <&cam_node>, "clocks:0=",<&cam0_clk>, + <&cam_node>, "VANA-supply:0=",<&cam0_reg>; + always-on = <0>, "+99"; ++ link-frequency = <&cam_endpoint>,"link-frequencies#0"; + }; + }; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1381-dmaengine-dw-axi-dmac-Only-start-idle-channels.patch b/target/linux/bcm27xx/patches-6.6/950-1381-dmaengine-dw-axi-dmac-Only-start-idle-channels.patch new file mode 100644 index 0000000000..81b7f805f3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1381-dmaengine-dw-axi-dmac-Only-start-idle-channels.patch @@ -0,0 +1,29 @@ +From 59a8855b51c1d8acf37d3c80f34782d71f474617 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 13 Nov 2024 10:37:22 +0000 +Subject: [PATCH] dmaengine: dw-axi-dmac: Only start idle channels + +Attempting to start a non-idle channel causes an error message to be +logged, and is inefficient. Test for emptiness of the desc_issued list +before doing so. + +Signed-off-by: Phil Elwell +--- + drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c ++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +@@ -536,9 +536,11 @@ static void dma_chan_issue_pending(struc + { + struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); + unsigned long flags; ++ bool was_empty; + + spin_lock_irqsave(&chan->vc.lock, flags); +- if (vchan_issue_pending(&chan->vc)) ++ was_empty = list_empty(&chan->vc.desc_issued); ++ if (vchan_issue_pending(&chan->vc) && was_empty) + axi_chan_start_first_queued(chan); + spin_unlock_irqrestore(&chan->vc.lock, flags); + } diff --git a/target/linux/bcm27xx/patches-6.6/950-1382-mailbox-Add-RP1-mailbox-support.patch b/target/linux/bcm27xx/patches-6.6/950-1382-mailbox-Add-RP1-mailbox-support.patch new file mode 100644 index 0000000000..23bdb6a8fb --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1382-mailbox-Add-RP1-mailbox-support.patch @@ -0,0 +1,252 @@ +From 0d58d8cfb6f989f290d983552fcaa116e582e84a Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 31 Oct 2024 17:33:38 +0000 +Subject: [PATCH] mailbox: Add RP1 mailbox support + +The Raspberry Pi RP1 includes 2 M3 cores running firmware. This driver +adds a mailbox communication channel to them via a doorbell and some +shared memory. + +Signed-off-by: Phil Elwell +--- + drivers/mailbox/Kconfig | 9 ++ + drivers/mailbox/Makefile | 2 + + drivers/mailbox/rp1-mailbox.c | 208 ++++++++++++++++++++++++++++++++++ + 3 files changed, 219 insertions(+) + create mode 100644 drivers/mailbox/rp1-mailbox.c + +--- a/drivers/mailbox/Kconfig ++++ b/drivers/mailbox/Kconfig +@@ -295,4 +295,13 @@ config QCOM_IPCC + acts as an interrupt controller for receiving interrupts from clients. + Say Y here if you want to build this driver. + ++config MBOX_RP1 ++ tristate "RP1 Mailbox" ++ depends on MFD_RP1 ++ help ++ An implementation of a mailbox interface to the Raspberry Pi RP1 I/O ++ interface. Although written as a mailbox driver, the hardware only ++ provides an array of 32 doorbells. ++ Say Y here if you want to use the RP1 Mailbox. ++ + endif +--- a/drivers/mailbox/Makefile ++++ b/drivers/mailbox/Makefile +@@ -62,3 +62,5 @@ obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox + obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o + + obj-$(CONFIG_APPLE_MAILBOX) += apple-mailbox.o ++ ++obj-$(CONFIG_MBOX_RP1) += rp1-mailbox.o +--- /dev/null ++++ b/drivers/mailbox/rp1-mailbox.c +@@ -0,0 +1,208 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2023 Raspberry Pi Ltd. ++ * ++ * Parts of this driver are based on: ++ * - bcm2835-mailbox.c ++ * Copyright (C) 2010,2015 Broadcom ++ * Copyright (C) 2013-2014 Lubomir Rintel ++ * Copyright (C) 2013 Craig McGeachie ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * RP1's PROC_EVENTS register can generate interrupts on the M3 cores (when ++ * enabled). The 32-bit register is treated as 32 events, all of which share a ++ * common interrupt. HOST_EVENTS is the same in the reverse direction. ++ */ ++#define SYSCFG_PROC_EVENTS 0x00000008 ++#define SYSCFG_HOST_EVENTS 0x0000000c ++#define SYSCFG_HOST_EVENT_IRQ_EN 0x00000010 ++#define SYSCFG_HOST_EVENT_IRQ 0x00000014 ++ ++#define HW_SET_BITS 0x00002000 ++#define HW_CLR_BITS 0x00003000 ++ ++#define MAX_CHANS 4 /* 32 is the hardware limit */ ++ ++struct rp1_mbox { ++ void __iomem *regs; ++ unsigned int irq; ++ struct mbox_controller controller; ++}; ++ ++static struct rp1_mbox *rp1_chan_mbox(struct mbox_chan *chan) ++{ ++ return container_of(chan->mbox, struct rp1_mbox, controller); ++} ++ ++static unsigned int rp1_chan_event(struct mbox_chan *chan) ++{ ++ return (unsigned int)(uintptr_t)chan->con_priv; ++} ++ ++static irqreturn_t rp1_mbox_irq(int irq, void *dev_id) ++{ ++ struct rp1_mbox *mbox = dev_id; ++ struct mbox_chan *chan; ++ unsigned int doorbell; ++ unsigned int evs; ++ ++ evs = readl(mbox->regs + SYSCFG_HOST_EVENT_IRQ); ++ writel(evs, mbox->regs + SYSCFG_HOST_EVENTS + HW_CLR_BITS); ++ ++ while (evs) { ++ doorbell = __ffs(evs); ++ chan = &mbox->controller.chans[doorbell]; ++ mbox_chan_received_data(chan, NULL); ++ evs &= ~(1 << doorbell); ++ } ++ return IRQ_HANDLED; ++} ++ ++static int rp1_send_data(struct mbox_chan *chan, void *data) ++{ ++ struct rp1_mbox *mbox = rp1_chan_mbox(chan); ++ unsigned int event = rp1_chan_event(chan); ++ ++ writel(event, mbox->regs + SYSCFG_PROC_EVENTS + HW_SET_BITS); ++ ++ return 0; ++} ++ ++static int rp1_startup(struct mbox_chan *chan) ++{ ++ struct rp1_mbox *mbox = rp1_chan_mbox(chan); ++ unsigned int event = rp1_chan_event(chan); ++ ++ writel(event, mbox->regs + SYSCFG_HOST_EVENT_IRQ_EN + HW_SET_BITS); ++ ++ return 0; ++} ++ ++static void rp1_shutdown(struct mbox_chan *chan) ++{ ++ struct rp1_mbox *mbox = rp1_chan_mbox(chan); ++ unsigned int event = rp1_chan_event(chan); ++ ++ writel(event, mbox->regs + SYSCFG_HOST_EVENT_IRQ_EN + HW_CLR_BITS); ++} ++ ++static bool rp1_last_tx_done(struct mbox_chan *chan) ++{ ++ struct rp1_mbox *mbox = rp1_chan_mbox(chan); ++ unsigned int event = rp1_chan_event(chan); ++ unsigned int evs; ++ ++ evs = readl(mbox->regs + SYSCFG_HOST_EVENT_IRQ); ++ ++ return !(evs & event); ++} ++ ++static const struct mbox_chan_ops rp1_mbox_chan_ops = { ++ .send_data = rp1_send_data, ++ .startup = rp1_startup, ++ .shutdown = rp1_shutdown, ++ .last_tx_done = rp1_last_tx_done ++}; ++ ++static struct mbox_chan *rp1_mbox_xlate(struct mbox_controller *mbox, ++ const struct of_phandle_args *spec) ++{ ++ struct mbox_chan *chan; ++ unsigned int doorbell; ++ ++ if (spec->args_count != 1) ++ return ERR_PTR(-EINVAL); ++ ++ doorbell = spec->args[0]; ++ if (doorbell >= MAX_CHANS) ++ return ERR_PTR(-EINVAL); ++ ++ chan = &mbox->chans[doorbell]; ++ if (chan->con_priv) ++ return ERR_PTR(-EBUSY); ++ ++ chan->con_priv = (void *)(uintptr_t)(1 << doorbell); ++ ++ return chan; ++} ++ ++static int rp1_mbox_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mbox_chan *chans; ++ struct rp1_mbox *mbox; ++ int ret = 0; ++ ++ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); ++ if (mbox == NULL) ++ return -ENOMEM; ++ ++ ret = devm_request_irq(dev, platform_get_irq(pdev, 0), ++ rp1_mbox_irq, 0, dev_name(dev), mbox); ++ if (ret) { ++ dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n", ++ ret); ++ return -ENODEV; ++ } ++ ++ mbox->regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(mbox->regs)) { ++ ret = PTR_ERR(mbox->regs); ++ return ret; ++ } ++ ++ chans = devm_kcalloc(dev, MAX_CHANS, sizeof(*chans), GFP_KERNEL); ++ if (!chans) ++ return -ENOMEM; ++ ++ mbox->controller.txdone_poll = true; ++ mbox->controller.txpoll_period = 5; ++ mbox->controller.ops = &rp1_mbox_chan_ops; ++ mbox->controller.of_xlate = &rp1_mbox_xlate; ++ mbox->controller.dev = dev; ++ mbox->controller.num_chans = MAX_CHANS; ++ mbox->controller.chans = chans; ++ ++ ret = devm_mbox_controller_register(dev, &mbox->controller); ++ if (ret) ++ return ret; ++ ++ platform_set_drvdata(pdev, mbox); ++ ++ return 0; ++} ++ ++static const struct of_device_id rp1_mbox_of_match[] = { ++ { .compatible = "raspberrypi,rp1-mbox", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rp1_mbox_of_match); ++ ++static struct platform_driver rp1_mbox_driver = { ++ .driver = { ++ .name = "rp1-mbox", ++ .of_match_table = rp1_mbox_of_match, ++ }, ++ .probe = rp1_mbox_probe, ++}; ++ ++module_platform_driver(rp1_mbox_driver); ++ ++MODULE_AUTHOR("Phil Elwell "); ++MODULE_DESCRIPTION("RP1 mailbox IPC driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/bcm27xx/patches-6.6/950-1383-firmware-Add-an-RP1-firmware-interface-over-mbox.patch b/target/linux/bcm27xx/patches-6.6/950-1383-firmware-Add-an-RP1-firmware-interface-over-mbox.patch new file mode 100644 index 0000000000..3a9eb03c94 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1383-firmware-Add-an-RP1-firmware-interface-over-mbox.patch @@ -0,0 +1,421 @@ +From 67daeadcaa7cee1f4b9df7aa108d199e73f35451 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 31 Oct 2024 17:36:54 +0000 +Subject: [PATCH] firmware: Add an RP1 firmware interface over mbox + +The RP1 firmware runs a simple communications channel over some shared +memory and a mailbox. This driver provides access to that channel. + +Signed-off-by: Phil Elwell +--- + drivers/firmware/Kconfig | 9 + + drivers/firmware/Makefile | 1 + + drivers/firmware/rp1.c | 316 +++++++++++++++++++++++++++++++++++ + include/linux/rp1-firmware.h | 53 ++++++ + 4 files changed, 379 insertions(+) + create mode 100644 drivers/firmware/rp1.c + create mode 100644 include/linux/rp1-firmware.h + +--- a/drivers/firmware/Kconfig ++++ b/drivers/firmware/Kconfig +@@ -153,6 +153,15 @@ config RASPBERRYPI_FIRMWARE + This option enables support for communicating with the firmware on the + Raspberry Pi. + ++config FIRMWARE_RP1 ++ tristate "RP1 Firmware Driver" ++ depends on MBOX_RP1 ++ help ++ The Raspberry Pi RP1 processor presents a firmware ++ interface using shared memory and a mailbox. To enable ++ the driver that communicates with it, say Y. Otherwise, ++ say N. ++ + config FW_CFG_SYSFS + tristate "QEMU fw_cfg device support in sysfs" + depends on SYSFS && (ARM || ARM64 || PARISC || PPC_PMAC || SPARC || X86) +--- a/drivers/firmware/Makefile ++++ b/drivers/firmware/Makefile +@@ -17,6 +17,7 @@ obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o + obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o + obj-$(CONFIG_MTK_ADSP_IPC) += mtk-adsp-ipc.o + obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o ++obj-$(CONFIG_FIRMWARE_RP1) += rp1.o + obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o + obj-$(CONFIG_QCOM_SCM) += qcom-scm.o + qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o +--- /dev/null ++++ b/drivers/firmware/rp1.c +@@ -0,0 +1,316 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2023-24 Raspberry Pi Ltd. ++ * ++ * Parts of this driver are based on: ++ * - raspberrypi.c, by Eric Anholt ++ * Copyright (C) 2015 Broadcom ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define RP1_MAILBOX_FIRMWARE 0 ++ ++enum rp1_firmware_ops { ++ MBOX_SUCCESS = 0x0000, ++ GET_FIRMWARE_VERSION = 0x0001, // na -> 160-bit version ++ GET_FEATURE = 0x0002, // FOURCC -> op base (0 == unsupported), op count ++ ++ COMMON_COUNT ++}; ++ ++struct rp1_firmware { ++ struct mbox_client cl; ++ struct mbox_chan *chan; /* The doorbell channel */ ++ uint32_t __iomem *buf; /* The shared buffer */ ++ u32 buf_size; /* The size of the shared buffer */ ++ struct completion c; ++ ++ struct kref consumers; ++}; ++ ++struct rp1_get_feature_resp { ++ uint32_t op_base; ++ uint32_t op_count; ++}; ++ ++static DEFINE_MUTEX(transaction_lock); ++ ++static const struct of_device_id rp1_firmware_of_match[] = { ++ { .compatible = "raspberrypi,rp1-firmware", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rp1_firmware_of_match); ++ ++static void response_callback(struct mbox_client *cl, void *msg) ++{ ++ struct rp1_firmware *fw = container_of(cl, struct rp1_firmware, cl); ++ ++ complete(&fw->c); ++} ++ ++/* ++ * Sends a request to the RP1 firmware and synchronously waits for the reply. ++ * Returns zero or a positive count of response bytes on success, negative on ++ * error. ++ */ ++ ++int rp1_firmware_message(struct rp1_firmware *fw, uint16_t op, ++ const void *data, unsigned int data_len, ++ void *resp, unsigned int resp_space) ++{ ++ int ret; ++ u32 rc; ++ ++ if (data_len + 4 > fw->buf_size) ++ return -EINVAL; ++ ++ mutex_lock(&transaction_lock); ++ ++ memcpy_toio(&fw->buf[1], data, data_len); ++ writel((op << 16) | data_len, fw->buf); ++ ++ reinit_completion(&fw->c); ++ ret = mbox_send_message(fw->chan, NULL); ++ if (ret >= 0) { ++ if (wait_for_completion_timeout(&fw->c, HZ)) ++ ret = 0; ++ else ++ ret = -ETIMEDOUT; ++ } else { ++ dev_err(fw->cl.dev, "mbox_send_message returned %d\n", ret); ++ } ++ ++ if (ret == 0) { ++ rc = readl(fw->buf); ++ if (rc & 0x80000000) { ++ ret = (int32_t)rc; ++ } else { ++ ret = min(rc, resp_space); ++ memcpy_fromio(resp, &fw->buf[1], ret); ++ } ++ } ++ ++ mutex_unlock(&transaction_lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(rp1_firmware_message); ++ ++static void rp1_firmware_delete(struct kref *kref) ++{ ++ struct rp1_firmware *fw = container_of(kref, struct rp1_firmware, consumers); ++ ++ mbox_free_channel(fw->chan); ++ kfree(fw); ++} ++ ++void rp1_firmware_put(struct rp1_firmware *fw) ++{ ++ kref_put(&fw->consumers, rp1_firmware_delete); ++} ++EXPORT_SYMBOL_GPL(rp1_firmware_put); ++ ++int rp1_firmware_get_feature(struct rp1_firmware *fw, uint32_t fourcc, ++ uint32_t *op_base, uint32_t *op_count) ++{ ++ struct rp1_get_feature_resp resp; ++ int ret; ++ ++ memset(&resp, 0, sizeof(resp)); ++ ret = rp1_firmware_message(fw, GET_FEATURE, ++ &fourcc, sizeof(fourcc), ++ &resp, sizeof(resp)); ++ *op_base = resp.op_base; ++ *op_count = resp.op_count; ++ if (ret < 0) ++ return ret; ++ if (ret < sizeof(resp) || !resp.op_base) ++ return -EOPNOTSUPP; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(rp1_firmware_get_feature); ++ ++static void devm_rp1_firmware_put(void *data) ++{ ++ struct rp1_firmware *fw = data; ++ ++ rp1_firmware_put(fw); ++} ++ ++/** ++ * rp1_firmware_get - Get pointer to rp1_firmware structure. ++ * ++ * The reference to rp1_firmware has to be released with rp1_firmware_put(). ++ * ++ * Returns an error pointer on failure. ++ */ ++struct rp1_firmware *rp1_firmware_get(struct device_node *client) ++{ ++ const char *match = rp1_firmware_of_match[0].compatible; ++ struct platform_device *pdev; ++ struct device_node *fwnode; ++ struct rp1_firmware *fw; ++ ++ if (client) { ++ fwnode = of_parse_phandle(client, "firmware", 0); ++ if (!fwnode) ++ fwnode = of_get_parent(client); ++ if (fwnode && !of_device_is_compatible(fwnode, match)) { ++ of_node_put(fwnode); ++ fwnode = NULL; ++ } ++ } ++ ++ if (!fwnode) ++ fwnode = of_find_matching_node(NULL, rp1_firmware_of_match); ++ ++ if (!fwnode) ++ return ERR_PTR(-ENOENT); ++ ++ pdev = of_find_device_by_node(fwnode); ++ of_node_put(fwnode); ++ ++ if (!pdev) ++ return ERR_PTR(-EPROBE_DEFER); ++ ++ fw = platform_get_drvdata(pdev); ++ if (!fw) ++ goto err_defer; ++ ++ if (!kref_get_unless_zero(&fw->consumers)) ++ goto err_defer; ++ ++ put_device(&pdev->dev); ++ ++ return fw; ++ ++err_defer: ++ put_device(&pdev->dev); ++ return ERR_PTR(-EPROBE_DEFER); ++} ++EXPORT_SYMBOL_GPL(rp1_firmware_get); ++ ++/** ++ * devm_rp1_firmware_get - Get pointer to rp1_firmware structure. ++ * @firmware_node: Pointer to the firmware Device Tree node. ++ * ++ * Returns NULL is the firmware device is not ready. ++ */ ++struct rp1_firmware *devm_rp1_firmware_get(struct device *dev, struct device_node *client) ++{ ++ struct rp1_firmware *fw; ++ int ret; ++ ++ fw = rp1_firmware_get(client); ++ if (IS_ERR(fw)) ++ return fw; ++ ++ ret = devm_add_action_or_reset(dev, devm_rp1_firmware_put, fw); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ return fw; ++} ++EXPORT_SYMBOL_GPL(devm_rp1_firmware_get); ++ ++static int rp1_firmware_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *shmem; ++ struct rp1_firmware *fw; ++ struct resource res; ++ uint32_t version[5]; ++ int ret; ++ ++ shmem = of_parse_phandle(dev->of_node, "shmem", 0); ++ if (!of_device_is_compatible(shmem, "raspberrypi,rp1-shmem")) { ++ of_node_put(shmem); ++ return -ENXIO; ++ } ++ ++ ret = of_address_to_resource(shmem, 0, &res); ++ of_node_put(shmem); ++ if (ret) { ++ dev_err(dev, "failed to get shared memory (%pOF) - %d\n", shmem, ret); ++ return ret; ++ } ++ ++ /* ++ * Memory will be freed by rp1_firmware_delete() once all users have ++ * released their firmware handles. Don't use devm_kzalloc() here. ++ */ ++ fw = kzalloc(sizeof(*fw), GFP_KERNEL); ++ if (!fw) ++ return -ENOMEM; ++ ++ fw->buf_size = resource_size(&res); ++ fw->buf = devm_ioremap(dev, res.start, fw->buf_size); ++ if (!fw->buf) { ++ dev_err(dev, "failed to ioremap shared memory\n"); ++ kfree(fw); ++ return -EADDRNOTAVAIL; ++ } ++ ++ fw->cl.dev = dev; ++ fw->cl.rx_callback = response_callback; ++ fw->cl.tx_block = false; ++ ++ fw->chan = mbox_request_channel(&fw->cl, RP1_MAILBOX_FIRMWARE); ++ if (IS_ERR(fw->chan)) { ++ int ret = PTR_ERR(fw->chan); ++ ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "Failed to get mbox channel: %d\n", ret); ++ kfree(fw); ++ return ret; ++ } ++ ++ init_completion(&fw->c); ++ kref_init(&fw->consumers); ++ ++ platform_set_drvdata(pdev, fw); ++ ++ ret = rp1_firmware_message(fw, GET_FIRMWARE_VERSION, ++ NULL, 0, &version, sizeof(version)); ++ if (ret == sizeof(version)) { ++ dev_info(dev, "RP1 Firmware version %08x%08x%08x%08x%08x\n", ++ version[0], version[1], version[2], version[3], version[4]); ++ ret = 0; ++ } else if (ret >= 0) { ++ ret = -EIO; ++ } ++ ++ return ret; ++} ++ ++static int rp1_firmware_remove(struct platform_device *pdev) ++{ ++ struct rp1_firmware *fw = platform_get_drvdata(pdev); ++ ++ rp1_firmware_put(fw); ++ ++ return 0; ++} ++ ++static struct platform_driver rp1_firmware_driver = { ++ .driver = { ++ .name = "rp1-firmware", ++ .of_match_table = rp1_firmware_of_match, ++ }, ++ .probe = rp1_firmware_probe, ++ .remove = rp1_firmware_remove, ++}; ++ ++module_platform_driver(rp1_firmware_driver); ++ ++MODULE_AUTHOR("Phil Elwell "); ++MODULE_DESCRIPTION("RP1 firmware driver"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/include/linux/rp1-firmware.h +@@ -0,0 +1,53 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2023 2023-2024 Raspberry Pi Ltd. ++ */ ++ ++#ifndef __SOC_RP1_FIRMWARE_H__ ++#define __SOC_RP1_FIRMWARE_H__ ++ ++#include ++#include ++ ++#define RP1_FOURCC(s) ((uint32_t)((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | (s[3] << 0))) ++ ++struct rp1_firmware; ++ ++#if IS_ENABLED(CONFIG_FIRMWARE_RP1) ++int rp1_firmware_message(struct rp1_firmware *fw, uint16_t op, ++ const void *data, unsigned int data_len, ++ void *resp, unsigned int resp_space); ++void rp1_firmware_put(struct rp1_firmware *fw); ++struct rp1_firmware *rp1_firmware_get(struct device_node *fwnode); ++struct rp1_firmware *devm_rp1_firmware_get(struct device *dev, struct device_node *fwnode); ++int rp1_firmware_get_feature(struct rp1_firmware *fw, uint32_t fourcc, ++ uint32_t *op_base, uint32_t *op_count); ++#else ++static inline int rp1_firmware_message(struct rp1_firmware *fw, uint16_t op, ++ const void *data, unsigned int data_len, ++ void *resp, unsigned int resp_space) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline void rp1_firmware_put(struct rp1_firmware *fw) { } ++ ++static inline struct rp1_firmware *rp1_firmware_get(struct device_node *fwnode) ++{ ++ return NULL; ++} ++ ++static inline struct rp1_firmware *devm_rp1_firmware_get(struct device *dev, ++ struct device_node *fwnode) ++{ ++ return NULL; ++} ++ ++static inline int rp1_firmware_get_feature(struct rp1_firmware *fw, uint32_t fourcc, ++ uint32_t *op_base, uint32_t *op_count) ++{ ++ return -EOPNOTSUPP; ++} ++#endif ++ ++#endif /* __SOC_RP1_FIRMWARE_H__ */ diff --git a/target/linux/bcm27xx/patches-6.6/950-1384-misc-Add-RP1-PIO-driver.patch b/target/linux/bcm27xx/patches-6.6/950-1384-misc-Add-RP1-PIO-driver.patch new file mode 100644 index 0000000000..521129b919 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1384-misc-Add-RP1-PIO-driver.patch @@ -0,0 +1,1385 @@ +From 55fd5c9018e1520d45f08cf08630a493ec7dedea Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 31 Oct 2024 18:26:00 +0000 +Subject: [PATCH] misc: Add RP1 PIO driver + +Provide remote access to the PIO hardware in RP1. There is a single +instance, with 4 state machines. + +Signed-off-by: Phil Elwell +--- + drivers/misc/Kconfig | 8 + + drivers/misc/Makefile | 1 + + drivers/misc/rp1-fw-pio.h | 53 ++ + drivers/misc/rp1-pio.c | 1064 ++++++++++++++++++++++++++++++++ + include/uapi/misc/rp1_pio_if.h | 212 +++++++ + 5 files changed, 1338 insertions(+) + create mode 100644 drivers/misc/rp1-fw-pio.h + create mode 100644 drivers/misc/rp1-pio.c + create mode 100644 include/uapi/misc/rp1_pio_if.h + +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -17,6 +17,14 @@ config BCM2835_SMI + Driver for enabling and using Broadcom's Secondary/Slow Memory Interface. + Appears as /dev/bcm2835_smi. For ioctl interface see drivers/misc/bcm2835_smi.h + ++config RP1_PIO ++ tristate "Raspberry Pi RP1 PIO driver" ++ select FIRMWARE_RP1 ++ default n ++ help ++ Driver providing control of the Raspberry Pi PIO block, as found in ++ RP1. ++ + config AD525X_DPOT + tristate "Analog Devices Digital Potentiometers" + depends on (I2C || SPI) && SYSFS +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -18,6 +18,7 @@ obj-$(CONFIG_TIFM_7XX1) += tifm_7 + obj-$(CONFIG_PHANTOM) += phantom.o + obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o + obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o ++obj-$(CONFIG_RP1_PIO) += rp1-pio.o + obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o + obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o + obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o +--- /dev/null ++++ b/drivers/misc/rp1-fw-pio.h +@@ -0,0 +1,53 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2023 2023-2024 Raspberry Pi Ltd. ++ */ ++ ++#ifndef __SOC_RP1_FIRMWARE_OPS_H__ ++#define __SOC_RP1_FIRMWARE_OPS_H__ ++ ++#include ++ ++#define FOURCC_PIO RP1_FOURCC("PIO ") ++ ++enum rp1_pio_ops { ++ PIO_CAN_ADD_PROGRAM, // u16 num_instrs, u16 origin -> origin ++ PIO_ADD_PROGRAM, // u16 num_instrs, u16 origin, u16 prog[] -> rc ++ PIO_REMOVE_PROGRAM, // u16 num_instrs, u16 origin ++ PIO_CLEAR_INSTR_MEM, // - ++ ++ PIO_SM_CLAIM, // u16 mask -> sm ++ PIO_SM_UNCLAIM, // u16 mask ++ PIO_SM_IS_CLAIMED, // u16 mask -> claimed ++ ++ PIO_SM_INIT, // u16 sm, u16 initial_pc, u32 sm_config[4] ++ PIO_SM_SET_CONFIG, // u16 sm, u16 rsvd, u32 sm_config[4] ++ PIO_SM_EXEC, // u16 sm, u16 instr, u8 blocking, u8 rsvd ++ PIO_SM_CLEAR_FIFOS, // u16 sm ++ PIO_SM_SET_CLKDIV, // u16 sm, u16 div_int, u8 div_frac, u8 rsvd ++ PIO_SM_SET_PINS, // u16 sm, u16 rsvd, u32 values, u32 mask ++ PIO_SM_SET_PINDIRS, // u16 sm, u16 rsvd, u32 dirs, u32 mask ++ PIO_SM_SET_ENABLED, // u16 mask, u8 enable, u8 rsvd ++ PIO_SM_RESTART, // u16 mask ++ PIO_SM_CLKDIV_RESTART, // u16 mask ++ PIO_SM_ENABLE_SYNC, // u16 mask ++ PIO_SM_PUT, // u16 sm, u8 blocking, u8 rsvd, u32 data ++ PIO_SM_GET, // u16 sm, u8 blocking, u8 rsvd -> u32 data ++ PIO_SM_SET_DMACTRL, // u16 sm, u16 is_tx, u32 ctrl ++ ++ GPIO_INIT, // u16 gpio ++ GPIO_SET_FUNCTION, // u16 gpio, u16 fn ++ GPIO_SET_PULLS, // u16 gpio, u8 up, u8 down ++ GPIO_SET_OUTOVER, // u16 gpio, u16 value ++ GPIO_SET_INOVER, // u16 gpio, u16 value ++ GPIO_SET_OEOVER, // u16 gpio, u16 value ++ GPIO_SET_INPUT_ENABLED, // u16 gpio, u16 value ++ GPIO_SET_DRIVE_STRENGTH, // u16 gpio, u16 value ++ ++ READ_HW, // src address, len -> data bytes ++ WRITE_HW, // dst address, data ++ ++ PIO_COUNT ++}; ++ ++#endif +--- /dev/null ++++ b/drivers/misc/rp1-pio.c +@@ -0,0 +1,1064 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// PIO driver for RP1 ++// ++// Copyright (C) 2023-2024 Raspberry Pi Ltd. ++// ++// Parts of this driver are based on: ++// - vcio.c, by Noralf Trønnes ++// Copyright (C) 2010 Broadcom ++// Copyright (C) 2015 Noralf Trønnes ++// Copyright (C) 2021 Raspberry Pi (Trading) Ltd. ++// - bcm2835_smi.c & bcm2835_smi_dev.c by Luke Wren ++// Copyright (c) 2015 Raspberry Pi (Trading) Ltd. ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "rp1-fw-pio.h" ++ ++#define DRIVER_NAME "rp1-pio" ++ ++#define RP1_PIO_SMS_COUNT 4 ++#define RP1_PIO_INSTR_COUNT 32 ++ ++#define MAX_ARG_SIZE 256 ++ ++#define RP1_PIO_FIFO_TX0 0x00 ++#define RP1_PIO_FIFO_TX1 0x04 ++#define RP1_PIO_FIFO_TX2 0x08 ++#define RP1_PIO_FIFO_TX3 0x0c ++#define RP1_PIO_FIFO_RX0 0x10 ++#define RP1_PIO_FIFO_RX1 0x14 ++#define RP1_PIO_FIFO_RX2 0x18 ++#define RP1_PIO_FIFO_RX3 0x1c ++ ++#define RP1_PIO_DMACTRL_DEFAULT 0x80000104 ++ ++#define HANDLER(_n, _f) \ ++ [_IOC_NR(PIO_IOC_ ## _n)] = { #_n, rp1_pio_ ## _f, _IOC_SIZE(PIO_IOC_ ## _n) } ++ ++ ++#define ROUND_UP(x, y) (((x) + (y) - 1) - (((x) + (y) - 1) % (y))) ++ ++#define DMA_BOUNCE_BUFFER_SIZE 0x1000 ++#define DMA_BOUNCE_BUFFER_COUNT 4 ++ ++struct dma_buf_info { ++ void *buf; ++ dma_addr_t phys; ++ struct scatterlist sgl; ++}; ++ ++struct dma_info { ++ struct semaphore buf_sem; ++ struct dma_chan *chan; ++ size_t buf_size; ++ size_t buf_count; ++ unsigned int head_idx; ++ unsigned int tail_idx; ++ struct dma_buf_info bufs[DMA_BOUNCE_BUFFER_COUNT]; ++}; ++ ++struct rp1_pio_device { ++ struct platform_device *pdev; ++ struct rp1_firmware *fw; ++ uint16_t fw_pio_base; ++ uint16_t fw_pio_count; ++ dev_t dev_num; ++ struct class *dev_class; ++ struct cdev cdev; ++ phys_addr_t phys_addr; ++ uint32_t claimed_sms; ++ uint32_t claimed_dmas; ++ spinlock_t lock; ++ struct mutex instr_mutex; ++ struct dma_info dma_configs[RP1_PIO_SMS_COUNT][RP1_PIO_DIR_COUNT]; ++ uint32_t used_instrs; ++ uint8_t instr_refcounts[RP1_PIO_INSTR_COUNT]; ++ uint16_t instrs[RP1_PIO_INSTR_COUNT]; ++ uint client_count; ++}; ++ ++struct rp1_pio_client { ++ struct rp1_pio_device *pio; ++ uint32_t claimed_sms; ++ uint32_t claimed_instrs; ++ uint32_t claimed_dmas; ++}; ++ ++static struct rp1_pio_device *g_pio; ++ ++static int rp1_pio_message(struct rp1_pio_device *pio, ++ uint16_t op, const void *data, unsigned int data_len) ++{ ++ uint32_t rc; ++ int ret; ++ ++ if (op >= pio->fw_pio_count) ++ return -EOPNOTSUPP; ++ ret = rp1_firmware_message(pio->fw, pio->fw_pio_base + op, ++ data, data_len, ++ &rc, sizeof(rc)); ++ if (ret == 4) ++ ret = rc; ++ return ret; ++} ++ ++static int rp1_pio_message_resp(struct rp1_pio_device *pio, ++ uint16_t op, const void *data, unsigned int data_len, ++ void *resp, void __user *userbuf, unsigned int resp_len) ++{ ++ uint32_t resp_buf[1 + 32]; ++ int ret; ++ ++ if (op >= pio->fw_pio_count) ++ return -EOPNOTSUPP; ++ if (resp_len + 4 >= sizeof(resp_buf)) ++ return -EINVAL; ++ if (!resp && !userbuf) ++ return -EINVAL; ++ ret = rp1_firmware_message(pio->fw, pio->fw_pio_base + op, ++ data, data_len, ++ resp_buf, resp_len + 4); ++ if (ret >= 4 && !resp_buf[0]) { ++ ret -= 4; ++ if (resp) ++ memcpy(resp, &resp_buf[1], ret); ++ else if (copy_to_user(userbuf, &resp_buf[1], ret)) ++ ret = -EFAULT; ++ } else if (ret >= 0) { ++ ret = -EIO; ++ } ++ return ret; ++} ++ ++static int rp1_pio_read_hw(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_device *pio = client->pio; ++ struct rp1_access_hw_args *args = param; ++ ++ return rp1_pio_message_resp(pio, READ_HW, ++ args, 8, NULL, args->data, args->len); ++} ++ ++static int rp1_pio_write_hw(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_device *pio = client->pio; ++ struct rp1_access_hw_args *args = param; ++ uint32_t write_buf[32 + 1]; ++ int len; ++ ++ len = min(args->len, sizeof(write_buf) - 4); ++ write_buf[0] = args->addr; ++ if (copy_from_user(&write_buf[1], args->data, len)) ++ return -EFAULT; ++ return rp1_firmware_message(pio->fw, pio->fw_pio_base + WRITE_HW, ++ write_buf, 4 + len, NULL, 0); ++} ++ ++static int rp1_pio_find_program(struct rp1_pio_device *pio, ++ struct rp1_pio_add_program_args *prog) ++{ ++ uint start, end, prog_size; ++ uint32_t used_mask; ++ uint i; ++ ++ start = (prog->origin != RP1_PIO_ORIGIN_ANY) ? prog->origin : 0; ++ end = (prog->origin != RP1_PIO_ORIGIN_ANY) ? prog->origin : ++ (RP1_PIO_INSTRUCTION_COUNT - prog->num_instrs); ++ prog_size = sizeof(prog->instrs[0]) * prog->num_instrs; ++ used_mask = (uint32_t)(~0) >> (32 - prog->num_instrs); ++ ++ /* Find the best match */ ++ for (i = start; i <= end; i++) { ++ uint32_t mask = used_mask << i; ++ ++ if ((pio->used_instrs & mask) != mask) ++ continue; ++ if (!memcmp(pio->instrs + i, prog->instrs, prog_size)) ++ return i; ++ } ++ ++ return -1; ++} ++ ++static int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_add_program_args *args = param; ++ struct rp1_pio_device *pio = client->pio; ++ int offset; ++ ++ if (args->num_instrs > RP1_PIO_INSTR_COUNT || ++ ((args->origin != RP1_PIO_ORIGIN_ANY) && ++ (args->origin >= RP1_PIO_INSTR_COUNT || ++ ((args->origin + args->num_instrs) > RP1_PIO_INSTR_COUNT)))) ++ return -EINVAL; ++ ++ mutex_lock(&pio->instr_mutex); ++ offset = rp1_pio_find_program(pio, args); ++ mutex_unlock(&pio->instr_mutex); ++ if (offset >= 0) ++ return offset; ++ ++ /* Don't send the instructions, just the header */ ++ return rp1_pio_message(pio, PIO_CAN_ADD_PROGRAM, args, ++ offsetof(struct rp1_pio_add_program_args, instrs)); ++} ++ ++static int rp1_pio_add_program(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_add_program_args *args = param; ++ struct rp1_pio_device *pio = client->pio; ++ int offset; ++ uint i; ++ ++ if (args->num_instrs > RP1_PIO_INSTR_COUNT || ++ ((args->origin != RP1_PIO_ORIGIN_ANY) && ++ (args->origin >= RP1_PIO_INSTR_COUNT || ++ ((args->origin + args->num_instrs) > RP1_PIO_INSTR_COUNT)))) ++ return -EINVAL; ++ ++ mutex_lock(&pio->instr_mutex); ++ offset = rp1_pio_find_program(pio, args); ++ if (offset < 0) ++ offset = rp1_pio_message(client->pio, PIO_ADD_PROGRAM, args, sizeof(*args)); ++ ++ if (offset >= 0) { ++ uint32_t used_mask; ++ uint prog_size; ++ ++ used_mask = ((uint32_t)(~0) >> (-args->num_instrs & 0x1f)) << offset; ++ prog_size = sizeof(args->instrs[0]) * args->num_instrs; ++ ++ if ((pio->used_instrs & used_mask) != used_mask) { ++ pio->used_instrs |= used_mask; ++ memcpy(pio->instrs + offset, args->instrs, prog_size); ++ } ++ client->claimed_instrs |= used_mask; ++ for (i = 0; i < args->num_instrs; i++) ++ pio->instr_refcounts[offset + i]++; ++ } ++ mutex_unlock(&pio->instr_mutex); ++ return offset; ++} ++ ++static void rp1_pio_remove_instrs(struct rp1_pio_device *pio, uint32_t mask) ++{ ++ struct rp1_pio_remove_program_args args; ++ uint i; ++ ++ mutex_lock(&pio->instr_mutex); ++ args.num_instrs = 0; ++ for (i = 0; ; i++, mask >>= 1) { ++ if ((mask & 1) && pio->instr_refcounts[i] && !--pio->instr_refcounts[i]) { ++ pio->used_instrs &= ~(1 << i); ++ args.num_instrs++; ++ } else if (args.num_instrs) { ++ args.origin = i - args.num_instrs; ++ rp1_pio_message(pio, PIO_REMOVE_PROGRAM, &args, sizeof(args)); ++ args.num_instrs = 0; ++ } ++ if (!mask) ++ break; ++ } ++ mutex_unlock(&pio->instr_mutex); ++} ++ ++static int rp1_pio_remove_program(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_remove_program_args *args = param; ++ uint32_t used_mask; ++ int ret = -ENOENT; ++ ++ if (args->num_instrs > RP1_PIO_INSTR_COUNT || ++ args->origin >= RP1_PIO_INSTR_COUNT || ++ (args->origin + args->num_instrs) > RP1_PIO_INSTR_COUNT) ++ return -EINVAL; ++ ++ used_mask = ((uint32_t)(~0) >> (32 - args->num_instrs)) << args->origin; ++ if ((client->claimed_instrs & used_mask) == used_mask) { ++ client->claimed_instrs &= ~used_mask; ++ rp1_pio_remove_instrs(client->pio, used_mask); ++ ret = 0; ++ } ++ return ret; ++} ++ ++static int rp1_pio_clear_instr_mem(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_device *pio = client->pio; ++ ++ mutex_lock(&pio->instr_mutex); ++ (void)rp1_pio_message(client->pio, PIO_CLEAR_INSTR_MEM, NULL, 0); ++ memset(pio->instr_refcounts, 0, sizeof(pio->instr_refcounts)); ++ pio->used_instrs = 0; ++ client->claimed_instrs = 0; ++ mutex_unlock(&pio->instr_mutex); ++ return 0; ++} ++ ++static int rp1_pio_sm_claim(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_claim_args *args = param; ++ struct rp1_pio_device *pio = client->pio; ++ int ret; ++ ++ mutex_lock(&pio->instr_mutex); ++ ret = rp1_pio_message(client->pio, PIO_SM_CLAIM, args, sizeof(*args)); ++ if (ret >= 0) { ++ if (args->mask) ++ client->claimed_sms |= args->mask; ++ else ++ client->claimed_sms |= (1 << ret); ++ pio->claimed_sms |= client->claimed_sms; ++ } ++ mutex_unlock(&pio->instr_mutex); ++ return ret; ++} ++ ++static int rp1_pio_sm_unclaim(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_claim_args *args = param; ++ struct rp1_pio_device *pio = client->pio; ++ ++ mutex_lock(&pio->instr_mutex); ++ (void)rp1_pio_message(client->pio, PIO_SM_UNCLAIM, args, sizeof(*args)); ++ client->claimed_sms &= ~args->mask; ++ pio->claimed_sms &= ~args->mask; ++ mutex_unlock(&pio->instr_mutex); ++ return 0; ++} ++ ++static int rp1_pio_sm_is_claimed(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_claim_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_IS_CLAIMED, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_init(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_init_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_INIT, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_set_config(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_config_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_CONFIG, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_exec(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_exec_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_EXEC, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_clear_fifos(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_clear_fifos_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_CLEAR_FIFOS, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_set_clkdiv(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_clkdiv_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_CLKDIV, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_set_pins(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_pins_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_PINS, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_set_pindirs(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_pindirs_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_PINDIRS, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_set_enabled(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_enabled_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_ENABLED, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_restart(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_restart_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_RESTART, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_clkdiv_restart(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_restart_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_CLKDIV_RESTART, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_enable_sync(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_enable_sync_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_ENABLE_SYNC, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_put(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_put_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_PUT, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_get(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_get_args *args = param; ++ int ret; ++ ++ ret = rp1_pio_message_resp(client->pio, PIO_SM_GET, args, sizeof(*args), ++ &args->data, NULL, sizeof(args->data)); ++ if (ret >= 0) ++ return offsetof(struct rp1_pio_sm_get_args, data) + ret; ++ return ret; ++} ++ ++static int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_dmactrl_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_DMACTRL, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_init_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_INIT, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_function_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_FUNCTION, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_pulls_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_PULLS, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_set_outover(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_OUTOVER, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_set_inover(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_INOVER, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_set_oeover(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_OEOVER, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_INPUT_ENABLED, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_DRIVE_STRENGTH, args, sizeof(*args)); ++} ++ ++static void rp1_pio_sm_dma_callback(void *param) ++{ ++ struct dma_info *dma = param; ++ ++ up(&dma->buf_sem); ++} ++ ++static void rp1_pio_sm_dma_free(struct device *dev, struct dma_info *dma) ++{ ++ dmaengine_terminate_all(dma->chan); ++ while (dma->buf_count > 0) { ++ dma->buf_count--; ++ dma_free_coherent(dev, ROUND_UP(dma->buf_size, PAGE_SIZE), ++ dma->bufs[dma->buf_count].buf, dma->bufs[dma->buf_count].phys); ++ } ++ ++ dma_release_channel(dma->chan); ++} ++ ++static int rp1_pio_sm_config_xfer(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_config_xfer_args *args = param; ++ struct rp1_pio_sm_set_dmactrl_args set_dmactrl_args; ++ struct rp1_pio_device *pio = client->pio; ++ struct platform_device *pdev = pio->pdev; ++ struct device *dev = &pdev->dev; ++ struct dma_slave_config config = {}; ++ phys_addr_t fifo_addr; ++ struct dma_info *dma; ++ uint32_t dma_mask; ++ char chan_name[4]; ++ uint buf_size; ++ int ret = 0; ++ ++ if (args->sm >= RP1_PIO_SMS_COUNT || args->dir >= RP1_PIO_DIR_COUNT || ++ !args->buf_size || (args->buf_size & 3) || ++ !args->buf_count || args->buf_count > DMA_BOUNCE_BUFFER_COUNT) ++ return -EINVAL; ++ ++ dma_mask = 1 << (args->sm * 2 + args->dir); ++ ++ dma = &pio->dma_configs[args->sm][args->dir]; ++ ++ spin_lock(&pio->lock); ++ if (pio->claimed_dmas & dma_mask) ++ rp1_pio_sm_dma_free(dev, dma); ++ pio->claimed_dmas |= dma_mask; ++ client->claimed_dmas |= dma_mask; ++ spin_unlock(&pio->lock); ++ ++ dma->buf_size = args->buf_size; ++ /* Round up the allocations */ ++ buf_size = ROUND_UP(args->buf_size, PAGE_SIZE); ++ sema_init(&dma->buf_sem, 0); ++ ++ /* Allocate and configure a DMA channel */ ++ /* Careful - each SM FIFO has its own DREQ value */ ++ chan_name[0] = (args->dir == RP1_PIO_DIR_TO_SM) ? 't' : 'r'; ++ chan_name[1] = 'x'; ++ chan_name[2] = '0' + args->sm; ++ chan_name[3] = '\0'; ++ ++ dma->chan = dma_request_chan(dev, chan_name); ++ if (IS_ERR(dma->chan)) ++ return PTR_ERR(dma->chan); ++ ++ /* Alloc and map bounce buffers */ ++ for (dma->buf_count = 0; dma->buf_count < args->buf_count; dma->buf_count++) { ++ struct dma_buf_info *dbi = &dma->bufs[dma->buf_count]; ++ ++ dbi->buf = dma_alloc_coherent(dma->chan->device->dev, buf_size, ++ &dbi->phys, GFP_KERNEL); ++ if (!dbi->buf) { ++ ret = -ENOMEM; ++ goto err_dma_free; ++ } ++ sg_init_table(&dbi->sgl, 1); ++ sg_dma_address(&dbi->sgl) = dbi->phys; ++ } ++ ++ fifo_addr = pio->phys_addr; ++ fifo_addr += args->sm * (RP1_PIO_FIFO_TX1 - RP1_PIO_FIFO_TX0); ++ fifo_addr += (args->dir == RP1_PIO_DIR_TO_SM) ? RP1_PIO_FIFO_TX0 : RP1_PIO_FIFO_RX0; ++ ++ config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ config.src_addr = fifo_addr; ++ config.dst_addr = fifo_addr; ++ config.direction = (args->dir == RP1_PIO_DIR_TO_SM) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; ++ ++ ret = dmaengine_slave_config(dma->chan, &config); ++ if (ret) ++ goto err_dma_free; ++ ++ set_dmactrl_args.sm = args->sm; ++ set_dmactrl_args.is_tx = (args->dir == RP1_PIO_DIR_TO_SM); ++ set_dmactrl_args.ctrl = RP1_PIO_DMACTRL_DEFAULT; ++ if (args->dir == RP1_PIO_DIR_FROM_SM) ++ set_dmactrl_args.ctrl = (RP1_PIO_DMACTRL_DEFAULT & ~0x1f) | 1; ++ ++ ret = rp1_pio_sm_set_dmactrl(client, &set_dmactrl_args); ++ if (ret) ++ goto err_dma_free; ++ ++ return 0; ++ ++err_dma_free: ++ rp1_pio_sm_dma_free(dev, dma); ++ ++ spin_lock(&pio->lock); ++ client->claimed_dmas &= ~dma_mask; ++ pio->claimed_dmas &= ~dma_mask; ++ spin_unlock(&pio->lock); ++ ++ return ret; ++} ++ ++static int rp1_pio_sm_tx_user(struct rp1_pio_device *pio, struct dma_info *dma, ++ const void __user *userbuf, size_t bytes) ++{ ++ struct platform_device *pdev = pio->pdev; ++ struct dma_async_tx_descriptor *desc; ++ struct device *dev = &pdev->dev; ++ int ret = 0; ++ ++ // Clean the slate - we're running synchronously ++ dma->head_idx = 0; ++ dma->tail_idx = 0; ++ ++ while (bytes > 0) { ++ size_t copy_bytes = min(bytes, dma->buf_size); ++ struct dma_buf_info *dbi; ++ ++ /* grab the next free buffer, waiting if they're all full */ ++ if (dma->head_idx - dma->tail_idx == dma->buf_count) { ++ if (down_timeout(&dma->buf_sem, ++ msecs_to_jiffies(1000))) { ++ dev_err(dev, "DMA bounce timed out\n"); ++ break; ++ } ++ dma->tail_idx++; ++ } ++ ++ dbi = &dma->bufs[dma->head_idx % dma->buf_count]; ++ ++ sg_dma_len(&dbi->sgl) = copy_bytes; ++ ++ ret = copy_from_user(dbi->buf, userbuf, copy_bytes); ++ if (ret < 0) ++ break; ++ ++ userbuf += copy_bytes; ++ ++ desc = dmaengine_prep_slave_sg(dma->chan, &dbi->sgl, 1, ++ DMA_MEM_TO_DEV, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK | ++ DMA_PREP_FENCE); ++ if (!desc) { ++ dev_err(dev, "DMA preparation failedzn"); ++ ret = -EIO; ++ break; ++ } ++ ++ desc->callback = rp1_pio_sm_dma_callback; ++ desc->callback_param = dma; ++ ++ /* Submit the buffer - the callback will kick the semaphore */ ++ ret = dmaengine_submit(desc); ++ if (ret < 0) ++ break; ++ ret = 0; ++ ++ dma_async_issue_pending(dma->chan); ++ ++ dma->head_idx++; ++ bytes -= copy_bytes; ++ } ++ ++ // Block for completion ++ while (dma->tail_idx != dma->head_idx) { ++ if (down_timeout(&dma->buf_sem, msecs_to_jiffies(1000))) { ++ dev_err(dev, "DMA wait timed out\n"); ++ ret = -ETIMEDOUT; ++ break; ++ } ++ dma->tail_idx++; ++ } ++ ++ return ret; ++} ++ ++static int rp1_pio_sm_rx_user(struct rp1_pio_device *pio, struct dma_info *dma, ++ void __user *userbuf, size_t bytes) ++{ ++ struct platform_device *pdev = pio->pdev; ++ struct dma_async_tx_descriptor *desc; ++ struct device *dev = &pdev->dev; ++ int ret = 0; ++ ++ /* Clean the slate - we're running synchronously */ ++ dma->head_idx = 0; ++ dma->tail_idx = 0; ++ ++ while (bytes || dma->tail_idx != dma->head_idx) { ++ size_t copy_bytes = min(bytes, dma->buf_size); ++ struct dma_buf_info *dbi; ++ ++ /* ++ * wait for the next RX to complete if all the buffers are ++ * outstanding or we're finishing up. ++ */ ++ if (!bytes || dma->head_idx - dma->tail_idx == dma->buf_count) { ++ if (down_timeout(&dma->buf_sem, ++ msecs_to_jiffies(1000))) { ++ dev_err(dev, "DMA wait timed out"); ++ ret = -ETIMEDOUT; ++ break; ++ } ++ ++ dbi = &dma->bufs[dma->tail_idx++ % dma->buf_count]; ++ ret = copy_to_user(userbuf, dbi->buf, sg_dma_len(&dbi->sgl)); ++ if (ret < 0) ++ break; ++ userbuf += sg_dma_len(&dbi->sgl); ++ ++ if (!bytes) ++ continue; ++ } ++ ++ dbi = &dma->bufs[dma->head_idx % dma->buf_count]; ++ sg_dma_len(&dbi->sgl) = copy_bytes; ++ desc = dmaengine_prep_slave_sg(dma->chan, &dbi->sgl, 1, ++ DMA_DEV_TO_MEM, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK | ++ DMA_PREP_FENCE); ++ if (!desc) { ++ dev_err(dev, "DMA preparation failed"); ++ ret = -EIO; ++ break; ++ } ++ ++ desc->callback = rp1_pio_sm_dma_callback; ++ desc->callback_param = dma; ++ ++ // Submit the buffer - the callback will kick the semaphore ++ ++ ret = dmaengine_submit(desc); ++ if (ret < 0) ++ break; ++ ++ dma_async_issue_pending(dma->chan); ++ ++ dma->head_idx++; ++ bytes -= copy_bytes; ++ } ++ ++ return ret; ++} ++ ++static int rp1_pio_sm_xfer_data(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_xfer_data_args *args = param; ++ struct rp1_pio_device *pio = client->pio; ++ struct dma_info *dma; ++ ++ if (args->sm >= RP1_PIO_SMS_COUNT || args->dir >= RP1_PIO_DIR_COUNT || ++ !args->data_bytes || !args->data) ++ return -EINVAL; ++ ++ dma = &pio->dma_configs[args->sm][args->dir]; ++ ++ if (args->dir == RP1_PIO_DIR_TO_SM) ++ return rp1_pio_sm_tx_user(pio, dma, args->data, args->data_bytes); ++ else ++ return rp1_pio_sm_rx_user(pio, dma, args->data, args->data_bytes); ++} ++ ++struct handler_info { ++ const char *name; ++ int (*func)(struct rp1_pio_client *client, void *param); ++ int argsize; ++} ioctl_handlers[] = { ++ HANDLER(SM_CONFIG_XFER, sm_config_xfer), ++ HANDLER(SM_XFER_DATA, sm_xfer_data), ++ ++ HANDLER(CAN_ADD_PROGRAM, can_add_program), ++ HANDLER(ADD_PROGRAM, add_program), ++ HANDLER(REMOVE_PROGRAM, remove_program), ++ HANDLER(CLEAR_INSTR_MEM, clear_instr_mem), ++ ++ HANDLER(SM_CLAIM, sm_claim), ++ HANDLER(SM_UNCLAIM, sm_unclaim), ++ HANDLER(SM_IS_CLAIMED, sm_is_claimed), ++ ++ HANDLER(SM_INIT, sm_init), ++ HANDLER(SM_SET_CONFIG, sm_set_config), ++ HANDLER(SM_EXEC, sm_exec), ++ HANDLER(SM_CLEAR_FIFOS, sm_clear_fifos), ++ HANDLER(SM_SET_CLKDIV, sm_set_clkdiv), ++ HANDLER(SM_SET_PINS, sm_set_pins), ++ HANDLER(SM_SET_PINDIRS, sm_set_pindirs), ++ HANDLER(SM_SET_ENABLED, sm_set_enabled), ++ HANDLER(SM_RESTART, sm_restart), ++ HANDLER(SM_CLKDIV_RESTART, sm_clkdiv_restart), ++ HANDLER(SM_ENABLE_SYNC, sm_enable_sync), ++ HANDLER(SM_PUT, sm_put), ++ HANDLER(SM_GET, sm_get), ++ HANDLER(SM_SET_DMACTRL, sm_set_dmactrl), ++ ++ HANDLER(GPIO_INIT, gpio_init), ++ HANDLER(GPIO_SET_FUNCTION, gpio_set_function), ++ HANDLER(GPIO_SET_PULLS, gpio_set_pulls), ++ HANDLER(GPIO_SET_OUTOVER, gpio_set_outover), ++ HANDLER(GPIO_SET_INOVER, gpio_set_inover), ++ HANDLER(GPIO_SET_OEOVER, gpio_set_oeover), ++ HANDLER(GPIO_SET_INPUT_ENABLED, gpio_set_input_enabled), ++ HANDLER(GPIO_SET_DRIVE_STRENGTH, gpio_set_drive_strength), ++ ++ HANDLER(READ_HW, read_hw), ++ HANDLER(WRITE_HW, write_hw), ++}; ++ ++static int rp1_pio_open(struct inode *inode, struct file *filp) ++{ ++ struct rp1_pio_device *pio = g_pio; ++ struct rp1_pio_client *client; ++ ++ client = kzalloc(sizeof(*client), GFP_KERNEL); ++ ++ client->pio = pio; ++ filp->private_data = client; ++ ++ return 0; ++} ++ ++static int rp1_pio_release(struct inode *inode, struct file *filp) ++{ ++ struct rp1_pio_client *client = filp->private_data; ++ struct rp1_pio_device *pio = client->pio; ++ uint claimed_dmas = client->claimed_dmas; ++ int i; ++ ++ /* Free any allocated resources */ ++ ++ for (i = 0; claimed_dmas; i++) { ++ uint mask = (1 << i); ++ ++ if (claimed_dmas & mask) { ++ struct dma_info *dma = &pio->dma_configs[i >> 1][i & 1]; ++ ++ claimed_dmas &= ~mask; ++ rp1_pio_sm_dma_free(&pio->pdev->dev, dma); ++ } ++ } ++ ++ spin_lock(&pio->lock); ++ pio->claimed_dmas &= ~client->claimed_dmas; ++ spin_unlock(&pio->lock); ++ ++ if (client->claimed_sms) { ++ struct rp1_pio_sm_set_enabled_args se_args = { ++ .mask = client->claimed_sms, .enable = 0 ++ }; ++ struct rp1_pio_sm_claim_args uc_args = { ++ .mask = client->claimed_sms ++ }; ++ ++ rp1_pio_sm_set_enabled(client, &se_args); ++ rp1_pio_sm_unclaim(client, &uc_args); ++ } ++ ++ if (client->claimed_instrs) ++ rp1_pio_remove_instrs(pio, client->claimed_instrs); ++ ++ /* Reinitialise the SM? */ ++ ++ kfree(client); ++ ++ return 0; ++} ++ ++static long rp1_pio_ioctl(struct file *filp, unsigned int ioctl_num, ++ unsigned long ioctl_param) ++{ ++ struct rp1_pio_client *client = filp->private_data; ++ struct device *dev = &client->pio->pdev->dev; ++ void __user *argp = (void __user *)ioctl_param; ++ int nr = _IOC_NR(ioctl_num); ++ int sz = _IOC_SIZE(ioctl_num); ++ struct handler_info *hdlr = &ioctl_handlers[nr]; ++ uint32_t argbuf[MAX_ARG_SIZE/sizeof(uint32_t)]; ++ int ret; ++ ++ if (nr >= ARRAY_SIZE(ioctl_handlers) || !hdlr->func) { ++ dev_err(dev, "unknown ioctl: %x\n", ioctl_num); ++ return -EOPNOTSUPP; ++ } ++ ++ if (sz != hdlr->argsize) { ++ dev_err(dev, "wrong %s argsize (expected %d, got %d)\n", ++ hdlr->name, hdlr->argsize, sz); ++ return -EINVAL; ++ } ++ ++ if (copy_from_user(argbuf, argp, sz)) ++ return -EFAULT; ++ ++ ret = (hdlr->func)(client, argbuf); ++ dev_dbg(dev, "%s: %s -> %d\n", __func__, hdlr->name, ret); ++ if (ret > 0) { ++ if (copy_to_user(argp, argbuf, ret)) ++ ret = -EFAULT; ++ } ++ ++ return ret; ++} ++ ++const struct file_operations rp1_pio_fops = { ++ .owner = THIS_MODULE, ++ .open = rp1_pio_open, ++ .release = rp1_pio_release, ++ .unlocked_ioctl = rp1_pio_ioctl, ++}; ++ ++static int rp1_pio_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct resource *ioresource; ++ struct rp1_pio_device *pio; ++ struct rp1_firmware *fw; ++ uint32_t op_count = 0; ++ uint32_t op_base = 0; ++ struct device *cdev; ++ char dev_name[16]; ++ void *p; ++ int ret; ++ int i; ++ ++ /* Run-time check for a build-time misconfiguration */ ++ for (i = 0; i < ARRAY_SIZE(ioctl_handlers); i++) { ++ struct handler_info *hdlr = &ioctl_handlers[i]; ++ ++ if (WARN_ON(hdlr->argsize > MAX_ARG_SIZE)) ++ return -EINVAL; ++ } ++ ++ fw = devm_rp1_firmware_get(dev, dev->of_node); ++ if (IS_ERR(fw)) ++ return PTR_ERR(fw); ++ ++ ret = rp1_firmware_get_feature(fw, FOURCC_PIO, &op_base, &op_count); ++ if (ret < 0) ++ return ret; ++ ++ pio = devm_kzalloc(&pdev->dev, sizeof(*pio), GFP_KERNEL); ++ if (!pio) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, pio); ++ pio->fw_pio_base = op_base; ++ pio->fw_pio_count = op_count; ++ pio->pdev = pdev; ++ pio->fw = fw; ++ spin_lock_init(&pio->lock); ++ mutex_init(&pio->instr_mutex); ++ ++ p = devm_platform_get_and_ioremap_resource(pdev, 0, &ioresource); ++ if (IS_ERR(p)) ++ return PTR_ERR(p); ++ ++ pio->phys_addr = ioresource->start; ++ ++ ret = alloc_chrdev_region(&pio->dev_num, 0, 1, DRIVER_NAME); ++ if (ret < 0) { ++ dev_err(dev, "alloc_chrdev_region failed (rc=%d)\n", ret); ++ goto out_err; ++ } ++ ++ cdev_init(&pio->cdev, &rp1_pio_fops); ++ ret = cdev_add(&pio->cdev, pio->dev_num, 1); ++ if (ret) { ++ dev_err(dev, "cdev_add failed (err %d)\n", ret); ++ goto out_unregister; ++ } ++ ++ pio->dev_class = class_create(DRIVER_NAME); ++ if (IS_ERR(pio->dev_class)) { ++ ret = PTR_ERR(pio->dev_class); ++ dev_err(dev, "class_create failed (err %d)\n", ret); ++ goto out_cdev_del; ++ } ++ pdev->id = of_alias_get_id(pdev->dev.of_node, "pio"); ++ if (pdev->id < 0) { ++ dev_err(dev, "alias is missing\n"); ++ return -EINVAL; ++ goto out_class_destroy; ++ } ++ sprintf(dev_name, "pio%d", pdev->id); ++ cdev = device_create(pio->dev_class, NULL, pio->dev_num, NULL, dev_name); ++ if (IS_ERR(cdev)) { ++ ret = PTR_ERR(cdev); ++ dev_err(dev, "%s: device_create failed (err %d)\n", __func__, ret); ++ goto out_class_destroy; ++ } ++ ++ g_pio = pio; ++ ++ dev_info(dev, "Created instance as %s\n", dev_name); ++ return 0; ++ ++out_class_destroy: ++ class_destroy(pio->dev_class); ++ ++out_cdev_del: ++ cdev_del(&pio->cdev); ++ ++out_unregister: ++ unregister_chrdev_region(pio->dev_num, 1); ++ ++out_err: ++ return ret; ++} ++ ++static void rp1_pio_remove(struct platform_device *pdev) ++{ ++ struct rp1_pio_device *pio = platform_get_drvdata(pdev); ++ ++ /* There should be no clients */ ++ ++ if (g_pio == pio) ++ g_pio = NULL; ++} ++ ++static const struct of_device_id rp1_pio_ids[] = { ++ { .compatible = "raspberrypi,rp1-pio" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, rp1_pio_ids); ++ ++static struct platform_driver rp1_pio_driver = { ++ .driver = { ++ .name = "rp1-pio", ++ .of_match_table = of_match_ptr(rp1_pio_ids), ++ }, ++ .probe = rp1_pio_probe, ++ .remove_new = rp1_pio_remove, ++ .shutdown = rp1_pio_remove, ++}; ++ ++module_platform_driver(rp1_pio_driver); ++ ++MODULE_DESCRIPTION("PIO controller driver for Raspberry Pi RP1"); ++MODULE_AUTHOR("Phil Elwell"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/include/uapi/misc/rp1_pio_if.h +@@ -0,0 +1,212 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (c) 2023-24 Raspberry Pi Ltd. ++ * All rights reserved. ++ */ ++#ifndef _PIO_RP1_IF_H ++#define _PIO_RP1_IF_H ++ ++#include ++ ++#define RP1_PIO_INSTRUCTION_COUNT 32 ++#define RP1_PIO_SM_COUNT 4 ++#define RP1_PIO_GPIO_COUNT 28 ++#define RP1_GPIO_FUNC_PIO 7 ++ ++#define RP1_PIO_ORIGIN_ANY ((uint16_t)(~0)) ++ ++#define RP1_PIO_DIR_TO_SM 0 ++#define RP1_PIO_DIR_FROM_SM 1 ++#define RP1_PIO_DIR_COUNT 2 ++ ++typedef struct { ++ uint32_t clkdiv; ++ uint32_t execctrl; ++ uint32_t shiftctrl; ++ uint32_t pinctrl; ++} rp1_pio_sm_config; ++ ++struct rp1_pio_add_program_args { ++ uint16_t num_instrs; ++ uint16_t origin; ++ uint16_t instrs[RP1_PIO_INSTRUCTION_COUNT]; ++}; ++ ++struct rp1_pio_remove_program_args { ++ uint16_t num_instrs; ++ uint16_t origin; ++}; ++ ++struct rp1_pio_sm_claim_args { ++ uint16_t mask; ++}; ++ ++struct rp1_pio_sm_init_args { ++ uint16_t sm; ++ uint16_t initial_pc; ++ rp1_pio_sm_config config; ++}; ++ ++struct rp1_pio_sm_set_config_args { ++ uint16_t sm; ++ uint16_t rsvd; ++ rp1_pio_sm_config config; ++}; ++ ++struct rp1_pio_sm_exec_args { ++ uint16_t sm; ++ uint16_t instr; ++ uint8_t blocking; ++ uint8_t rsvd; ++}; ++ ++struct rp1_pio_sm_clear_fifos_args { ++ uint16_t sm; ++}; ++ ++struct rp1_pio_sm_set_clkdiv_args { ++ uint16_t sm; ++ uint16_t div_int; ++ uint8_t div_frac; ++ uint8_t rsvd; ++}; ++ ++struct rp1_pio_sm_set_pins_args { ++ uint16_t sm; ++ uint16_t rsvd; ++ uint32_t values; ++ uint32_t mask; ++}; ++ ++struct rp1_pio_sm_set_pindirs_args { ++ uint16_t sm; ++ uint16_t rsvd; ++ uint32_t dirs; ++ uint32_t mask; ++}; ++ ++struct rp1_pio_sm_set_enabled_args { ++ uint16_t mask; ++ uint8_t enable; ++ uint8_t rsvd; ++}; ++ ++struct rp1_pio_sm_restart_args { ++ uint16_t mask; ++}; ++ ++struct rp1_pio_sm_clkdiv_restart_args { ++ uint16_t mask; ++}; ++ ++struct rp1_pio_sm_enable_sync_args { ++ uint16_t mask; ++}; ++ ++struct rp1_pio_sm_put_args { ++ uint16_t sm; ++ uint8_t blocking; ++ uint8_t rsvd; ++ uint32_t data; ++}; ++ ++struct rp1_pio_sm_get_args { ++ uint16_t sm; ++ uint8_t blocking; ++ uint8_t rsvd; ++ uint32_t data; /* IN/OUT */ ++}; ++ ++struct rp1_pio_sm_set_dmactrl_args { ++ uint16_t sm; ++ uint8_t is_tx; ++ uint8_t rsvd; ++ uint32_t ctrl; ++}; ++ ++struct rp1_gpio_init_args { ++ uint16_t gpio; ++}; ++ ++struct rp1_gpio_set_function_args { ++ uint16_t gpio; ++ uint16_t fn; ++}; ++ ++struct rp1_gpio_set_pulls_args { ++ uint16_t gpio; ++ uint8_t up; ++ uint8_t down; ++}; ++ ++struct rp1_gpio_set_args { ++ uint16_t gpio; ++ uint16_t value; ++}; ++ ++struct rp1_pio_sm_config_xfer_args { ++ uint16_t sm; ++ uint16_t dir; ++ uint16_t buf_size; ++ uint16_t buf_count; ++}; ++ ++struct rp1_pio_sm_xfer_data_args { ++ uint16_t sm; ++ uint16_t dir; ++ uint16_t data_bytes; ++ void *data; ++}; ++ ++struct rp1_access_hw_args { ++ uint32_t addr; ++ uint32_t len; ++ void *data; ++}; ++ ++#define PIO_IOC_MAGIC 102 ++ ++#define PIO_IOC_SM_CONFIG_XFER _IOW(PIO_IOC_MAGIC, 0, struct rp1_pio_sm_config_xfer_args) ++#define PIO_IOC_SM_XFER_DATA _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args) ++ ++#ifdef CONFIG_COMPAT ++//XXX #define PIO_IOC_SM_XFER_DATA32 _IOW(PIO_IOC_MAGIC, 2, struct pio_sm_xfer_data_args) ++#endif ++ ++#define PIO_IOC_READ_HW _IOW(PIO_IOC_MAGIC, 8, struct rp1_access_hw_args) ++#define PIO_IOC_WRITE_HW _IOW(PIO_IOC_MAGIC, 9, struct rp1_access_hw_args) ++ ++#define PIO_IOC_CAN_ADD_PROGRAM _IOW(PIO_IOC_MAGIC, 10, struct rp1_pio_add_program_args) ++#define PIO_IOC_ADD_PROGRAM _IOW(PIO_IOC_MAGIC, 11, struct rp1_pio_add_program_args) ++#define PIO_IOC_REMOVE_PROGRAM _IOW(PIO_IOC_MAGIC, 12, struct rp1_pio_remove_program_args) ++#define PIO_IOC_CLEAR_INSTR_MEM _IO(PIO_IOC_MAGIC, 13) ++ ++#define PIO_IOC_SM_CLAIM _IOW(PIO_IOC_MAGIC, 20, struct rp1_pio_sm_claim_args) ++#define PIO_IOC_SM_UNCLAIM _IOW(PIO_IOC_MAGIC, 21, struct rp1_pio_sm_claim_args) ++#define PIO_IOC_SM_IS_CLAIMED _IOW(PIO_IOC_MAGIC, 22, struct rp1_pio_sm_claim_args) ++ ++#define PIO_IOC_SM_INIT _IOW(PIO_IOC_MAGIC, 30, struct rp1_pio_sm_init_args) ++#define PIO_IOC_SM_SET_CONFIG _IOW(PIO_IOC_MAGIC, 31, struct rp1_pio_sm_set_config_args) ++#define PIO_IOC_SM_EXEC _IOW(PIO_IOC_MAGIC, 32, struct rp1_pio_sm_exec_args) ++#define PIO_IOC_SM_CLEAR_FIFOS _IOW(PIO_IOC_MAGIC, 33, struct rp1_pio_sm_clear_fifos_args) ++#define PIO_IOC_SM_SET_CLKDIV _IOW(PIO_IOC_MAGIC, 34, struct rp1_pio_sm_set_clkdiv_args) ++#define PIO_IOC_SM_SET_PINS _IOW(PIO_IOC_MAGIC, 35, struct rp1_pio_sm_set_pins_args) ++#define PIO_IOC_SM_SET_PINDIRS _IOW(PIO_IOC_MAGIC, 36, struct rp1_pio_sm_set_pindirs_args) ++#define PIO_IOC_SM_SET_ENABLED _IOW(PIO_IOC_MAGIC, 37, struct rp1_pio_sm_set_enabled_args) ++#define PIO_IOC_SM_RESTART _IOW(PIO_IOC_MAGIC, 38, struct rp1_pio_sm_restart_args) ++#define PIO_IOC_SM_CLKDIV_RESTART _IOW(PIO_IOC_MAGIC, 39, struct rp1_pio_sm_restart_args) ++#define PIO_IOC_SM_ENABLE_SYNC _IOW(PIO_IOC_MAGIC, 40, struct rp1_pio_sm_enable_sync_args) ++#define PIO_IOC_SM_PUT _IOW(PIO_IOC_MAGIC, 41, struct rp1_pio_sm_put_args) ++#define PIO_IOC_SM_GET _IOWR(PIO_IOC_MAGIC, 42, struct rp1_pio_sm_get_args) ++#define PIO_IOC_SM_SET_DMACTRL _IOW(PIO_IOC_MAGIC, 43, struct rp1_pio_sm_set_dmactrl_args) ++ ++#define PIO_IOC_GPIO_INIT _IOW(PIO_IOC_MAGIC, 50, struct rp1_gpio_init_args) ++#define PIO_IOC_GPIO_SET_FUNCTION _IOW(PIO_IOC_MAGIC, 51, struct rp1_gpio_set_function_args) ++#define PIO_IOC_GPIO_SET_PULLS _IOW(PIO_IOC_MAGIC, 52, struct rp1_gpio_set_pulls_args) ++#define PIO_IOC_GPIO_SET_OUTOVER _IOW(PIO_IOC_MAGIC, 53, struct rp1_gpio_set_args) ++#define PIO_IOC_GPIO_SET_INOVER _IOW(PIO_IOC_MAGIC, 54, struct rp1_gpio_set_args) ++#define PIO_IOC_GPIO_SET_OEOVER _IOW(PIO_IOC_MAGIC, 55, struct rp1_gpio_set_args) ++#define PIO_IOC_GPIO_SET_INPUT_ENABLED _IOW(PIO_IOC_MAGIC, 56, struct rp1_gpio_set_args) ++#define PIO_IOC_GPIO_SET_DRIVE_STRENGTH _IOW(PIO_IOC_MAGIC, 57, struct rp1_gpio_set_args) ++ ++#endif diff --git a/target/linux/bcm27xx/patches-6.6/950-1386-dts-bcm2712-rpi-Add-RP1-firmware-and-mailboxes.patch b/target/linux/bcm27xx/patches-6.6/950-1386-dts-bcm2712-rpi-Add-RP1-firmware-and-mailboxes.patch new file mode 100644 index 0000000000..2afb809e5e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1386-dts-bcm2712-rpi-Add-RP1-firmware-and-mailboxes.patch @@ -0,0 +1,118 @@ +From 0b76dec8dfba8c1a4793dff0c86bf73d088a812e Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 1 Nov 2024 09:12:01 +0000 +Subject: [PATCH] dts: bcm2712-rpi: Add RP1 firmware and mailboxes + +Declare the communications channel to RP1. + +Signed-off-by: Phil Elwell +--- + .../boot/dts/broadcom/bcm2712-rpi-5-b.dts | 4 +-- + .../boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 4 +-- + arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 4 +++ + arch/arm64/boot/dts/broadcom/rp1.dtsi | 27 +++++++++++++++++++ + 4 files changed, 35 insertions(+), 4 deletions(-) + +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts +@@ -195,7 +195,7 @@ i2c_rp1boot: &_i2c3 { }; + // This is the RP1 peripheral space + ranges = <0xc0 0x40000000 + 0x02000000 0x00 0x00000000 +- 0x00 0x00400000>; ++ 0x00 0x00410000>; + + dma-ranges = + // inbound RP1 1x_xxxxxxxx -> PCIe 1x_xxxxxxxx +@@ -207,7 +207,7 @@ i2c_rp1boot: &_i2c3 { }; + // This allows the RP1 DMA controller to address RP1 hardware + <0xc0 0x40000000 + 0x02000000 0x0 0x00000000 +- 0x0 0x00400000>, ++ 0x0 0x00410000>, + + // inbound RP1 0x_xxxxxxxx -> PCIe 1x_xxxxxxxx + <0x00 0x00000000 +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi +@@ -176,7 +176,7 @@ i2c_rp1boot: &_i2c3 { }; + // This is the RP1 peripheral space + ranges = <0xc0 0x40000000 + 0x02000000 0x00 0x00000000 +- 0x00 0x00400000>; ++ 0x00 0x00410000>; + + dma-ranges = + // inbound RP1 1x_xxxxxxxx -> PCIe 1x_xxxxxxxx +@@ -188,7 +188,7 @@ i2c_rp1boot: &_i2c3 { }; + // This allows the RP1 DMA controller to address RP1 hardware + <0xc0 0x40000000 + 0x02000000 0x0 0x00000000 +- 0x0 0x00400000>, ++ 0x0 0x00410000>, + + // inbound RP1 0x_xxxxxxxx -> PCIe 1x_xxxxxxxx + <0x00 0x00000000 +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi +@@ -294,6 +294,10 @@ pciex4: &pcie2 { }; + status = "okay"; + }; + ++&rp1_mbox { ++ status = "okay"; ++}; ++ + /* Add some gpiomem nodes to make the devices accessible to userspace. + * /dev/gpiomem should expose the registers for the interface with DT alias + * gpio. +--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi ++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi +@@ -13,6 +13,14 @@ + + // ranges and dma-ranges must be provided by the includer + ++ rp1_mbox: mailbox@8000 { ++ compatible = "raspberrypi,rp1-mbox"; ++ status = "disabled"; ++ reg = <0xc0 0x40008000 0x0 0x4000>; // SYSCFG ++ interrupts = ; ++ #mbox-cells = <1>; ++ }; ++ + rp1_clocks: clocks@18000 { + compatible = "raspberrypi,rp1-clocks"; + #clock-cells = <1>; +@@ -1183,6 +1191,19 @@ + assigned-clocks = <&rp1_clocks RP1_CLK_DPI>; + assigned-clock-parents = <&rp1_clocks RP1_PLL_VIDEO>; + }; ++ ++ sram: sram@400000 { ++ compatible = "mmio-sram"; ++ reg = <0xc0 0x40400000 0x0 0x10000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 0xc0 0x40400000 0x10000>; ++ ++ rp1_fw_shmem: shmem@ff00 { ++ compatible = "raspberrypi,rp1-shmem"; ++ reg = <0xff00 0x100>; // firmware mailbox buffer ++ }; ++ }; + }; + }; + +@@ -1281,6 +1302,12 @@ + }; + + / { ++ rp1_firmware: rp1_firmware { ++ compatible = "raspberrypi,rp1-firmware", "simple-mfd"; ++ mboxes = <&rp1_mbox 0>; ++ shmem = <&rp1_fw_shmem>; ++ }; ++ + rp1_vdd_3v3: rp1_vdd_3v3 { + compatible = "regulator-fixed"; + regulator-name = "vdd-3v3"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1387-dts-bcm2712-rpi-Add-the-RP1-PIO-device.patch b/target/linux/bcm27xx/patches-6.6/950-1387-dts-bcm2712-rpi-Add-the-RP1-PIO-device.patch new file mode 100644 index 0000000000..29e54277c0 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1387-dts-bcm2712-rpi-Add-the-RP1-PIO-device.patch @@ -0,0 +1,55 @@ +From 3e3c1b9922b22d362a4a9133361597ac80b974bb Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 1 Nov 2024 09:13:53 +0000 +Subject: [PATCH] dts: bcm2712-rpi: Add the RP1 PIO device + +Declare the device that proxies RP1's PIO hardware. + +Signed-off-by: Phil Elwell +--- + arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 5 +++++ + arch/arm64/boot/dts/broadcom/rp1.dtsi | 12 ++++++++++++ + 2 files changed, 17 insertions(+) + +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi +@@ -97,6 +97,10 @@ + }; + }; + ++pio: &rp1_pio { ++ status = "okay"; ++}; ++ + / { + chosen: chosen { + bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe cgroup_disable=memory numa_policy=interleave"; +@@ -129,6 +133,7 @@ + i2c12 = &i2c_rp1boot; + mailbox = &mailbox; + mmc0 = &sdio1; ++ pio0 = &pio; + serial0 = &uart0; + serial1 = &uart1; + serial10 = &uart10; +--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi ++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi +@@ -1028,6 +1028,18 @@ + status = "disabled"; + }; + ++ rp1_pio: pio@178000 { ++ reg = <0xc0 0x40178000 0x0 0x20>; ++ compatible = "raspberrypi,rp1-pio"; ++ firmware = <&rp1_firmware>; ++ dmas = <&rp1_dma RP1_DMA_PIO_CH0_TX>, <&rp1_dma RP1_DMA_PIO_CH0_RX>, ++ <&rp1_dma RP1_DMA_PIO_CH1_TX>, <&rp1_dma RP1_DMA_PIO_CH1_RX>, ++ <&rp1_dma RP1_DMA_PIO_CH2_TX>, <&rp1_dma RP1_DMA_PIO_CH2_RX>, ++ <&rp1_dma RP1_DMA_PIO_CH3_TX>, <&rp1_dma RP1_DMA_PIO_CH3_RX>; ++ dma-names = "tx0", "rx0", "tx1", "rx1", "tx2", "rx2", "tx3", "rx3"; ++ status = "disabled"; ++ }; ++ + rp1_mmc0: mmc@180000 { + reg = <0xc0 0x40180000 0x0 0x100>; + compatible = "raspberrypi,rp1-dwcmshc"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1388-misc-rp1-pio-Add-an-in-kernel-API.patch b/target/linux/bcm27xx/patches-6.6/950-1388-misc-rp1-pio-Add-an-in-kernel-API.patch new file mode 100644 index 0000000000..6accaf000e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1388-misc-rp1-pio-Add-an-in-kernel-API.patch @@ -0,0 +1,1797 @@ +From 2819a61eb000c207589c97eef9d69a237c6cfdf3 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 8 Nov 2024 09:31:38 +0000 +Subject: [PATCH] misc: rp1-pio: Add an in-kernel API + +The header file linux/pio_rp1.h adds a pico-sdk-like interface to the +RP1 PIO subsystem for other drivers. + +Signed-off-by: Phil Elwell +--- + drivers/misc/rp1-pio.c | 169 ++++-- + include/linux/pio_instructions.h | 481 +++++++++++++++++ + include/linux/pio_rp1.h | 873 +++++++++++++++++++++++++++++++ + 3 files changed, 1474 insertions(+), 49 deletions(-) + create mode 100644 include/linux/pio_instructions.h + create mode 100644 include/linux/pio_rp1.h + +--- a/drivers/misc/rp1-pio.c ++++ b/drivers/misc/rp1-pio.c +@@ -1,15 +1,17 @@ + // SPDX-License-Identifier: GPL-2.0 +-// PIO driver for RP1 +-// +-// Copyright (C) 2023-2024 Raspberry Pi Ltd. +-// +-// Parts of this driver are based on: +-// - vcio.c, by Noralf Trønnes +-// Copyright (C) 2010 Broadcom +-// Copyright (C) 2015 Noralf Trønnes +-// Copyright (C) 2021 Raspberry Pi (Trading) Ltd. +-// - bcm2835_smi.c & bcm2835_smi_dev.c by Luke Wren +-// Copyright (c) 2015 Raspberry Pi (Trading) Ltd. ++/* ++ * PIO driver for RP1 ++ * ++ * Copyright (C) 2023-2024 Raspberry Pi Ltd. ++ * ++ * Parts of this driver are based on: ++ * - vcio.c, by Noralf Trønnes ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 2015 Noralf Trønnes ++ * Copyright (C) 2021 Raspberry Pi (Trading) Ltd. ++ * - bcm2835_smi.c & bcm2835_smi_dev.c by Luke Wren ++ * Copyright (c) 2015 Raspberry Pi (Trading) Ltd. ++ */ + + #include + #include +@@ -97,6 +99,7 @@ struct rp1_pio_client { + uint32_t claimed_sms; + uint32_t claimed_instrs; + uint32_t claimed_dmas; ++ int error; + }; + + static struct rp1_pio_device *g_pio; +@@ -195,7 +198,7 @@ static int rp1_pio_find_program(struct r + return -1; + } + +-static int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param) ++int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_add_program_args *args = param; + struct rp1_pio_device *pio = client->pio; +@@ -217,8 +220,9 @@ static int rp1_pio_can_add_program(struc + return rp1_pio_message(pio, PIO_CAN_ADD_PROGRAM, args, + offsetof(struct rp1_pio_add_program_args, instrs)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_can_add_program); + +-static int rp1_pio_add_program(struct rp1_pio_client *client, void *param) ++int rp1_pio_add_program(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_add_program_args *args = param; + struct rp1_pio_device *pio = client->pio; +@@ -254,6 +258,7 @@ static int rp1_pio_add_program(struct rp + mutex_unlock(&pio->instr_mutex); + return offset; + } ++EXPORT_SYMBOL_GPL(rp1_pio_add_program); + + static void rp1_pio_remove_instrs(struct rp1_pio_device *pio, uint32_t mask) + { +@@ -277,7 +282,7 @@ static void rp1_pio_remove_instrs(struct + mutex_unlock(&pio->instr_mutex); + } + +-static int rp1_pio_remove_program(struct rp1_pio_client *client, void *param) ++int rp1_pio_remove_program(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_remove_program_args *args = param; + uint32_t used_mask; +@@ -296,8 +301,9 @@ static int rp1_pio_remove_program(struct + } + return ret; + } ++EXPORT_SYMBOL_GPL(rp1_pio_remove_program); + +-static int rp1_pio_clear_instr_mem(struct rp1_pio_client *client, void *param) ++int rp1_pio_clear_instr_mem(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_device *pio = client->pio; + +@@ -309,8 +315,9 @@ static int rp1_pio_clear_instr_mem(struc + mutex_unlock(&pio->instr_mutex); + return 0; + } ++EXPORT_SYMBOL_GPL(rp1_pio_clear_instr_mem); + +-static int rp1_pio_sm_claim(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_claim(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_claim_args *args = param; + struct rp1_pio_device *pio = client->pio; +@@ -328,8 +335,9 @@ static int rp1_pio_sm_claim(struct rp1_p + mutex_unlock(&pio->instr_mutex); + return ret; + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_claim); + +-static int rp1_pio_sm_unclaim(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_unclaim(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_claim_args *args = param; + struct rp1_pio_device *pio = client->pio; +@@ -341,99 +349,113 @@ static int rp1_pio_sm_unclaim(struct rp1 + mutex_unlock(&pio->instr_mutex); + return 0; + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_unclaim); + +-static int rp1_pio_sm_is_claimed(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_is_claimed(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_claim_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_IS_CLAIMED, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_is_claimed); + +-static int rp1_pio_sm_init(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_init(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_init_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_INIT, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_init); + +-static int rp1_pio_sm_set_config(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_set_config(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_set_config_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_SET_CONFIG, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_config); + +-static int rp1_pio_sm_exec(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_exec(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_exec_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_EXEC, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_exec); + +-static int rp1_pio_sm_clear_fifos(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_clear_fifos(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_clear_fifos_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_CLEAR_FIFOS, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_clear_fifos); + +-static int rp1_pio_sm_set_clkdiv(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_set_clkdiv(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_set_clkdiv_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_SET_CLKDIV, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_clkdiv); + +-static int rp1_pio_sm_set_pins(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_set_pins(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_set_pins_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_SET_PINS, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_pins); + +-static int rp1_pio_sm_set_pindirs(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_set_pindirs(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_set_pindirs_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_SET_PINDIRS, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_pindirs); + +-static int rp1_pio_sm_set_enabled(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_set_enabled(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_set_enabled_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_SET_ENABLED, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_enabled); + +-static int rp1_pio_sm_restart(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_restart(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_restart_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_RESTART, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_restart); + +-static int rp1_pio_sm_clkdiv_restart(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_clkdiv_restart(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_restart_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_CLKDIV_RESTART, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_clkdiv_restart); + +-static int rp1_pio_sm_enable_sync(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_enable_sync(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_enable_sync_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_ENABLE_SYNC, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_enable_sync); + +-static int rp1_pio_sm_put(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_put(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_put_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_PUT, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_put); + +-static int rp1_pio_sm_get(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_get(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_get_args *args = param; + int ret; +@@ -444,69 +466,79 @@ static int rp1_pio_sm_get(struct rp1_pio + return offsetof(struct rp1_pio_sm_get_args, data) + ret; + return ret; + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_get); + +-static int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_set_dmactrl_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_SET_DMACTRL, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_dmactrl); + +-static int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_init_args *args = param; + + return rp1_pio_message(client->pio, GPIO_INIT, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_init); + +-static int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_set_function_args *args = param; + + return rp1_pio_message(client->pio, GPIO_SET_FUNCTION, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_function); + +-static int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_set_pulls_args *args = param; + + return rp1_pio_message(client->pio, GPIO_SET_PULLS, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_pulls); + +-static int rp1_pio_gpio_set_outover(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_set_outover(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_set_args *args = param; + + return rp1_pio_message(client->pio, GPIO_SET_OUTOVER, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_outover); + +-static int rp1_pio_gpio_set_inover(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_set_inover(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_set_args *args = param; + + return rp1_pio_message(client->pio, GPIO_SET_INOVER, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_inover); + +-static int rp1_pio_gpio_set_oeover(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_set_oeover(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_set_args *args = param; + + return rp1_pio_message(client->pio, GPIO_SET_OEOVER, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_oeover); + +-static int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_set_args *args = param; + + return rp1_pio_message(client->pio, GPIO_SET_INPUT_ENABLED, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_input_enabled); + +-static int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_set_args *args = param; + + return rp1_pio_message(client->pio, GPIO_SET_DRIVE_STRENGTH, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_drive_strength); + + static void rp1_pio_sm_dma_callback(void *param) + { +@@ -633,7 +665,7 @@ static int rp1_pio_sm_tx_user(struct rp1 + struct device *dev = &pdev->dev; + int ret = 0; + +- // Clean the slate - we're running synchronously ++ /* Clean the slate - we're running synchronously */ + dma->head_idx = 0; + dma->tail_idx = 0; + +@@ -686,7 +718,7 @@ static int rp1_pio_sm_tx_user(struct rp1 + bytes -= copy_bytes; + } + +- // Block for completion ++ /* Block for completion */ + while (dma->tail_idx != dma->head_idx) { + if (down_timeout(&dma->buf_sem, msecs_to_jiffies(1000))) { + dev_err(dev, "DMA wait timed out\n"); +@@ -830,22 +862,22 @@ struct handler_info { + HANDLER(WRITE_HW, write_hw), + }; + +-static int rp1_pio_open(struct inode *inode, struct file *filp) ++struct rp1_pio_client *pio_open(void) + { +- struct rp1_pio_device *pio = g_pio; + struct rp1_pio_client *client; + + client = kzalloc(sizeof(*client), GFP_KERNEL); ++ if (!client) ++ return ERR_PTR(-ENOMEM); + +- client->pio = pio; +- filp->private_data = client; ++ client->pio = g_pio; + +- return 0; ++ return client; + } ++EXPORT_SYMBOL_GPL(pio_open); + +-static int rp1_pio_release(struct inode *inode, struct file *filp) ++void pio_close(struct rp1_pio_client *client) + { +- struct rp1_pio_client *client = filp->private_data; + struct rp1_pio_device *pio = client->pio; + uint claimed_dmas = client->claimed_dmas; + int i; +@@ -885,6 +917,45 @@ static int rp1_pio_release(struct inode + /* Reinitialise the SM? */ + + kfree(client); ++} ++EXPORT_SYMBOL_GPL(pio_close); ++ ++void pio_set_error(struct rp1_pio_client *client, int err) ++{ ++ client->error = err; ++} ++EXPORT_SYMBOL_GPL(pio_set_error); ++ ++int pio_get_error(const struct rp1_pio_client *client) ++{ ++ return client->error; ++} ++EXPORT_SYMBOL_GPL(pio_get_error); ++ ++void pio_clear_error(struct rp1_pio_client *client) ++{ ++ client->error = 0; ++} ++EXPORT_SYMBOL_GPL(pio_clear_error); ++ ++static int rp1_pio_open(struct inode *inode, struct file *filp) ++{ ++ struct rp1_pio_client *client; ++ ++ client = pio_open(); ++ if (IS_ERR(client)) ++ return PTR_ERR(client); ++ ++ filp->private_data = client; ++ ++ return 0; ++} ++ ++static int rp1_pio_release(struct inode *inode, struct file *filp) ++{ ++ struct rp1_pio_client *client = filp->private_data; ++ ++ pio_close(client); + + return 0; + } +--- /dev/null ++++ b/include/linux/pio_instructions.h +@@ -0,0 +1,481 @@ ++/* SPDX-License-Identifier: BSD-3-Clause */ ++/* ++ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. ++ */ ++ ++#ifndef _HARDWARE_PIO_INSTRUCTIONS_H ++#define _HARDWARE_PIO_INSTRUCTIONS_H ++ ++/** \brief PIO instruction encoding ++ * \defgroup pio_instructions pio_instructions ++ * \ingroup hardware_pio ++ * ++ * Functions for generating PIO instruction encodings programmatically. In debug builds ++ *`PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` can be set to 1 to enable validation of encoding function ++ * parameters. ++ * ++ * For fuller descriptions of the instructions in question see the "RP2040 Datasheet" ++ */ ++ ++// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS, Enable/disable assertions in the PIO instructions, type=bool, default=0, group=pio_instructions ++#ifndef PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS ++#define PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS 0 ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++enum pio_instr_bits { ++ pio_instr_bits_jmp = 0x0000, ++ pio_instr_bits_wait = 0x2000, ++ pio_instr_bits_in = 0x4000, ++ pio_instr_bits_out = 0x6000, ++ pio_instr_bits_push = 0x8000, ++ pio_instr_bits_pull = 0x8080, ++ pio_instr_bits_mov = 0xa000, ++ pio_instr_bits_irq = 0xc000, ++ pio_instr_bits_set = 0xe000, ++}; ++ ++#ifndef NDEBUG ++#define _PIO_INVALID_IN_SRC 0x08u ++#define _PIO_INVALID_OUT_DEST 0x10u ++#define _PIO_INVALID_SET_DEST 0x20u ++#define _PIO_INVALID_MOV_SRC 0x40u ++#define _PIO_INVALID_MOV_DEST 0x80u ++#else ++#define _PIO_INVALID_IN_SRC 0u ++#define _PIO_INVALID_OUT_DEST 0u ++#define _PIO_INVALID_SET_DEST 0u ++#define _PIO_INVALID_MOV_SRC 0u ++#define _PIO_INVALID_MOV_DEST 0u ++#endif ++ ++/*! \brief Enumeration of values to pass for source/destination args for instruction encoding functions ++ * \ingroup pio_instructions ++ * ++ * \note Not all values are suitable for all functions. Validity is only checked in debug mode when ++ * `PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` is 1 ++ */ ++enum pio_src_dest { ++ pio_pins = 0u, ++ pio_x = 1u, ++ pio_y = 2u, ++ pio_null = 3u | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, ++ pio_pindirs = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, ++ pio_exec_mov = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, ++ pio_status = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, ++ pio_pc = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, ++ pio_isr = 6u | _PIO_INVALID_SET_DEST, ++ pio_osr = 7u | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST, ++ pio_exec_out = 7u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, ++}; ++ ++static inline uint _pio_major_instr_bits(uint instr) { ++ return instr & 0xe000u; ++} ++ ++static inline uint _pio_encode_instr_and_args(enum pio_instr_bits instr_bits, uint arg1, uint arg2) { ++ valid_params_if(PIO_INSTRUCTIONS, arg1 <= 0x7); ++#if PARAM_ASSERTIONS_ENABLED(PIO_INSTRUCTIONS) ++ uint32_t major = _pio_major_instr_bits(instr_bits); ++ if (major == pio_instr_bits_in || major == pio_instr_bits_out) { ++ assert(arg2 && arg2 <= 32); ++ } else { ++ assert(arg2 <= 31); ++ } ++#endif ++ return instr_bits | (arg1 << 5u) | (arg2 & 0x1fu); ++} ++ ++static inline uint _pio_encode_instr_and_src_dest(enum pio_instr_bits instr_bits, enum pio_src_dest dest, uint value) { ++ return _pio_encode_instr_and_args(instr_bits, dest & 7u, value); ++} ++ ++/*! \brief Encode just the delay slot bits of an instruction ++ * \ingroup pio_instructions ++ * ++ * \note This function does not return a valid instruction encoding; instead it returns an encoding of the delay ++ * slot suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when ++ * combining the results of this function with the results of \ref pio_encode_sideset and \ref pio_encode_sideset_opt ++ * as they share the same bits within the instruction encoding. ++ * ++ * \param cycles the number of cycles 0-31 (or less if side set is being used) ++ * \return the delay slot bits to be ORed with an instruction encoding ++ */ ++static inline uint pio_encode_delay(uint cycles) { ++ // note that the maximum cycles will be smaller if sideset_bit_count > 0 ++ valid_params_if(PIO_INSTRUCTIONS, cycles <= 0x1f); ++ return cycles << 8u; ++} ++ ++/*! \brief Encode just the side set bits of an instruction (in non optional side set mode) ++ * \ingroup pio_instructions ++ * ++ * \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits ++ * suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when ++ * combining the results of this function with the results of \ref pio_encode_delay as they share the same bits ++ * within the instruction encoding. ++ * ++ * \param sideset_bit_count number of side set bits as would be specified via `.sideset` in pioasm ++ * \param value the value to sideset on the pins ++ * \return the side set bits to be ORed with an instruction encoding ++ */ ++static inline uint pio_encode_sideset(uint sideset_bit_count, uint value) { ++ valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 5); ++ valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1)); ++ return value << (13u - sideset_bit_count); ++} ++ ++/*! \brief Encode just the side set bits of an instruction (in optional -`opt` side set mode) ++ * \ingroup pio_instructions ++ * ++ * \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits ++ * suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when ++ * combining the results of this function with the results of \ref pio_encode_delay as they share the same bits ++ * within the instruction encoding. ++ * ++ * \param sideset_bit_count number of side set bits as would be specified via `.sideset opt` in pioasm ++ * \param value the value to sideset on the pins ++ * \return the side set bits to be ORed with an instruction encoding ++ */ ++static inline uint pio_encode_sideset_opt(uint sideset_bit_count, uint value) { ++ valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 4); ++ valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1)); ++ return 0x1000u | value << (12u - sideset_bit_count); ++} ++ ++/*! \brief Encode an unconditional JMP instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 0, addr); ++} ++ ++/*! \brief Encode a conditional JMP if scratch X zero instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP !X ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_not_x(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 1, addr); ++} ++ ++/*! \brief Encode a conditional JMP if scratch X non-zero (and post-decrement X) instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP X-- ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_x_dec(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 2, addr); ++} ++ ++/*! \brief Encode a conditional JMP if scratch Y zero instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP !Y ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_not_y(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 3, addr); ++} ++ ++/*! \brief Encode a conditional JMP if scratch Y non-zero (and post-decrement Y) instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP Y-- ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_y_dec(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 4, addr); ++} ++ ++/*! \brief Encode a conditional JMP if scratch X not equal scratch Y instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP X!=Y ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_x_ne_y(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 5, addr); ++} ++ ++/*! \brief Encode a conditional JMP if input pin high instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP PIN ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_pin(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 6, addr); ++} ++ ++/*! \brief Encode a conditional JMP if output shift register not empty instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP !OSRE ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_not_osre(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 7, addr); ++} ++ ++static inline uint _pio_encode_irq(bool relative, uint irq) { ++ valid_params_if(PIO_INSTRUCTIONS, irq <= 7); ++ return (relative ? 0x10u : 0x0u) | irq; ++} ++ ++/*! \brief Encode a WAIT for GPIO pin instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `WAIT GPIO ` ++ * ++ * \param polarity true for `WAIT 1`, false for `WAIT 0` ++ * \param gpio The real GPIO number 0-31 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_wait_gpio(bool polarity, uint gpio) { ++ return _pio_encode_instr_and_args(pio_instr_bits_wait, 0u | (polarity ? 4u : 0u), gpio); ++} ++ ++/*! \brief Encode a WAIT for pin instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `WAIT PIN ` ++ * ++ * \param polarity true for `WAIT 1`, false for `WAIT 0` ++ * \param pin The pin number 0-31 relative to the executing SM's input pin mapping ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_wait_pin(bool polarity, uint pin) { ++ return _pio_encode_instr_and_args(pio_instr_bits_wait, 1u | (polarity ? 4u : 0u), pin); ++} ++ ++/*! \brief Encode a WAIT for IRQ instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `WAIT IRQ ` ++ * ++ * \param polarity true for `WAIT 1`, false for `WAIT 0` ++ * \param relative true for a `WAIT IRQ REL`, false for regular `WAIT IRQ ` ++ * \param irq the irq number 0-7 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_wait_irq(bool polarity, bool relative, uint irq) { ++ valid_params_if(PIO_INSTRUCTIONS, irq <= 7); ++ return _pio_encode_instr_and_args(pio_instr_bits_wait, 2u | (polarity ? 4u : 0u), _pio_encode_irq(relative, irq)); ++} ++ ++/*! \brief Encode an IN instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `IN , ` ++ * ++ * \param src The source to take data from ++ * \param count The number of bits 1-32 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_in(enum pio_src_dest src, uint count) { ++ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_IN_SRC)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_in, src, count); ++} ++ ++/*! \brief Encode an OUT instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `OUT , ` ++ * ++ * \param dest The destination to write data to ++ * \param count The number of bits 1-32 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_out(enum pio_src_dest dest, uint count) { ++ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_OUT_DEST)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_out, dest, count); ++} ++ ++/*! \brief Encode a PUSH instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `PUSH , ` ++ * ++ * \param if_full true for `PUSH IF_FULL ...`, false for `PUSH ...` ++ * \param block true for `PUSH ... BLOCK`, false for `PUSH ...` ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_push(bool if_full, bool block) { ++ return _pio_encode_instr_and_args(pio_instr_bits_push, (if_full ? 2u : 0u) | (block ? 1u : 0u), 0); ++} ++ ++/*! \brief Encode a PULL instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `PULL , ` ++ * ++ * \param if_empty true for `PULL IF_EMPTY ...`, false for `PULL ...` ++ * \param block true for `PULL ... BLOCK`, false for `PULL ...` ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_pull(bool if_empty, bool block) { ++ return _pio_encode_instr_and_args(pio_instr_bits_pull, (if_empty ? 2u : 0u) | (block ? 1u : 0u), 0); ++} ++ ++/*! \brief Encode a MOV instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `MOV , ` ++ * ++ * \param dest The destination to write data to ++ * \param src The source to take data from ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_mov(enum pio_src_dest dest, enum pio_src_dest src) { ++ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); ++ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, src & 7u); ++} ++ ++/*! \brief Encode a MOV instruction with bit invert ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `MOV , ~` ++ * ++ * \param dest The destination to write inverted data to ++ * \param src The source to take data from ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_mov_not(enum pio_src_dest dest, enum pio_src_dest src) { ++ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); ++ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (1u << 3u) | (src & 7u)); ++} ++ ++/*! \brief Encode a MOV instruction with bit reverse ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `MOV , ::` ++ * ++ * \param dest The destination to write bit reversed data to ++ * \param src The source to take data from ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_mov_reverse(enum pio_src_dest dest, enum pio_src_dest src) { ++ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); ++ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (2u << 3u) | (src & 7u)); ++} ++ ++/*! \brief Encode a IRQ SET instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `IRQ SET ` ++ * ++ * \param relative true for a `IRQ SET REL`, false for regular `IRQ SET ` ++ * \param irq the irq number 0-7 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_irq_set(bool relative, uint irq) { ++ return _pio_encode_instr_and_args(pio_instr_bits_irq, 0, _pio_encode_irq(relative, irq)); ++} ++ ++/*! \brief Encode a IRQ WAIT instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `IRQ WAIT ` ++ * ++ * \param relative true for a `IRQ WAIT REL`, false for regular `IRQ WAIT ` ++ * \param irq the irq number 0-7 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_irq_wait(bool relative, uint irq) { ++ return _pio_encode_instr_and_args(pio_instr_bits_irq, 1, _pio_encode_irq(relative, irq)); ++} ++ ++/*! \brief Encode a IRQ CLEAR instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `IRQ CLEAR ` ++ * ++ * \param relative true for a `IRQ CLEAR REL`, false for regular `IRQ CLEAR ` ++ * \param irq the irq number 0-7 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_irq_clear(bool relative, uint irq) { ++ return _pio_encode_instr_and_args(pio_instr_bits_irq, 2, _pio_encode_irq(relative, irq)); ++} ++ ++/*! \brief Encode a SET instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `SET , ` ++ * ++ * \param dest The destination to apply the value to ++ * \param value The value 0-31 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_set(enum pio_src_dest dest, uint value) { ++ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_SET_DEST)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_set, dest, value); ++} ++ ++/*! \brief Encode a NOP instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `NOP` which is itself encoded as `MOV y, y` ++ * ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_nop(void) { ++ return pio_encode_mov(pio_y, pio_y); ++} ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +--- /dev/null ++++ b/include/linux/pio_rp1.h +@@ -0,0 +1,873 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (c) 2024 Raspberry Pi Ltd. ++ * All rights reserved. ++ */ ++ ++#ifndef _PIO_RP1_H ++#define _PIO_RP1_H ++ ++#include ++ ++#define PARAM_WARNINGS_ENABLED 1 ++ ++#ifdef DEBUG ++#define PARAM_WARNINGS_ENABLED 1 ++#endif ++ ++#ifndef PARAM_WARNINGS_ENABLED ++#define PARAM_WARNINGS_ENABLED 0 ++#endif ++ ++#define bad_params_if(client, test) \ ++ ({ bool f = (test); if (f) pio_set_error(client, -EINVAL); \ ++ if (f && PARAM_WARNINGS_ENABLED) WARN_ON((test)); \ ++ f; }) ++ ++#ifndef PARAM_ASSERTIONS_ENABLE_ALL ++#define PARAM_ASSERTIONS_ENABLE_ALL 0 ++#endif ++ ++#ifndef PARAM_ASSERTIONS_DISABLE_ALL ++#define PARAM_ASSERTIONS_DISABLE_ALL 0 ++#endif ++ ++#define PARAM_ASSERTIONS_ENABLED(x) \ ++ ((PARAM_ASSERTIONS_ENABLED_ ## x || PARAM_ASSERTIONS_ENABLE_ALL) && \ ++ !PARAM_ASSERTIONS_DISABLE_ALL) ++#define valid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) WARN_ON(test); }) ++ ++#include ++ ++#define NUM_PIO_STATE_MACHINES 4 ++#define PIO_INSTRUCTION_COUNT 32 ++#define PIO_ORIGIN_ANY ((uint)(~0)) ++#define GPIOS_MASK ((1 << RP1_PIO_GPIO_COUNT) - 1) ++ ++#define PICO_NO_HARDWARE 0 ++ ++#define pio0 pio_open_helper(0) ++ ++#define PROC_PIO_SM0_PINCTRL_OUT_BASE_BITS 0x0000001f ++#define PROC_PIO_SM0_PINCTRL_OUT_BASE_LSB 0 ++#define PROC_PIO_SM0_PINCTRL_OUT_COUNT_BITS 0x03f00000 ++#define PROC_PIO_SM0_PINCTRL_OUT_COUNT_LSB 20 ++#define PROC_PIO_SM0_PINCTRL_SET_BASE_BITS 0x000003e0 ++#define PROC_PIO_SM0_PINCTRL_SET_BASE_LSB 5 ++#define PROC_PIO_SM0_PINCTRL_SET_COUNT_BITS 0x1c000000 ++#define PROC_PIO_SM0_PINCTRL_SET_COUNT_LSB 26 ++#define PROC_PIO_SM0_PINCTRL_IN_BASE_BITS 0x000f8000 ++#define PROC_PIO_SM0_PINCTRL_IN_BASE_LSB 15 ++#define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_BITS 0x00007c00 ++#define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_LSB 10 ++#define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_BITS 0xe0000000 ++#define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_LSB 29 ++#define PROC_PIO_SM0_EXECCTRL_SIDE_EN_BITS 0x40000000 ++#define PROC_PIO_SM0_EXECCTRL_SIDE_EN_LSB 30 ++#define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_BITS 0x20000000 ++#define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_LSB 29 ++#define PROC_PIO_SM0_CLKDIV_INT_LSB 16 ++#define PROC_PIO_SM0_CLKDIV_FRAC_LSB 8 ++#define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_BITS 0x0001f000 ++#define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_LSB 12 ++#define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS 0x00000f80 ++#define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB 7 ++#define PROC_PIO_SM0_EXECCTRL_JMP_PIN_BITS 0x1f000000 ++#define PROC_PIO_SM0_EXECCTRL_JMP_PIN_LSB 24 ++#define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_BITS 0x00040000 ++#define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_LSB 18 ++#define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_BITS 0x00020000 ++#define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_LSB 17 ++#define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS 0x00010000 ++#define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_LSB 16 ++#define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_BITS 0x01f00000 ++#define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB 20 ++#define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS 0x00080000 ++#define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB 19 ++#define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS 0x3e000000 ++#define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB 25 ++#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS 0x40000000 ++#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB 30 ++#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS 0x80000000 ++#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_LSB 31 ++#define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_BITS 0x00020000 ++#define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_LSB 17 ++#define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_BITS 0x00040000 ++#define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_LSB 18 ++#define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS 0x00f80000 ++#define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_LSB 19 ++#define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS 0x00000020 ++#define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_LSB 5 ++#define PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS 0x0000001f ++#define PROC_PIO_SM0_EXECCTRL_STATUS_N_LSB 0 ++ ++enum pio_fifo_join { ++ PIO_FIFO_JOIN_NONE = 0, ++ PIO_FIFO_JOIN_TX = 1, ++ PIO_FIFO_JOIN_RX = 2, ++}; ++ ++enum pio_mov_status_type { ++ STATUS_TX_LESSTHAN = 0, ++ STATUS_RX_LESSTHAN = 1 ++}; ++ ++enum pio_xfer_dir { ++ PIO_DIR_TO_SM, ++ PIO_DIR_FROM_SM, ++ PIO_DIR_COUNT ++}; ++ ++enum clock_index { ++ clk_sys = 5 ++}; ++ ++typedef struct pio_program { ++ const uint16_t *instructions; ++ uint8_t length; ++ int8_t origin; // required instruction memory origin or -1 ++} pio_program_t; ++ ++enum gpio_function { ++ GPIO_FUNC_FSEL0 = 0, ++ GPIO_FUNC_FSEL1 = 1, ++ GPIO_FUNC_FSEL2 = 2, ++ GPIO_FUNC_FSEL3 = 3, ++ GPIO_FUNC_FSEL4 = 4, ++ GPIO_FUNC_FSEL5 = 5, ++ GPIO_FUNC_FSEL6 = 6, ++ GPIO_FUNC_FSEL7 = 7, ++ GPIO_FUNC_FSEL8 = 8, ++ GPIO_FUNC_NULL = 0x1f, ++ ++ // Name a few ++ GPIO_FUNC_SYS_RIO = 5, ++ GPIO_FUNC_PROC_RIO = 6, ++ GPIO_FUNC_PIO = 7, ++}; ++ ++enum gpio_irq_level { ++ GPIO_IRQ_LEVEL_LOW = 0x1u, ++ GPIO_IRQ_LEVEL_HIGH = 0x2u, ++ GPIO_IRQ_EDGE_FALL = 0x4u, ++ GPIO_IRQ_EDGE_RISE = 0x8u, ++}; ++ ++enum gpio_override { ++ GPIO_OVERRIDE_NORMAL = 0, ++ GPIO_OVERRIDE_INVERT = 1, ++ GPIO_OVERRIDE_LOW = 2, ++ GPIO_OVERRIDE_HIGH = 3, ++}; ++enum gpio_slew_rate { ++ GPIO_SLEW_RATE_SLOW = 0, ++ GPIO_SLEW_RATE_FAST = 1 ++}; ++ ++enum gpio_drive_strength { ++ GPIO_DRIVE_STRENGTH_2MA = 0, ++ GPIO_DRIVE_STRENGTH_4MA = 1, ++ GPIO_DRIVE_STRENGTH_8MA = 2, ++ GPIO_DRIVE_STRENGTH_12MA = 3 ++}; ++ ++typedef rp1_pio_sm_config pio_sm_config; ++ ++typedef struct rp1_pio_client *PIO; ++ ++void pio_set_error(struct rp1_pio_client *client, int err); ++int pio_get_error(struct rp1_pio_client *client); ++void pio_clear_error(struct rp1_pio_client *client); ++ ++int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param); ++int rp1_pio_add_program(struct rp1_pio_client *client, void *param); ++int rp1_pio_remove_program(struct rp1_pio_client *client, void *param); ++int rp1_pio_clear_instr_mem(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_claim(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_unclaim(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_is_claimed(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_init(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_config(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_exec(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_clear_fifos(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_clkdiv(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_pins(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_pindirs(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_enabled(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_restart(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_clkdiv_restart(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_enable_sync(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_put(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_get(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_outover(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_inover(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_oeover(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param); ++ ++int pio_init(void); ++PIO pio_open(void); ++void pio_close(PIO pio); ++ ++int pio_sm_config_xfer(PIO pio, uint sm, uint dir, uint buf_size, uint buf_count); ++int pio_sm_xfer_data(PIO pio, uint sm, uint dir, uint data_bytes, void *data); ++ ++static inline bool pio_can_add_program(struct rp1_pio_client *client, ++ const pio_program_t *program) ++{ ++ struct rp1_pio_add_program_args args; ++ ++ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT)) ++ return false; ++ args.origin = (program->origin == -1) ? PIO_ORIGIN_ANY : program->origin; ++ args.num_instrs = program->length; ++ ++ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); ++ return rp1_pio_can_add_program(client, &args); ++} ++ ++static inline bool pio_can_add_program_at_offset(struct rp1_pio_client *client, ++ const pio_program_t *program, uint offset) ++{ ++ struct rp1_pio_add_program_args args; ++ ++ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT || ++ offset >= PIO_INSTRUCTION_COUNT)) ++ return false; ++ args.origin = offset; ++ args.num_instrs = program->length; ++ ++ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); ++ return !rp1_pio_can_add_program(client, &args); ++} ++ ++uint pio_add_program(struct rp1_pio_client *client, const pio_program_t *program) ++{ ++ struct rp1_pio_add_program_args args; ++ int offset; ++ ++ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT)) ++ return PIO_ORIGIN_ANY; ++ args.origin = (program->origin == -1) ? PIO_ORIGIN_ANY : program->origin; ++ args.num_instrs = program->length; ++ ++ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); ++ offset = rp1_pio_add_program(client, &args); ++ return (offset >= 0) ? offset : PIO_ORIGIN_ANY; ++} ++ ++static inline int pio_add_program_at_offset(struct rp1_pio_client *client, ++ const pio_program_t *program, uint offset) ++{ ++ struct rp1_pio_add_program_args args; ++ ++ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT || ++ offset >= PIO_INSTRUCTION_COUNT)) ++ return -EINVAL; ++ args.origin = offset; ++ args.num_instrs = program->length; ++ ++ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); ++ return rp1_pio_add_program(client, &args); ++} ++ ++static inline int pio_remove_program(struct rp1_pio_client *client, const pio_program_t *program, ++ uint loaded_offset) ++{ ++ struct rp1_pio_remove_program_args args; ++ ++ args.origin = loaded_offset; ++ args.num_instrs = program->length; ++ ++ return rp1_pio_remove_program(client, &args); ++} ++ ++static inline int pio_clear_instruction_memory(struct rp1_pio_client *client) ++{ ++ return rp1_pio_clear_instr_mem(client, NULL); ++} ++ ++static inline int pio_sm_claim(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_claim_args args = { .mask = 1 << sm }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_claim(client, &args); ++} ++ ++static inline int pio_claim_sm_mask(struct rp1_pio_client *client, uint mask) ++{ ++ struct rp1_pio_sm_claim_args args = { .mask = mask }; ++ ++ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) ++ return -EINVAL; ++ ++ return rp1_pio_sm_claim(client, &args); ++} ++ ++static inline int pio_sm_unclaim(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_claim_args args = { .mask = 1 << sm }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_claim(client, &args); ++} ++ ++static inline int pio_claim_unused_sm(struct rp1_pio_client *client, bool required) ++{ ++ struct rp1_pio_sm_claim_args args = { .mask = 0 }; ++ int sm; ++ ++ sm = rp1_pio_sm_claim(client, &args); ++ if (sm < 0 && required) ++ WARN_ON("No PIO state machines are available"); ++ return sm; ++} ++ ++static inline bool pio_sm_is_claimed(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_claim_args args = { .mask = (1 << sm) }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return true; ++ return rp1_pio_sm_is_claimed(client, &args); ++} ++ ++static inline int pio_sm_init(struct rp1_pio_client *client, uint sm, uint initial_pc, ++ const pio_sm_config *config) ++{ ++ struct rp1_pio_sm_init_args args = { .sm = sm, .initial_pc = initial_pc, ++ .config = *config }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || ++ initial_pc >= PIO_INSTRUCTION_COUNT)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_init(client, &args); ++} ++ ++static inline int pio_sm_set_config(struct rp1_pio_client *client, uint sm, ++ const pio_sm_config *config) ++{ ++ struct rp1_pio_sm_init_args args = { .sm = sm, .config = *config }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_set_config(client, &args); ++} ++ ++int pio_sm_exec(struct rp1_pio_client *client, uint sm, uint instr) ++{ ++ struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = false }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || instr > (uint16_t)~0)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_exec(client, &args); ++} ++ ++int pio_sm_exec_wait_blocking(struct rp1_pio_client *client, uint sm, uint instr) ++{ ++ struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = true }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || instr > (uint16_t)~0)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_exec(client, &args); ++} ++ ++static inline int pio_sm_clear_fifos(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_clear_fifos_args args = { .sm = sm }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_clear_fifos(client, &args); ++} ++ ++static inline bool pio_calculate_clkdiv_from_float(float div, uint16_t *div_int, ++ uint8_t *div_frac) ++{ ++ if (bad_params_if(NULL, div < 1 || div > 65536)) ++ return false; ++ *div_int = (uint16_t)div; ++ if (*div_int == 0) ++ *div_frac = 0; ++ else ++ *div_frac = (uint8_t)((div - (float)*div_int) * (1u << 8u)); ++ return true; ++} ++ ++static inline int pio_sm_set_clkdiv_int_frac(struct rp1_pio_client *client, uint sm, ++ uint16_t div_int, uint8_t div_frac) ++{ ++ struct rp1_pio_sm_set_clkdiv_args args = { .sm = sm, .div_int = div_int, ++ .div_frac = div_frac }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || ++ (div_int == 0 && div_frac != 0))) ++ return -EINVAL; ++ return rp1_pio_sm_set_clkdiv(client, &args); ++} ++ ++static inline int pio_sm_set_clkdiv(struct rp1_pio_client *client, uint sm, float div) ++{ ++ struct rp1_pio_sm_set_clkdiv_args args = { .sm = sm }; ++ ++ if (!pio_calculate_clkdiv_from_float(div, &args.div_int, &args.div_frac)) ++ return -EINVAL; ++ return rp1_pio_sm_set_clkdiv(client, &args); ++} ++ ++static inline int pio_sm_set_pins(struct rp1_pio_client *client, uint sm, uint32_t pin_values) ++{ ++ struct rp1_pio_sm_set_pins_args args = { .sm = sm, .values = pin_values, ++ .mask = GPIOS_MASK }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_set_pins(client, &args); ++} ++ ++static inline int pio_sm_set_pins_with_mask(struct rp1_pio_client *client, uint sm, ++ uint32_t pin_values, uint32_t pin_mask) ++{ ++ struct rp1_pio_sm_set_pins_args args = { .sm = sm, .values = pin_values, ++ .mask = pin_mask }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_set_pins(client, &args); ++} ++ ++static inline int pio_sm_set_pindirs_with_mask(struct rp1_pio_client *client, uint sm, ++ uint32_t pin_dirs, uint32_t pin_mask) ++{ ++ struct rp1_pio_sm_set_pindirs_args args = { .sm = sm, .dirs = pin_dirs, ++ .mask = pin_mask }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || ++ (pin_dirs & GPIOS_MASK) != pin_dirs || ++ (pin_mask & pin_mask) != pin_mask)) ++ return -EINVAL; ++ return rp1_pio_sm_set_pindirs(client, &args); ++} ++ ++static inline int pio_sm_set_consecutive_pindirs(struct rp1_pio_client *client, uint sm, ++ uint pin_base, uint pin_count, bool is_out) ++{ ++ uint32_t mask = ((1 << pin_count) - 1) << pin_base; ++ struct rp1_pio_sm_set_pindirs_args args = { .sm = sm, .dirs = is_out ? mask : 0, ++ .mask = mask }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || ++ pin_base >= RP1_PIO_GPIO_COUNT || ++ pin_count > RP1_PIO_GPIO_COUNT || ++ (pin_base + pin_count) > RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_sm_set_pindirs(client, &args); ++} ++ ++static inline int pio_sm_set_enabled(struct rp1_pio_client *client, uint sm, bool enabled) ++{ ++ struct rp1_pio_sm_set_enabled_args args = { .mask = (1 << sm), .enable = enabled }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_set_enabled(client, &args); ++} ++ ++static inline int pio_set_sm_mask_enabled(struct rp1_pio_client *client, uint32_t mask, ++ bool enabled) ++{ ++ struct rp1_pio_sm_set_enabled_args args = { .mask = mask, .enable = enabled }; ++ ++ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) ++ return -EINVAL; ++ return rp1_pio_sm_set_enabled(client, &args); ++} ++ ++static inline int pio_sm_restart(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_restart_args args = { .mask = (1 << sm) }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_restart(client, &args); ++} ++ ++static inline int pio_restart_sm_mask(struct rp1_pio_client *client, uint32_t mask) ++{ ++ struct rp1_pio_sm_restart_args args = { .mask = (uint16_t)mask }; ++ ++ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) ++ return -EINVAL; ++ return rp1_pio_sm_restart(client, &args); ++} ++ ++static inline int pio_sm_clkdiv_restart(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_restart_args args = { .mask = (1 << sm) }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_clkdiv_restart(client, &args); ++} ++ ++static inline int pio_clkdiv_restart_sm_mask(struct rp1_pio_client *client, uint32_t mask) ++{ ++ struct rp1_pio_sm_restart_args args = { .mask = (uint16_t)mask }; ++ ++ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) ++ return -EINVAL; ++ return rp1_pio_sm_clkdiv_restart(client, &args); ++} ++ ++static inline int pio_enable_sm_in_sync_mask(struct rp1_pio_client *client, uint32_t mask) ++{ ++ struct rp1_pio_sm_enable_sync_args args = { .mask = (uint16_t)mask }; ++ ++ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) ++ return -EINVAL; ++ return rp1_pio_sm_enable_sync(client, &args); ++} ++ ++static inline int pio_sm_set_dmactrl(struct rp1_pio_client *client, uint sm, bool is_tx, ++ uint32_t ctrl) ++{ ++ struct rp1_pio_sm_set_dmactrl_args args = { .sm = sm, .is_tx = is_tx, .ctrl = ctrl }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_set_dmactrl(client, &args); ++}; ++ ++static inline int pio_sm_put(struct rp1_pio_client *client, uint sm, uint32_t data) ++{ ++ struct rp1_pio_sm_put_args args = { .sm = (uint16_t)sm, .blocking = false, .data = data }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_put(client, &args); ++} ++ ++static inline int pio_sm_put_blocking(struct rp1_pio_client *client, uint sm, uint32_t data) ++{ ++ struct rp1_pio_sm_put_args args = { .sm = (uint16_t)sm, .blocking = true, .data = data }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_put(client, &args); ++} ++ ++static inline uint32_t pio_sm_get(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_get_args args = { .sm = (uint16_t)sm, .blocking = false }; ++ ++ if (!bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ rp1_pio_sm_get(client, &args); ++ return args.data; ++} ++ ++static inline uint32_t pio_sm_get_blocking(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_get_args args = { .sm = (uint16_t)sm, .blocking = true }; ++ ++ if (!bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ rp1_pio_sm_get(client, &args); ++ return args.data; ++} ++ ++static inline void sm_config_set_out_pins(pio_sm_config *c, uint out_base, uint out_count) ++{ ++ if (bad_params_if(NULL, out_base >= RP1_PIO_GPIO_COUNT || ++ out_count > RP1_PIO_GPIO_COUNT)) ++ return; ++ ++ c->pinctrl = (c->pinctrl & ~(PROC_PIO_SM0_PINCTRL_OUT_BASE_BITS | ++ PROC_PIO_SM0_PINCTRL_OUT_COUNT_BITS)) | ++ (out_base << PROC_PIO_SM0_PINCTRL_OUT_BASE_LSB) | ++ (out_count << PROC_PIO_SM0_PINCTRL_OUT_COUNT_LSB); ++} ++ ++static inline void sm_config_set_set_pins(pio_sm_config *c, uint set_base, uint set_count) ++{ ++ if (bad_params_if(NULL, set_base >= RP1_PIO_GPIO_COUNT || ++ set_count > 5)) ++ return; ++ ++ c->pinctrl = (c->pinctrl & ~(PROC_PIO_SM0_PINCTRL_SET_BASE_BITS | ++ PROC_PIO_SM0_PINCTRL_SET_COUNT_BITS)) | ++ (set_base << PROC_PIO_SM0_PINCTRL_SET_BASE_LSB) | ++ (set_count << PROC_PIO_SM0_PINCTRL_SET_COUNT_LSB); ++} ++ ++ ++static inline void sm_config_set_in_pins(pio_sm_config *c, uint in_base) ++{ ++ if (bad_params_if(NULL, in_base >= RP1_PIO_GPIO_COUNT)) ++ return; ++ ++ c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_IN_BASE_BITS) | ++ (in_base << PROC_PIO_SM0_PINCTRL_IN_BASE_LSB); ++} ++ ++static inline void sm_config_set_sideset_pins(pio_sm_config *c, uint sideset_base) ++{ ++ if (bad_params_if(NULL, sideset_base >= RP1_PIO_GPIO_COUNT)) ++ return; ++ ++ c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_SIDESET_BASE_BITS) | ++ (sideset_base << PROC_PIO_SM0_PINCTRL_SIDESET_BASE_LSB); ++} ++ ++static inline void sm_config_set_sideset(pio_sm_config *c, uint bit_count, bool optional, ++ bool pindirs) ++{ ++ if (bad_params_if(NULL, bit_count > 5 || ++ (optional && (bit_count == 0)))) ++ return; ++ c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_BITS) | ++ (bit_count << PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_LSB); ++ ++ c->execctrl = (c->execctrl & ~(PROC_PIO_SM0_EXECCTRL_SIDE_EN_BITS | ++ PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_BITS)) | ++ (optional << PROC_PIO_SM0_EXECCTRL_SIDE_EN_LSB) | ++ (pindirs << PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_LSB); ++} ++ ++static inline void sm_config_set_clkdiv_int_frac(pio_sm_config *c, uint16_t div_int, ++ uint8_t div_frac) ++{ ++ if (bad_params_if(NULL, div_int == 0 && div_frac != 0)) ++ return; ++ ++ c->clkdiv = ++ (((uint)div_frac) << PROC_PIO_SM0_CLKDIV_FRAC_LSB) | ++ (((uint)div_int) << PROC_PIO_SM0_CLKDIV_INT_LSB); ++} ++ ++static inline void sm_config_set_clkdiv(pio_sm_config *c, float div) ++{ ++ uint16_t div_int; ++ uint8_t div_frac; ++ ++ pio_calculate_clkdiv_from_float(div, &div_int, &div_frac); ++ sm_config_set_clkdiv_int_frac(c, div_int, div_frac); ++} ++ ++static inline void sm_config_set_wrap(pio_sm_config *c, uint wrap_target, uint wrap) ++{ ++ if (bad_params_if(NULL, wrap >= PIO_INSTRUCTION_COUNT || ++ wrap_target >= PIO_INSTRUCTION_COUNT)) ++ return; ++ ++ c->execctrl = (c->execctrl & ~(PROC_PIO_SM0_EXECCTRL_WRAP_TOP_BITS | ++ PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS)) | ++ (wrap_target << PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB) | ++ (wrap << PROC_PIO_SM0_EXECCTRL_WRAP_TOP_LSB); ++} ++ ++static inline void sm_config_set_jmp_pin(pio_sm_config *c, uint pin) ++{ ++ if (bad_params_if(NULL, pin >= RP1_PIO_GPIO_COUNT)) ++ return; ++ ++ c->execctrl = (c->execctrl & ~PROC_PIO_SM0_EXECCTRL_JMP_PIN_BITS) | ++ (pin << PROC_PIO_SM0_EXECCTRL_JMP_PIN_LSB); ++} ++ ++static inline void sm_config_set_in_shift(pio_sm_config *c, bool shift_right, bool autopush, ++ uint push_threshold) ++{ ++ if (bad_params_if(NULL, push_threshold > 32)) ++ return; ++ ++ c->shiftctrl = (c->shiftctrl & ++ ~(PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_BITS | ++ PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS | ++ PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_BITS)) | ++ (shift_right << PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_LSB) | ++ (autopush << PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_LSB) | ++ ((push_threshold & 0x1fu) << PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB); ++} ++ ++static inline void sm_config_set_out_shift(pio_sm_config *c, bool shift_right, bool autopull, ++ uint pull_threshold) ++{ ++ if (bad_params_if(NULL, pull_threshold > 32)) ++ return; ++ ++ c->shiftctrl = (c->shiftctrl & ++ ~(PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS | ++ PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_BITS | ++ PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS)) | ++ (shift_right << PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB) | ++ (autopull << PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_LSB) | ++ ((pull_threshold & 0x1fu) << PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB); ++} ++ ++static inline void sm_config_set_fifo_join(pio_sm_config *c, enum pio_fifo_join join) ++{ ++ if (bad_params_if(NULL, join != PIO_FIFO_JOIN_NONE && ++ join != PIO_FIFO_JOIN_TX && ++ join != PIO_FIFO_JOIN_RX)) ++ return; ++ ++ c->shiftctrl = (c->shiftctrl & (uint)~(PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS | ++ PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS)) | ++ (((uint)join) << PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB); ++} ++ ++static inline void sm_config_set_out_special(pio_sm_config *c, bool sticky, bool has_enable_pin, ++ uint enable_pin_index) ++{ ++ c->execctrl = (c->execctrl & ++ (uint)~(PROC_PIO_SM0_EXECCTRL_OUT_STICKY_BITS | ++ PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_BITS | ++ PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS)) | ++ (sticky << PROC_PIO_SM0_EXECCTRL_OUT_STICKY_LSB) | ++ (has_enable_pin << PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_LSB) | ++ ((enable_pin_index << PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_LSB) & ++ PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS); ++} ++ ++static inline void sm_config_set_mov_status(pio_sm_config *c, enum pio_mov_status_type status_sel, ++ uint status_n) ++{ ++ if (bad_params_if(NULL, status_sel != STATUS_TX_LESSTHAN && ++ status_sel != STATUS_RX_LESSTHAN)) ++ return; ++ ++ c->execctrl = (c->execctrl ++ & ~(PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS | PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS)) ++ | ((((uint)status_sel) << PROC_PIO_SM0_EXECCTRL_STATUS_SEL_LSB) & ++ PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS) ++ | ((status_n << PROC_PIO_SM0_EXECCTRL_STATUS_N_LSB) & ++ PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS); ++} ++ ++static inline pio_sm_config pio_get_default_sm_config(void) ++{ ++ pio_sm_config c = { 0 }; ++ ++ sm_config_set_clkdiv_int_frac(&c, 1, 0); ++ sm_config_set_wrap(&c, 0, 31); ++ sm_config_set_in_shift(&c, true, false, 32); ++ sm_config_set_out_shift(&c, true, false, 32); ++ return c; ++} ++ ++static inline uint32_t clock_get_hz(enum clock_index clk_index) ++{ ++ const uint32_t MHZ = 1000000; ++ ++ if (bad_params_if(NULL, clk_index != clk_sys)) ++ return 0; ++ return 200 * MHZ; ++} ++ ++static inline int pio_gpio_set_function(struct rp1_pio_client *client, uint gpio, ++ enum gpio_function fn) ++{ ++ struct rp1_gpio_set_function_args args = { .gpio = gpio, .fn = fn }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_function(client, &args); ++} ++ ++static inline int pio_gpio_init(struct rp1_pio_client *client, uint gpio) ++{ ++ struct rp1_gpio_init_args args = { .gpio = gpio }; ++ int ret; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ ret = rp1_pio_gpio_init(client, &args); ++ if (ret) ++ return ret; ++ return pio_gpio_set_function(client, gpio, RP1_GPIO_FUNC_PIO); ++} ++ ++static inline int pio_gpio_set_pulls(struct rp1_pio_client *client, uint gpio, bool up, bool down) ++{ ++ struct rp1_gpio_set_pulls_args args = { .gpio = gpio, .up = up, .down = down }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_pulls(client, &args); ++} ++ ++static inline int pio_gpio_set_outover(struct rp1_pio_client *client, uint gpio, uint value) ++{ ++ struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_outover(client, &args); ++} ++ ++static inline int pio_gpio_set_inover(struct rp1_pio_client *client, uint gpio, uint value) ++{ ++ struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_inover(client, &args); ++} ++ ++static inline int pio_gpio_set_oeover(struct rp1_pio_client *client, uint gpio, uint value) ++{ ++ struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_oeover(client, &args); ++} ++ ++static inline int pio_gpio_set_input_enabled(struct rp1_pio_client *client, uint gpio, ++ bool enabled) ++{ ++ struct rp1_gpio_set_args args = { .gpio = gpio, .value = enabled }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_input_enabled(client, &args); ++} ++ ++static inline int pio_gpio_set_drive_strength(struct rp1_pio_client *client, uint gpio, ++ enum gpio_drive_strength drive) ++{ ++ struct rp1_gpio_set_args args = { .gpio = gpio, .value = drive }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_drive_strength(client, &args); ++} ++ ++static inline int pio_gpio_pull_up(struct rp1_pio_client *client, uint gpio) ++{ ++ return pio_gpio_set_pulls(client, gpio, true, false); ++} ++ ++static inline int pio_gpio_pull_down(struct rp1_pio_client *client, uint gpio) ++{ ++ return pio_gpio_set_pulls(client, gpio, false, true); ++} ++ ++static inline int pio_gpio_disable_pulls(struct rp1_pio_client *client, uint gpio) ++{ ++ return pio_gpio_set_pulls(client, gpio, false, false); ++} ++ ++#endif diff --git a/target/linux/bcm27xx/patches-6.6/950-1389-pwm-Add-pwm-pio-rp1-driver.patch b/target/linux/bcm27xx/patches-6.6/950-1389-pwm-Add-pwm-pio-rp1-driver.patch new file mode 100644 index 0000000000..9eed5f568d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1389-pwm-Add-pwm-pio-rp1-driver.patch @@ -0,0 +1,299 @@ +From 4d20aadc3188ecfb62b309a9924ee9696a94fc33 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 8 Nov 2024 09:37:58 +0000 +Subject: [PATCH] pwm: Add pwm-pio-rp1 driver + +Use the PIO hardware on RP1 to implement a PWM interface. + +Signed-off-by: Phil Elwell +--- + drivers/pwm/Kconfig | 11 ++ + drivers/pwm/Makefile | 1 + + drivers/pwm/pwm-pio-rp1.c | 251 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 263 insertions(+) + create mode 100644 drivers/pwm/pwm-pio-rp1.c + +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -465,6 +465,17 @@ config PWM_PCA9685 + To compile this driver as a module, choose M here: the module + will be called pwm-pca9685. + ++config PWM_PIO_RP1 ++ tristate "RP1 PIO PWM support" ++ depends on FIRMWARE_RP1 || COMPILE_TEST ++ help ++ This is a PWM framework driver for Raspberry Pi 5, using the PIO ++ hardware of RP1 to provide PWM functionality. Supports up to 4 ++ instances on GPIOs in bank 0. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-pio-rp1. ++ + config PWM_PXA + tristate "PXA PWM support" + depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -42,6 +42,7 @@ obj-$(CONFIG_PWM_MXS) += pwm-mxs.o + obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o + obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o + obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o ++obj-$(CONFIG_PWM_PIO_RP1) += pwm-pio-rp1.o + obj-$(CONFIG_PWM_PXA) += pwm-pxa.o + obj-$(CONFIG_PWM_RASPBERRYPI_POE) += pwm-raspberrypi-poe.o + obj-$(CONFIG_PWM_RP1) += pwm-rp1.o +--- /dev/null ++++ b/drivers/pwm/pwm-pio-rp1.c +@@ -0,0 +1,251 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Raspberry Pi PIO PWM. ++ * ++ * Copyright (C) 2024 Raspberry Pi Ltd. ++ * ++ * Author: Phil Elwell (phil@raspberrypi.com) ++ * ++ * Based on the pwm-rp1 driver by: ++ * Naushir Patuck ++ * and on the pwm-gpio driver by: ++ * Vincent Whitchurch ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct pwm_pio_rp1 { ++ struct pwm_chip chip; ++ struct device *dev; ++ struct gpio_desc *gpiod; ++ struct mutex mutex; ++ PIO pio; ++ uint sm; ++ uint offset; ++ uint gpio; ++ uint32_t period; /* In SM cycles */ ++ uint32_t duty_cycle; /* In SM cycles */ ++ enum pwm_polarity polarity; ++ bool enabled; ++}; ++ ++/* Generated from pwm.pio by pioasm */ ++#define pwm_wrap_target 0 ++#define pwm_wrap 6 ++#define pwm_loop_ticks 3 ++ ++static const uint16_t pwm_program_instructions[] = { ++ // .wrap_target ++ 0x9080, // 0: pull noblock side 0 ++ 0xa027, // 1: mov x, osr ++ 0xa046, // 2: mov y, isr ++ 0x00a5, // 3: jmp x != y, 5 ++ 0x1806, // 4: jmp 6 side 1 ++ 0xa042, // 5: nop ++ 0x0083, // 6: jmp y--, 3 ++ // .wrap ++}; ++ ++static const struct pio_program pwm_program = { ++ .instructions = pwm_program_instructions, ++ .length = 7, ++ .origin = -1, ++}; ++ ++static unsigned int pwm_pio_resolution __read_mostly; ++ ++static inline pio_sm_config pwm_program_get_default_config(uint offset) ++{ ++ pio_sm_config c = pio_get_default_sm_config(); ++ ++ sm_config_set_wrap(&c, offset + pwm_wrap_target, offset + pwm_wrap); ++ sm_config_set_sideset(&c, 2, true, false); ++ return c; ++} ++ ++static inline void pwm_program_init(PIO pio, uint sm, uint offset, uint pin) ++{ ++ pio_gpio_init(pio, pin); ++ ++ pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); ++ pio_sm_config c = pwm_program_get_default_config(offset); ++ ++ sm_config_set_sideset_pins(&c, pin); ++ pio_sm_init(pio, sm, offset, &c); ++} ++ ++/* Write `period` to the input shift register - must be disabled */ ++static void pio_pwm_set_period(PIO pio, uint sm, uint32_t period) ++{ ++ pio_sm_put_blocking(pio, sm, period); ++ pio_sm_exec(pio, sm, pio_encode_pull(false, false)); ++ pio_sm_exec(pio, sm, pio_encode_out(pio_isr, 32)); ++} ++ ++/* Write `level` to TX FIFO. State machine will copy this into X. */ ++static void pio_pwm_set_level(PIO pio, uint sm, uint32_t level) ++{ ++ pio_sm_put_blocking(pio, sm, level); ++} ++ ++static int pwm_pio_rp1_apply(struct pwm_chip *chip, struct pwm_device *pwm, ++ const struct pwm_state *state) ++{ ++ struct pwm_pio_rp1 *ppwm = container_of(chip, struct pwm_pio_rp1, chip); ++ uint32_t new_duty_cycle; ++ uint32_t new_period; ++ ++ if (state->duty_cycle && state->duty_cycle < pwm_pio_resolution) ++ return -EINVAL; ++ ++ if (state->duty_cycle != state->period && ++ (state->period - state->duty_cycle < pwm_pio_resolution)) ++ return -EINVAL; ++ ++ new_period = state->period / pwm_pio_resolution; ++ new_duty_cycle = state->duty_cycle / pwm_pio_resolution; ++ ++ mutex_lock(&ppwm->mutex); ++ ++ if ((ppwm->enabled && !state->enabled) || new_period != ppwm->period) { ++ pio_sm_set_enabled(ppwm->pio, ppwm->sm, false); ++ ppwm->enabled = false; ++ } ++ ++ if (new_period != ppwm->period) { ++ pio_pwm_set_period(ppwm->pio, ppwm->sm, new_period); ++ ppwm->period = new_period; ++ } ++ ++ if (state->enabled && new_duty_cycle != ppwm->duty_cycle) { ++ pio_pwm_set_level(ppwm->pio, ppwm->sm, new_duty_cycle); ++ ppwm->duty_cycle = new_duty_cycle; ++ } ++ ++ if (state->polarity != ppwm->polarity) { ++ pio_gpio_set_outover(ppwm->pio, ppwm->gpio, ++ (state->polarity == PWM_POLARITY_INVERSED) ? ++ GPIO_OVERRIDE_INVERT : GPIO_OVERRIDE_NORMAL); ++ ppwm->polarity = state->polarity; ++ } ++ ++ if (!ppwm->enabled && state->enabled) { ++ pio_sm_set_enabled(ppwm->pio, ppwm->sm, true); ++ ppwm->enabled = true; ++ } ++ ++ mutex_unlock(&ppwm->mutex); ++ ++ return 0; ++} ++ ++static const struct pwm_ops pwm_pio_rp1_ops = { ++ .apply = pwm_pio_rp1_apply, ++}; ++ ++static int pwm_pio_rp1_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct of_phandle_args of_args = { 0 }; ++ struct device *dev = &pdev->dev; ++ struct pwm_pio_rp1 *ppwm; ++ struct pwm_chip *chip; ++ bool is_rp1; ++ ++ ppwm = devm_kzalloc(dev, sizeof(*ppwm), GFP_KERNEL); ++ if (IS_ERR(ppwm)) ++ return PTR_ERR(ppwm); ++ ++ chip = &ppwm->chip; ++ ++ mutex_init(&ppwm->mutex); ++ ++ ppwm->gpiod = devm_gpiod_get(dev, NULL, GPIOD_ASIS); ++ /* Need to check that this is an RP1 GPIO in the first bank, and retrieve the offset */ ++ /* Unfortunately I think this has to be done by parsing the gpios property */ ++ if (IS_ERR(ppwm->gpiod)) ++ return dev_err_probe(dev, PTR_ERR(ppwm->gpiod), ++ "could not get a gpio\n"); ++ ++ /* This really shouldn't fail, given that we have a gpiod */ ++ if (of_parse_phandle_with_args(np, "gpios", "#gpio-cells", 0, &of_args)) ++ return dev_err_probe(dev, -EINVAL, ++ "can't find gpio declaration\n"); ++ ++ is_rp1 = of_device_is_compatible(of_args.np, "raspberrypi,rp1-gpio"); ++ of_node_put(of_args.np); ++ if (!is_rp1 || of_args.args_count != 2) ++ return dev_err_probe(dev, -EINVAL, ++ "not an RP1 gpio\n"); ++ ++ ppwm->gpio = of_args.args[0]; ++ ++ ppwm->pio = pio_open(); ++ if (IS_ERR(ppwm->pio)) ++ return dev_err_probe(dev, PTR_ERR(ppwm->pio), ++ "%pfw: could not open PIO\n", ++ dev_fwnode(dev)); ++ ++ ppwm->sm = pio_claim_unused_sm(ppwm->pio, false); ++ if ((int)ppwm->sm < 0) { ++ pio_close(ppwm->pio); ++ return dev_err_probe(dev, -EBUSY, ++ "%pfw: no free PIO SM\n", ++ dev_fwnode(dev)); ++ } ++ ++ ppwm->offset = pio_add_program(ppwm->pio, &pwm_program); ++ if (ppwm->offset == PIO_ORIGIN_ANY) { ++ pio_close(ppwm->pio); ++ return dev_err_probe(dev, -EBUSY, ++ "%pfw: not enough PIO program space\n", ++ dev_fwnode(dev)); ++ } ++ ++ pwm_program_init(ppwm->pio, ppwm->sm, ppwm->offset, ppwm->gpio); ++ ++ pwm_pio_resolution = (1000u * 1000 * 1000 * pwm_loop_ticks) / clock_get_hz(clk_sys); ++ ++ chip->dev = dev; ++ chip->ops = &pwm_pio_rp1_ops; ++ chip->atomic = true; ++ chip->npwm = 1; ++ ++ platform_set_drvdata(pdev, ppwm); ++ ++ return devm_pwmchip_add(dev, chip); ++} ++ ++static void pwm_pio_rp1_remove(struct platform_device *pdev) ++{ ++ struct pwm_pio_rp1 *ppwm = platform_get_drvdata(pdev); ++ ++ pio_close(ppwm->pio); ++} ++ ++static const struct of_device_id pwm_pio_rp1_dt_ids[] = { ++ { .compatible = "raspberrypi,pwm-pio-rp1" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, pwm_pio_rp1_dt_ids); ++ ++static struct platform_driver pwm_pio_rp1_driver = { ++ .driver = { ++ .name = "pwm-pio-rp1", ++ .of_match_table = pwm_pio_rp1_dt_ids, ++ }, ++ .probe = pwm_pio_rp1_probe, ++ .remove_new = pwm_pio_rp1_remove, ++}; ++module_platform_driver(pwm_pio_rp1_driver); ++ ++MODULE_DESCRIPTION("PWM PIO RP1 driver"); ++MODULE_AUTHOR("Phil Elwell"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/bcm27xx/patches-6.6/950-1391-overlays-Add-pwm-pio-overlay.patch b/target/linux/bcm27xx/patches-6.6/950-1391-overlays-Add-pwm-pio-overlay.patch new file mode 100644 index 0000000000..bd6d633319 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1391-overlays-Add-pwm-pio-overlay.patch @@ -0,0 +1,99 @@ +From ba7e2e3d03a432acbc338c6c03e46dcd97cfa1b3 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 7 Nov 2024 11:41:33 +0000 +Subject: [PATCH] overlays: Add pwm-pio overlay + +Add an overlay to enable a single-channel PIO-assisted PWM interface on any +header pin. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 8 ++++ + arch/arm/boot/dts/overlays/overlay_map.dts | 4 ++ + .../arm/boot/dts/overlays/pwm-pio-overlay.dts | 39 +++++++++++++++++++ + 4 files changed, 52 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/pwm-pio-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -219,6 +219,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + pwm-2chan.dtbo \ + pwm-gpio.dtbo \ + pwm-ir-tx.dtbo \ ++ pwm-pio.dtbo \ + pwm1.dtbo \ + qca7000.dtbo \ + qca7000-uart0.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -3926,6 +3926,14 @@ Params: gpio_pin Output G + func Pin function (default 2 = Alt5) + + ++Name: pwm-pio ++Info: Configures a GPIO pin as PIO-assisted PWM output. Unlike hardware PWM, ++ this can be used on any RP1 GPIO in bank 0 (0-27). Up to 4 are ++ supported, assuming nothing else is using PIO. Pi 5 only. ++Load: dtoverlay=pwm-pio,= ++Params: gpio Output GPIO (0-27, default 4) ++ ++ + Name: pwm1 + Info: Configures one or two PWM channel on PWM1 (BCM2711 only) + N.B.: +--- a/arch/arm/boot/dts/overlays/overlay_map.dts ++++ b/arch/arm/boot/dts/overlays/overlay_map.dts +@@ -240,6 +240,10 @@ + bcm2712; + }; + ++ pwm-pio { ++ bcm2712; ++ }; ++ + pwm1 { + bcm2711; + }; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pwm-pio-overlay.dts +@@ -0,0 +1,39 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Device tree overlay for RP1 PIO PWM. ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2712"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ pwm_pio_pins: pwm_pio_pins@4 { ++ brcm,pins = <4>; /* gpio 4 */ ++ function = "pio"; ++ bias-disable; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target-path = "/"; ++ __overlay__ { ++ pwm_pio: pwm_pio@4 { ++ compatible = "raspberrypi,pwm-pio-rp1"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_pio_pins>; ++ gpios = <&gpio 4 0>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ gpio = <&pwm_pio>,"gpios:4", ++ <&pwm_pio_pins>,"brcm,pins:0", ++ /* modify reg values to allow multiple instantiation */ ++ <&pwm_pio>,"reg:0", ++ <&pwm_pio_pins>,"reg:0"; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-6.6/950-1392-fixup-misc-Add-RP1-PIO-driver.patch b/target/linux/bcm27xx/patches-6.6/950-1392-fixup-misc-Add-RP1-PIO-driver.patch new file mode 100644 index 0000000000..cd197f7a7b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1392-fixup-misc-Add-RP1-PIO-driver.patch @@ -0,0 +1,29 @@ +From 1b5acd42281ad102b79f4e1794f0a0cccdafda05 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Sat, 16 Nov 2024 16:53:31 +0000 +Subject: [PATCH] fixup! misc: Add RP1 PIO driver + +Signed-off-by: Phil Elwell +--- + include/uapi/misc/rp1_pio_if.h | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +--- a/include/uapi/misc/rp1_pio_if.h ++++ b/include/uapi/misc/rp1_pio_if.h +@@ -1,4 +1,4 @@ +-/* SPDX-License-Identifier: GPL-2.0 */ ++/* SPDX-License-Identifier: GPL-2.0 + WITH Linux-syscall-note */ + /* + * Copyright (c) 2023-24 Raspberry Pi Ltd. + * All rights reserved. +@@ -169,10 +169,6 @@ struct rp1_access_hw_args { + #define PIO_IOC_SM_CONFIG_XFER _IOW(PIO_IOC_MAGIC, 0, struct rp1_pio_sm_config_xfer_args) + #define PIO_IOC_SM_XFER_DATA _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args) + +-#ifdef CONFIG_COMPAT +-//XXX #define PIO_IOC_SM_XFER_DATA32 _IOW(PIO_IOC_MAGIC, 2, struct pio_sm_xfer_data_args) +-#endif +- + #define PIO_IOC_READ_HW _IOW(PIO_IOC_MAGIC, 8, struct rp1_access_hw_args) + #define PIO_IOC_WRITE_HW _IOW(PIO_IOC_MAGIC, 9, struct rp1_access_hw_args) + diff --git a/target/linux/bcm27xx/patches-6.6/950-1394-misc-rp1-pio-Add-compat_ioctl-method.patch b/target/linux/bcm27xx/patches-6.6/950-1394-misc-rp1-pio-Add-compat_ioctl-method.patch index bffa2418a1..6862a74e21 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1394-misc-rp1-pio-Add-compat_ioctl-method.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1394-misc-rp1-pio-Add-compat_ioctl-method.patch @@ -1,7 +1,7 @@ From b4472d09b1ffdafd8132803ffbec62596e559fd8 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Mon, 18 Nov 2024 09:10:52 +0000 -Subject: [PATCH 1394/1482] misc: rp1-pio: Add compat_ioctl method +Subject: [PATCH] misc: rp1-pio: Add compat_ioctl method Provide a compat_ioctl method, to support running a 64-bit kernel with a 32-bit userland. @@ -13,7 +13,7 @@ Signed-off-by: Phil Elwell --- a/drivers/misc/rp1-pio.c +++ b/drivers/misc/rp1-pio.c -@@ -1023,11 +1023,75 @@ static long rp1_pio_ioctl(struct file *f +@@ -996,11 +996,75 @@ static long rp1_pio_ioctl(struct file *f return ret; } diff --git a/target/linux/bcm27xx/patches-6.6/950-1396-i2c-designware-Add-support-for-bus-clear-feature.patch b/target/linux/bcm27xx/patches-6.6/950-1396-i2c-designware-Add-support-for-bus-clear-feature.patch new file mode 100644 index 0000000000..bc20db253a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1396-i2c-designware-Add-support-for-bus-clear-feature.patch @@ -0,0 +1,157 @@ +From 0e4968617aad7d0f88e0a630499202eaae407a19 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 26 Mar 2024 15:57:46 +0000 +Subject: [PATCH] i2c: designware: Add support for bus clear feature + +Newer versions of the DesignWare I2C block support the detection of +stuck signals, and a mechanism to recover from them. Add the required +software support to the driver. + +This change was prompted by the observation that reading a single byte +from register 0 of a VEML7700 seems to cause it to issue an ACK too +early, and the controller to complain about losing arbitration. There +is a suspicion that this may be a more widespread problem, but at least +this patch prevents the bus from locking up. + +See: https://github.com/raspberrypi/linux/issues/6057 + +Signed-off-by: Phil Elwell +--- + drivers/i2c/busses/i2c-designware-common.c | 12 ++++++++++++ + drivers/i2c/busses/i2c-designware-core.h | 8 ++++++++ + drivers/i2c/busses/i2c-designware-master.c | 19 ++++++++++++++++++- + 3 files changed, 38 insertions(+), 1 deletion(-) + +--- a/drivers/i2c/busses/i2c-designware-common.c ++++ b/drivers/i2c/busses/i2c-designware-common.c +@@ -57,6 +57,8 @@ static char *abort_sources[] = { + "slave lost the bus while transmitting data to a remote master", + [ABRT_SLAVE_RD_INTX] = + "incorrect slave-transmitter mode configuration", ++ [ABRT_SLAVE_SDA_STUCK_AT_LOW] = ++ "SDA stuck at low", + }; + + static int dw_reg_read(void *context, unsigned int reg, unsigned int *val) +@@ -609,8 +611,16 @@ int i2c_dw_wait_bus_not_busy(struct dw_i + int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev) + { + unsigned long abort_source = dev->abort_source; ++ unsigned int reg; + int i; + ++ if (abort_source & DW_IC_TX_ABRT_SLAVE_SDA_STUCK_AT_LOW) { ++ regmap_write(dev->map, DW_IC_ENABLE, ++ DW_IC_ENABLE_ENABLE | DW_IC_ENABLE_BUS_RECOVERY); ++ regmap_read_poll_timeout(dev->map, DW_IC_ENABLE, reg, ++ !(reg & DW_IC_ENABLE_BUS_RECOVERY), ++ 1100, 200000); ++ } + if (abort_source & DW_IC_TX_ABRT_NOACK) { + for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) + dev_dbg(dev->dev, +@@ -625,6 +635,8 @@ int i2c_dw_handle_tx_abort(struct dw_i2c + return -EAGAIN; + else if (abort_source & DW_IC_TX_ABRT_GCALL_READ) + return -EINVAL; /* wrong msgs[] data */ ++ else if (abort_source & DW_IC_TX_ABRT_SLAVE_SDA_STUCK_AT_LOW) ++ return -EREMOTEIO; + else + return -EIO; + } +--- a/drivers/i2c/busses/i2c-designware-core.h ++++ b/drivers/i2c/busses/i2c-designware-core.h +@@ -79,9 +79,12 @@ + #define DW_IC_TX_ABRT_SOURCE 0x80 + #define DW_IC_ENABLE_STATUS 0x9c + #define DW_IC_CLR_RESTART_DET 0xa8 ++#define DW_IC_SCL_STUCK_AT_LOW_TIMEOUT 0xac ++#define DW_IC_SDA_STUCK_AT_LOW_TIMEOUT 0xb0 + #define DW_IC_COMP_PARAM_1 0xf4 + #define DW_IC_COMP_VERSION 0xf8 + #define DW_IC_SDA_HOLD_MIN_VERS 0x3131312A /* "111*" == v1.11* */ ++#define DW_IC_BUS_CLEAR_MIN_VERS 0x3230302A /* "200*" == v2.00* */ + #define DW_IC_COMP_TYPE 0xfc + #define DW_IC_COMP_TYPE_VALUE 0x44570140 /* "DW" + 0x0140 */ + +@@ -111,6 +114,7 @@ + + #define DW_IC_ENABLE_ENABLE BIT(0) + #define DW_IC_ENABLE_ABORT BIT(1) ++#define DW_IC_ENABLE_BUS_RECOVERY BIT(3) + + #define DW_IC_STATUS_ACTIVITY BIT(0) + #define DW_IC_STATUS_TFE BIT(2) +@@ -118,6 +122,7 @@ + #define DW_IC_STATUS_MASTER_ACTIVITY BIT(5) + #define DW_IC_STATUS_SLAVE_ACTIVITY BIT(6) + #define DW_IC_STATUS_MASTER_HOLD_TX_FIFO_EMPTY BIT(7) ++#define DW_IC_STATUS_SDA_STUCK_NOT_RECOVERED BIT(11) + + #define DW_IC_SDA_HOLD_RX_SHIFT 16 + #define DW_IC_SDA_HOLD_RX_MASK GENMASK(23, 16) +@@ -165,6 +170,7 @@ + #define ABRT_SLAVE_FLUSH_TXFIFO 13 + #define ABRT_SLAVE_ARBLOST 14 + #define ABRT_SLAVE_RD_INTX 15 ++#define ABRT_SLAVE_SDA_STUCK_AT_LOW 17 + + #define DW_IC_TX_ABRT_7B_ADDR_NOACK BIT(ABRT_7B_ADDR_NOACK) + #define DW_IC_TX_ABRT_10ADDR1_NOACK BIT(ABRT_10ADDR1_NOACK) +@@ -180,6 +186,7 @@ + #define DW_IC_RX_ABRT_SLAVE_RD_INTX BIT(ABRT_SLAVE_RD_INTX) + #define DW_IC_RX_ABRT_SLAVE_ARBLOST BIT(ABRT_SLAVE_ARBLOST) + #define DW_IC_RX_ABRT_SLAVE_FLUSH_TXFIFO BIT(ABRT_SLAVE_FLUSH_TXFIFO) ++#define DW_IC_TX_ABRT_SLAVE_SDA_STUCK_AT_LOW BIT(ABRT_SLAVE_SDA_STUCK_AT_LOW) + + #define DW_IC_TX_ABRT_NOACK (DW_IC_TX_ABRT_7B_ADDR_NOACK | \ + DW_IC_TX_ABRT_10ADDR1_NOACK | \ +--- a/drivers/i2c/busses/i2c-designware-master.c ++++ b/drivers/i2c/busses/i2c-designware-master.c +@@ -215,6 +215,7 @@ static int i2c_dw_set_timings_master(str + */ + static int i2c_dw_init_master(struct dw_i2c_dev *dev) + { ++ unsigned int timeout = 0; + int ret; + + ret = i2c_dw_acquire_lock(dev); +@@ -238,6 +239,17 @@ static int i2c_dw_init_master(struct dw_ + regmap_write(dev->map, DW_IC_HS_SCL_LCNT, dev->hs_lcnt); + } + ++ if (dev->master_cfg & DW_IC_CON_BUS_CLEAR_CTRL) { ++ /* Set a sensible timeout if not already configured */ ++ regmap_read(dev->map, DW_IC_SDA_STUCK_AT_LOW_TIMEOUT, &timeout); ++ if (timeout == ~0) { ++ /* Use 10ms as a timeout, which is 1000 cycles at 100kHz */ ++ timeout = i2c_dw_clk_rate(dev) * 10; /* clock rate is in kHz */ ++ regmap_write(dev->map, DW_IC_SDA_STUCK_AT_LOW_TIMEOUT, timeout); ++ regmap_write(dev->map, DW_IC_SCL_STUCK_AT_LOW_TIMEOUT, timeout); ++ } ++ } ++ + /* Write SDA hold time if supported */ + if (dev->sda_hold_time) + regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time); +@@ -1074,6 +1086,7 @@ int i2c_dw_probe_master(struct dw_i2c_de + struct i2c_adapter *adap = &dev->adapter; + unsigned long irq_flags; + unsigned int ic_con; ++ unsigned int id_ver; + int ret; + + init_completion(&dev->cmd_complete); +@@ -1109,7 +1122,11 @@ int i2c_dw_probe_master(struct dw_i2c_de + if (ret) + return ret; + +- if (ic_con & DW_IC_CON_BUS_CLEAR_CTRL) ++ ret = regmap_read(dev->map, DW_IC_COMP_VERSION, &id_ver); ++ if (ret) ++ return ret; ++ ++ if (ic_con & DW_IC_CON_BUS_CLEAR_CTRL || id_ver >= DW_IC_BUS_CLEAR_MIN_VERS) + dev->master_cfg |= DW_IC_CON_BUS_CLEAR_CTRL; + + ret = dev->init(dev); diff --git a/target/linux/bcm27xx/patches-6.6/950-1397-drivers-media-pci-Update-Hailo-accelerator-device-dr.patch b/target/linux/bcm27xx/patches-6.6/950-1397-drivers-media-pci-Update-Hailo-accelerator-device-dr.patch new file mode 100644 index 0000000000..3ad28d6886 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1397-drivers-media-pci-Update-Hailo-accelerator-device-dr.patch @@ -0,0 +1,3592 @@ +From 32511f035b086bca254d8adab234cef3541492b4 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +Date: Thu, 17 Oct 2024 11:37:29 +0100 +Subject: [PATCH] drivers: media: pci: Update Hailo accelerator device driver + to v4.19 + +Sourced from https://github.com/hailo-ai/hailort-drivers/ + +Signed-off-by: Naushir Patuck +--- + drivers/media/pci/hailo/Makefile | 4 +- + drivers/media/pci/hailo/common/fw_operation.c | 50 ++- + drivers/media/pci/hailo/common/fw_operation.h | 8 +- + .../media/pci/hailo/common/fw_validation.c | 10 +- + .../media/pci/hailo/common/fw_validation.h | 7 +- + .../pci/hailo/common/hailo_ioctl_common.h | 28 +- + .../media/pci/hailo/common/hailo_resource.c | 23 +- + .../media/pci/hailo/common/hailo_resource.h | 2 +- + drivers/media/pci/hailo/common/pcie_common.c | 380 +++++++++--------- + drivers/media/pci/hailo/common/pcie_common.h | 38 +- + drivers/media/pci/hailo/common/soc_structs.h | 79 ++++ + drivers/media/pci/hailo/common/utils.h | 23 +- + drivers/media/pci/hailo/common/vdma_common.c | 93 +++-- + drivers/media/pci/hailo/common/vdma_common.h | 22 +- + drivers/media/pci/hailo/src/fops.c | 284 ++----------- + drivers/media/pci/hailo/src/fops.h | 5 +- + drivers/media/pci/hailo/src/nnc.c | 299 ++++++++++++++ + drivers/media/pci/hailo/src/nnc.h | 22 + + drivers/media/pci/hailo/src/pci_soc_ioctl.c | 155 ------- + drivers/media/pci/hailo/src/pcie.c | 166 +++----- + drivers/media/pci/hailo/src/pcie.h | 26 +- + drivers/media/pci/hailo/src/soc.c | 244 +++++++++++ + .../pci/hailo/src/{pci_soc_ioctl.h => soc.h} | 13 +- + drivers/media/pci/hailo/src/sysfs.c | 2 +- + drivers/media/pci/hailo/src/sysfs.h | 2 +- + drivers/media/pci/hailo/src/utils.c | 26 -- + drivers/media/pci/hailo/utils/compact.h | 2 +- + drivers/media/pci/hailo/utils/fw_common.h | 2 +- + .../pci/hailo/utils/integrated_nnc_utils.c | 10 +- + .../pci/hailo/utils/integrated_nnc_utils.h | 2 +- + drivers/media/pci/hailo/utils/logs.c | 2 +- + drivers/media/pci/hailo/utils/logs.h | 2 +- + drivers/media/pci/hailo/vdma/ioctl.c | 18 +- + drivers/media/pci/hailo/vdma/ioctl.h | 6 +- + drivers/media/pci/hailo/vdma/memory.c | 12 +- + drivers/media/pci/hailo/vdma/memory.h | 2 +- + drivers/media/pci/hailo/vdma/vdma.c | 39 +- + drivers/media/pci/hailo/vdma/vdma.h | 5 +- + 38 files changed, 1224 insertions(+), 889 deletions(-) + create mode 100644 drivers/media/pci/hailo/common/soc_structs.h + create mode 100644 drivers/media/pci/hailo/src/nnc.c + create mode 100644 drivers/media/pci/hailo/src/nnc.h + delete mode 100755 drivers/media/pci/hailo/src/pci_soc_ioctl.c + create mode 100644 drivers/media/pci/hailo/src/soc.c + rename drivers/media/pci/hailo/src/{pci_soc_ioctl.h => soc.h} (53%) + mode change 100755 => 100644 + delete mode 100644 drivers/media/pci/hailo/src/utils.c + +--- a/drivers/media/pci/hailo/Makefile ++++ b/drivers/media/pci/hailo/Makefile +@@ -8,9 +8,9 @@ obj-$(CONFIG_MEDIA_PCI_HAILO) := hailo_p + + hailo_pci-objs += src/pcie.o + hailo_pci-objs += src/fops.o +-hailo_pci-objs += src/utils.o + hailo_pci-objs += src/sysfs.o +-hailo_pci-objs += src/pci_soc_ioctl.o ++hailo_pci-objs += src/nnc.o ++hailo_pci-objs += src/soc.o + + hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/fw_validation.o + hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/fw_operation.o +--- a/drivers/media/pci/hailo/common/fw_operation.c ++++ b/drivers/media/pci/hailo/common/fw_operation.c +@@ -1,7 +1,7 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved. +-**/ ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ + + #include "fw_operation.h" + +@@ -15,7 +15,10 @@ typedef struct { + u32 chip_offset; + } FW_DEBUG_BUFFER_HEADER_t; + +-#define DEBUG_BUFFER_DATA_SIZE (DEBUG_BUFFER_TOTAL_SIZE - sizeof(FW_DEBUG_BUFFER_HEADER_t)) ++#define DEBUG_BUFFER_DATA_SIZE (DEBUG_BUFFER_TOTAL_SIZE - sizeof(FW_DEBUG_BUFFER_HEADER_t)) ++#define PCIE_D2H_NOTIFICATION_SRAM_OFFSET (0x640 + 0x640) ++#define PCIE_APP_CPU_DEBUG_OFFSET (8*1024) ++#define PCIE_CORE_CPU_DEBUG_OFFSET (PCIE_APP_CPU_DEBUG_OFFSET + DEBUG_BUFFER_TOTAL_SIZE) + + int hailo_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification) + { +@@ -35,6 +38,21 @@ int hailo_read_firmware_notification(str + return 0; + } + ++int hailo_pcie_read_firmware_notification(struct hailo_resource *resource, ++ struct hailo_d2h_notification *notification) ++{ ++ struct hailo_resource notification_resource; ++ ++ if (PCIE_D2H_NOTIFICATION_SRAM_OFFSET > resource->size) { ++ return -EINVAL; ++ } ++ ++ notification_resource.address = resource->address + PCIE_D2H_NOTIFICATION_SRAM_OFFSET, ++ notification_resource.size = sizeof(struct hailo_d2h_notification); ++ ++ return hailo_read_firmware_notification(¬ification_resource, notification); ++} ++ + static inline size_t calculate_log_ready_to_read(FW_DEBUG_BUFFER_HEADER_t *header) + { + size_t ready_to_read = 0; +@@ -100,4 +118,30 @@ long hailo_read_firmware_log(struct hail + + params->read_bytes = ready_to_read; + return 0; ++} ++ ++long hailo_pcie_read_firmware_log(struct hailo_resource *resource, struct hailo_read_log_params *params) ++{ ++ long err = 0; ++ struct hailo_resource log_resource = {resource->address, DEBUG_BUFFER_TOTAL_SIZE}; ++ ++ if (HAILO_CPU_ID_CPU0 == params->cpu_id) { ++ log_resource.address += PCIE_APP_CPU_DEBUG_OFFSET; ++ } else if (HAILO_CPU_ID_CPU1 == params->cpu_id) { ++ log_resource.address += PCIE_CORE_CPU_DEBUG_OFFSET; ++ } else { ++ return -EINVAL; ++ } ++ ++ if (0 == params->buffer_size) { ++ params->read_bytes = 0; ++ return 0; ++ } ++ ++ err = hailo_read_firmware_log(&log_resource, params); ++ if (0 != err) { ++ return err; ++ } ++ ++ return 0; + } +\ No newline at end of file +--- a/drivers/media/pci/hailo/common/fw_operation.h ++++ b/drivers/media/pci/hailo/common/fw_operation.h +@@ -1,7 +1,7 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved. +-**/ ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ + + #ifndef _HAILO_COMMON_FIRMWARE_OPERATION_H_ + #define _HAILO_COMMON_FIRMWARE_OPERATION_H_ +@@ -16,8 +16,12 @@ extern "C" { + + int hailo_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification); + ++int hailo_pcie_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification); ++ + long hailo_read_firmware_log(struct hailo_resource *fw_logger_resource, struct hailo_read_log_params *params); + ++long hailo_pcie_read_firmware_log(struct hailo_resource *resource, struct hailo_read_log_params *params); ++ + #ifdef __cplusplus + } + #endif +--- a/drivers/media/pci/hailo/common/fw_validation.c ++++ b/drivers/media/pci/hailo/common/fw_validation.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "fw_validation.h" +@@ -85,15 +85,15 @@ exit: + } + + int FW_VALIDATION__validate_cert_header(uintptr_t firmware_base_address, +- size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_t **out_firmware_cert) ++ size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_header_t **out_firmware_cert) + { + +- secure_boot_certificate_t *firmware_cert = NULL; ++ secure_boot_certificate_header_t *firmware_cert = NULL; + int err = -EINVAL; + u32 consumed_firmware_offset = *outer_consumed_firmware_offset; + +- firmware_cert = (secure_boot_certificate_t *) (firmware_base_address + consumed_firmware_offset); +- CONSUME_FIRMWARE(sizeof(secure_boot_certificate_t), -EINVAL); ++ firmware_cert = (secure_boot_certificate_header_t *) (firmware_base_address + consumed_firmware_offset); ++ CONSUME_FIRMWARE(sizeof(secure_boot_certificate_header_t), -EINVAL); + + if ((MAXIMUM_FIRMWARE_CERT_KEY_SIZE < firmware_cert->key_size) || + (MAXIMUM_FIRMWARE_CERT_CONTENT_SIZE < firmware_cert->content_size)) { +--- a/drivers/media/pci/hailo/common/fw_validation.h ++++ b/drivers/media/pci/hailo/common/fw_validation.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef PCIE_COMMON_FIRMWARE_HEADER_UTILS_H_ +@@ -44,8 +44,7 @@ typedef struct { + typedef struct { + u32 key_size; + u32 content_size; +- u8 certificates_data[0]; +-} secure_boot_certificate_t; ++} secure_boot_certificate_header_t; + + #ifdef _MSC_VER + #pragma warning(pop) +@@ -60,6 +59,6 @@ int FW_VALIDATION__validate_fw_header(ui + firmware_header_t **out_firmware_header, enum hailo_board_type board_type); + + int FW_VALIDATION__validate_cert_header(uintptr_t firmware_base_address, +- size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_t **out_firmware_cert); ++ size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_header_t **out_firmware_cert); + + #endif +\ No newline at end of file +--- a/drivers/media/pci/hailo/common/hailo_ioctl_common.h ++++ b/drivers/media/pci/hailo/common/hailo_ioctl_common.h +@@ -1,13 +1,13 @@ + // SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) AND MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_IOCTL_COMMON_H_ + #define _HAILO_IOCTL_COMMON_H_ + + #define HAILO_DRV_VER_MAJOR 4 +-#define HAILO_DRV_VER_MINOR 18 ++#define HAILO_DRV_VER_MINOR 19 + #define HAILO_DRV_VER_REVISION 0 + + #define _STRINGIFY_EXPANDED( x ) #x +@@ -17,10 +17,11 @@ + + // This value is not easily changeable. + // For example: the channel interrupts ioctls assume we have up to 32 channels +-#define MAX_VDMA_CHANNELS_PER_ENGINE (32) +-#define MAX_VDMA_ENGINES (3) +-#define SIZE_OF_VDMA_DESCRIPTOR (16) +-#define VDMA_DEST_CHANNELS_START (16) ++#define MAX_VDMA_CHANNELS_PER_ENGINE (32) ++#define VDMA_CHANNELS_PER_ENGINE_PER_DIRECTION (16) ++#define MAX_VDMA_ENGINES (3) ++#define SIZE_OF_VDMA_DESCRIPTOR (16) ++#define VDMA_DEST_CHANNELS_START (16) + + #define HAILO_VDMA_MAX_ONGOING_TRANSFERS (128) + #define HAILO_VDMA_MAX_ONGOING_TRANSFERS_MASK (HAILO_VDMA_MAX_ONGOING_TRANSFERS - 1) +@@ -37,8 +38,8 @@ + #define FW_ACCESS_APP_CPU_CONTROL_MASK (1 << FW_ACCESS_CONTROL_INTERRUPT_SHIFT) + #define FW_ACCESS_DRIVER_SHUTDOWN_SHIFT (2) + #define FW_ACCESS_DRIVER_SHUTDOWN_MASK (1 << FW_ACCESS_DRIVER_SHUTDOWN_SHIFT) +-#define FW_ACCESS_SOC_CONNECT_SHIFT (3) +-#define FW_ACCESS_SOC_CONNECT_MASK (1 << FW_ACCESS_SOC_CONNECT_SHIFT) ++#define FW_ACCESS_SOC_CONTROL_SHIFT (3) ++#define FW_ACCESS_SOC_CONTROL_MASK (1 << FW_ACCESS_SOC_CONTROL_SHIFT) + + #define INVALID_VDMA_CHANNEL (0xff) + +@@ -245,6 +246,12 @@ struct hailo_desc_list_release_params { + uintptr_t desc_handle; // in + }; + ++struct hailo_write_action_list_params { ++ uint8_t *data; // in ++ size_t size; // in ++ uint64_t dma_address; // out ++}; ++ + /* structure used in ioctl HAILO_DESC_LIST_BIND_VDMA_BUFFER */ + struct hailo_desc_list_program_params { + size_t buffer_handle; // in +@@ -508,6 +515,7 @@ struct hailo_vdma_launch_transfer_params + + /* structure used in ioctl HAILO_SOC_CONNECT */ + struct hailo_soc_connect_params { ++ uint16_t port_number; // in + uint8_t input_channel_index; // out + uint8_t output_channel_index; // out + uintptr_t input_desc_handle; // in +@@ -522,6 +530,7 @@ struct hailo_soc_close_params { + + /* structure used in ioctl HAILO_PCI_EP_ACCEPT */ + struct hailo_pci_ep_accept_params { ++ uint16_t port_number; // in + uint8_t input_channel_index; // out + uint8_t output_channel_index; // out + uintptr_t input_desc_handle; // in +@@ -562,6 +571,7 @@ struct tCompatibleHailoIoctlData + struct hailo_soc_close_params SocCloseParams; + struct hailo_pci_ep_accept_params AcceptParams; + struct hailo_pci_ep_close_params PciEpCloseParams; ++ struct hailo_write_action_list_params WriteActionListParams; + } Buffer; + }; + #endif // _MSC_VER +@@ -632,6 +642,7 @@ enum hailo_nnc_ioctl_code { + HAILO_DISABLE_NOTIFICATION_CODE, + HAILO_READ_LOG_CODE, + HAILO_RESET_NN_CORE_CODE, ++ HAILO_WRITE_ACTION_LIST_CODE, + + // Must be last + HAILO_NNC_IOCTL_MAX_NR +@@ -642,6 +653,7 @@ enum hailo_nnc_ioctl_code { + #define HAILO_DISABLE_NOTIFICATION _IO_(HAILO_NNC_IOCTL_MAGIC, HAILO_DISABLE_NOTIFICATION_CODE) + #define HAILO_READ_LOG _IOWR_(HAILO_NNC_IOCTL_MAGIC, HAILO_READ_LOG_CODE, struct hailo_read_log_params) + #define HAILO_RESET_NN_CORE _IO_(HAILO_NNC_IOCTL_MAGIC, HAILO_RESET_NN_CORE_CODE) ++#define HAILO_WRITE_ACTION_LIST _IOW_(HAILO_NNC_IOCTL_MAGIC, HAILO_WRITE_ACTION_LIST_CODE, struct hailo_write_action_list_params) + + enum hailo_soc_ioctl_code { + HAILO_SOC_IOCTL_CONNECT_CODE, +--- a/drivers/media/pci/hailo/common/hailo_resource.c ++++ b/drivers/media/pci/hailo/common/hailo_resource.c +@@ -1,24 +1,31 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "hailo_resource.h" + ++#include "utils.h" ++ + #include + #include + #include + #include + ++#define ALIGN_TO_32_BIT(addr) ((addr) & (~((uintptr_t)0x3))) + + u8 hailo_resource_read8(struct hailo_resource *resource, size_t offset) + { +- return ioread8((u8*)resource->address + offset); ++ u32 val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); ++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); ++ return (u8)READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, offset_in_bits, val); + } + + u16 hailo_resource_read16(struct hailo_resource *resource, size_t offset) + { +- return ioread16((u8*)resource->address + offset); ++ u32 val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); ++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); ++ return (u16)READ_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, offset_in_bits, val); + } + + u32 hailo_resource_read32(struct hailo_resource *resource, size_t offset) +@@ -28,12 +35,18 @@ u32 hailo_resource_read32(struct hailo_r + + void hailo_resource_write8(struct hailo_resource *resource, size_t offset, u8 value) + { +- iowrite8(value, (u8*)resource->address + offset); ++ u32 initial_val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); ++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); ++ iowrite32(WRITE_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, offset_in_bits, initial_val, value), ++ (u8*)ALIGN_TO_32_BIT(resource->address + offset)); + } + + void hailo_resource_write16(struct hailo_resource *resource, size_t offset, u16 value) + { +- iowrite16(value, (u8*)resource->address + offset); ++ u32 initial_val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); ++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); ++ iowrite32(WRITE_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, offset_in_bits, initial_val, value), ++ (u8*)ALIGN_TO_32_BIT(resource->address + offset)); + } + + void hailo_resource_write32(struct hailo_resource *resource, size_t offset, u32 value) +--- a/drivers/media/pci/hailo/common/hailo_resource.h ++++ b/drivers/media/pci/hailo/common/hailo_resource.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_COMMON_HAILO_RESOURCE_H_ +--- a/drivers/media/pci/hailo/common/pcie_common.c ++++ b/drivers/media/pci/hailo/common/pcie_common.c +@@ -1,10 +1,11 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "pcie_common.h" + #include "fw_operation.h" ++#include "soc_structs.h" + + #include + #include +@@ -35,10 +36,6 @@ + #define FIRMWARE_LOAD_WAIT_MAX_RETRIES (100) + #define FIRMWARE_LOAD_SLEEP_MS (50) + +-#define PCIE_APP_CPU_DEBUG_OFFSET (8*1024) +-#define PCIE_CORE_CPU_DEBUG_OFFSET (PCIE_APP_CPU_DEBUG_OFFSET + DEBUG_BUFFER_TOTAL_SIZE) +- +-#define PCIE_D2H_NOTIFICATION_SRAM_OFFSET (0x640 + 0x640) + #define PCIE_REQUEST_SIZE_OFFSET (0x640) + + #define PCIE_CONFIG_VENDOR_OFFSET (0x0098) +@@ -59,7 +56,6 @@ struct hailo_fw_addresses { + u32 app_fw_code_ram_base; + u32 boot_key_cert; + u32 boot_cont_cert; +- u32 boot_fw_trigger; + u32 core_code_ram_base; + u32 core_fw_header; + u32 atr0_trsl_addr1; +@@ -69,13 +65,11 @@ struct hailo_fw_addresses { + + struct loading_stage { + const struct hailo_file_batch *batch; ++ u32 trigger_address; + }; + + struct hailo_board_compatibility { + struct hailo_fw_addresses fw_addresses; +- const char *fw_filename; +- const struct hailo_config_constants board_cfg; +- const struct hailo_config_constants fw_cfg; + const struct loading_stage stages[MAX_LOADING_STAGES]; + }; + +@@ -85,28 +79,32 @@ static const struct hailo_file_batch hai + .address = 0xA0000, + .max_size = 0x8004, + .is_mandatory = true, +- .has_header = false ++ .has_header = false, ++ .has_core = false + }, + { + .filename = "hailo/hailo10h/u-boot.dtb.signed", + .address = 0xA8004, + .max_size = 0x20000, + .is_mandatory = true, +- .has_header = false ++ .has_header = false, ++ .has_core = false + }, + { + .filename = "hailo/hailo10h/scu_fw.bin", + .address = 0x20000, + .max_size = 0x40000, + .is_mandatory = true, +- .has_header = true ++ .has_header = true, ++ .has_core = false + }, + { + .filename = NULL, + .address = 0x00, + .max_size = 0x00, + .is_mandatory = false, +- .has_header = false ++ .has_header = false, ++ .has_core = false + } + }; + +@@ -116,36 +114,140 @@ static const struct hailo_file_batch hai + .address = 0x85000000, + .max_size = 0x1000000, + .is_mandatory = true, +- .has_header = false ++ .has_header = false, ++ .has_core = false + }, + { + .filename = "hailo/hailo10h/u-boot-tfa.itb", + .address = 0x86000000, + .max_size = 0x1000000, + .is_mandatory = true, +- .has_header = false ++ .has_header = false, ++ .has_core = false + }, + { + .filename = "hailo/hailo10h/fitImage", + .address = 0x87000000, + .max_size = 0x1000000, + .is_mandatory = true, +- .has_header = false ++ .has_header = false, ++ .has_core = false + }, + { + .filename = "hailo/hailo10h/core-image-minimal-hailo10-m2.ext4.gz", + .address = 0x88000000, + .max_size = 0x20000000, // Max size 512MB + .is_mandatory = true, +- .has_header = false ++ .has_header = false, ++ .has_core = false + }, + }; + ++// If loading linux from EMMC - only need few files from second batch (u-boot-spl.bin and u-boot-tfa.itb) ++static const struct hailo_file_batch hailo10h_files_stg2_linux_in_emmc[] = { ++ { ++ .filename = "hailo/hailo10h/u-boot-spl.bin", ++ .address = 0x85000000, ++ .max_size = 0x1000000, ++ .is_mandatory = true, ++ .has_header = false, ++ .has_core = false ++ }, ++ { ++ .filename = "hailo/hailo10h/u-boot-tfa.itb", ++ .address = 0x86000000, ++ .max_size = 0x1000000, ++ .is_mandatory = true, ++ .has_header = false, ++ .has_core = false ++ }, ++ { ++ .filename = NULL, ++ .address = 0x00, ++ .max_size = 0x00, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ }, ++}; ++ ++static const struct hailo_file_batch hailo8_files_stg1[] = { ++ { ++ .filename = "hailo/hailo8_fw.4.19.0.bin", ++ .address = 0x20000, ++ .max_size = 0x50000, ++ .is_mandatory = true, ++ .has_header = true, ++ .has_core = true ++ }, ++ { ++ .filename = "hailo/hailo8_board_cfg.bin", ++ .address = 0x60001000, ++ .max_size = PCIE_HAILO8_BOARD_CFG_MAX_SIZE, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ }, ++ { ++ .filename = "hailo/hailo8_fw_cfg.bin", ++ .address = 0x60001500, ++ .max_size = PCIE_HAILO8_FW_CFG_MAX_SIZE, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ }, ++ { ++ .filename = NULL, ++ .address = 0x00, ++ .max_size = 0x00, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ } ++}; ++ ++static const struct hailo_file_batch hailo10h_legacy_files_stg1[] = { ++ { ++ .filename = "hailo/hailo15_fw.bin", ++ .address = 0x20000, ++ .max_size = 0x100000, ++ .is_mandatory = true, ++ .has_header = true, ++ .has_core = true ++ }, ++ { ++ .filename = NULL, ++ .address = 0x00, ++ .max_size = 0x00, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ } ++}; ++ ++static const struct hailo_file_batch pluto_files_stg1[] = { ++ { ++ .filename = "hailo/pluto_fw.bin", ++ .address = 0x20000, ++ .max_size = 0x100000, ++ .is_mandatory = true, ++ .has_header = true, ++ .has_core = true ++ }, ++ { ++ .filename = NULL, ++ .address = 0x00, ++ .max_size = 0x00, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ } ++}; ++ + static const struct hailo_board_compatibility compat[HAILO_BOARD_TYPE_COUNT] = { + [HAILO_BOARD_TYPE_HAILO8] = { + .fw_addresses = { + .boot_fw_header = 0xE0030, +- .boot_fw_trigger = 0xE0980, + .boot_key_cert = 0xE0048, + .boot_cont_cert = 0xE0390, + .app_fw_code_ram_base = 0x60000, +@@ -155,22 +257,16 @@ static const struct hailo_board_compatib + .raise_ready_offset = 0x1684, + .boot_status = 0xe0000, + }, +- .fw_filename = "hailo/hailo8_fw.bin", +- .board_cfg = { +- .filename = "hailo/hailo8_board_cfg.bin", +- .address = 0x60001000, +- .max_size = PCIE_HAILO8_BOARD_CFG_MAX_SIZE, +- }, +- .fw_cfg = { +- .filename = "hailo/hailo8_fw_cfg.bin", +- .address = 0x60001500, +- .max_size = PCIE_HAILO8_FW_CFG_MAX_SIZE, ++ .stages = { ++ { ++ .batch = hailo8_files_stg1, ++ .trigger_address = 0xE0980 ++ }, + }, + }, + [HAILO_BOARD_TYPE_HAILO10H_LEGACY] = { + .fw_addresses = { + .boot_fw_header = 0x88000, +- .boot_fw_trigger = 0x88c98, + .boot_key_cert = 0x88018, + .boot_cont_cert = 0x886a8, + .app_fw_code_ram_base = 0x20000, +@@ -180,22 +276,16 @@ static const struct hailo_board_compatib + .raise_ready_offset = 0x1754, + .boot_status = 0x80000, + }, +- .fw_filename = "hailo/hailo15_fw.bin", +- .board_cfg = { +- .filename = NULL, +- .address = 0, +- .max_size = 0, +- }, +- .fw_cfg = { +- .filename = NULL, +- .address = 0, +- .max_size = 0, ++ .stages = { ++ { ++ .batch = hailo10h_legacy_files_stg1, ++ .trigger_address = 0x88c98 ++ }, + }, + }, + [HAILO_BOARD_TYPE_HAILO10H] = { + .fw_addresses = { + .boot_fw_header = 0x88000, +- .boot_fw_trigger = 0x88c98, + .boot_key_cert = 0x88018, + .boot_cont_cert = 0x886a8, + .app_fw_code_ram_base = 0x20000, +@@ -205,23 +295,18 @@ static const struct hailo_board_compatib + .raise_ready_offset = 0x1754, + .boot_status = 0x80000, + }, +- .fw_filename = NULL, +- .board_cfg = { +- .filename = NULL, +- .address = 0, +- .max_size = 0, +- }, +- .fw_cfg = { +- .filename = NULL, +- .address = 0, +- .max_size = 0, +- }, + .stages = { + { + .batch = hailo10h_files_stg1, ++ .trigger_address = 0x88c98 + }, + { + .batch = hailo10h_files_stg2, ++ .trigger_address = 0x84000000 ++ }, ++ { ++ .batch = hailo10h_files_stg2_linux_in_emmc, ++ .trigger_address = 0x84000000 + }, + }, + }, +@@ -230,7 +315,6 @@ static const struct hailo_board_compatib + [HAILO_BOARD_TYPE_PLUTO] = { + .fw_addresses = { + .boot_fw_header = 0x88000, +- .boot_fw_trigger = 0x88c98, + .boot_key_cert = 0x88018, + .boot_cont_cert = 0x886a8, + .app_fw_code_ram_base = 0x20000, +@@ -241,16 +325,11 @@ static const struct hailo_board_compatib + .raise_ready_offset = 0x174c, + .boot_status = 0x80000, + }, +- .fw_filename = "hailo/pluto_fw.bin", +- .board_cfg = { +- .filename = NULL, +- .address = 0, +- .max_size = 0, +- }, +- .fw_cfg = { +- .filename = NULL, +- .address = 0, +- .max_size = 0, ++ .stages = { ++ { ++ .batch = pluto_files_stg1, ++ .trigger_address = 0x88c98 ++ }, + }, + } + }; +@@ -340,21 +419,6 @@ void hailo_pcie_write_firmware_driver_sh + hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, fw_access_value); + } + +-int hailo_pcie_read_firmware_notification(struct hailo_pcie_resources *resources, +- struct hailo_d2h_notification *notification) +-{ +- struct hailo_resource notification_resource; +- +- if (PCIE_D2H_NOTIFICATION_SRAM_OFFSET > resources->fw_access.size) { +- return -EINVAL; +- } +- +- notification_resource.address = resources->fw_access.address + PCIE_D2H_NOTIFICATION_SRAM_OFFSET, +- notification_resource.size = sizeof(struct hailo_d2h_notification); +- +- return hailo_read_firmware_notification(¬ification_resource, notification); +-} +- + int hailo_pcie_configure_atr_table(struct hailo_resource *bridge_config, u64 trsl_addr, u32 atr_index) + { + size_t offset = 0; +@@ -388,7 +452,7 @@ static void write_memory_chunk(struct ha + u32 ATR_INDEX = 0; + BUG_ON(dest_offset + len > (u32)resources->fw_access.size); + +- (void)hailo_pcie_configure_atr_table(&resources->config, (u64)dest, ATR_INDEX); ++ (void)hailo_pcie_configure_atr_table(&resources->config, dest, ATR_INDEX); + (void)hailo_resource_write_buffer(&resources->fw_access, dest_offset, len, src); + } + +@@ -398,13 +462,13 @@ static void read_memory_chunk( + u32 ATR_INDEX = 0; + BUG_ON(src_offset + len > (u32)resources->fw_access.size); + +- (void)hailo_pcie_configure_atr_table(&resources->config, (u64)src, ATR_INDEX); ++ (void)hailo_pcie_configure_atr_table(&resources->config, src, ATR_INDEX); + (void)hailo_resource_read_buffer(&resources->fw_access, src_offset, len, dest); + } + + // Note: this function modify the device ATR table (that is also used by the firmware for control and vdma). + // Use with caution, and restore the original atr if needed. +-void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len) ++static void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len) + { + struct hailo_atr_config previous_atr = {0}; + hailo_ptr_t base_address = (dest & ~ATR_TABLE_SIZE_MASK); +@@ -417,8 +481,8 @@ void write_memory(struct hailo_pcie_reso + + if (base_address != dest) { + // Data is not aligned, write the first chunk +- chunk_len = min(base_address + ATR_TABLE_SIZE - dest, len); +- write_memory_chunk(resources, base_address, dest - base_address, src, chunk_len); ++ chunk_len = min((u32)(base_address + ATR_TABLE_SIZE - dest), len); ++ write_memory_chunk(resources, base_address, (u32)(dest - base_address), src, chunk_len); + offset += chunk_len; + } + +@@ -447,8 +511,8 @@ static void read_memory(struct hailo_pci + + if (base_address != src) { + // Data is not aligned, write the first chunk +- chunk_len = min(base_address + ATR_TABLE_SIZE - src, len); +- read_memory_chunk(resources, base_address, src - base_address, dest, chunk_len); ++ chunk_len = min((u32)(base_address + ATR_TABLE_SIZE - src), len); ++ read_memory_chunk(resources, base_address, (u32)(src - base_address), dest, chunk_len); + offset += chunk_len; + } + +@@ -463,12 +527,12 @@ static void read_memory(struct hailo_pci + } + + static void hailo_write_app_firmware(struct hailo_pcie_resources *resources, firmware_header_t *fw_header, +- secure_boot_certificate_t *fw_cert) ++ secure_boot_certificate_header_t *fw_cert) + { + const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); +- void *fw_code = (void*)((u8*)fw_header + sizeof(firmware_header_t)); +- void *key_data = &fw_cert->certificates_data[0]; +- void *content_data = &fw_cert->certificates_data[fw_cert->key_size]; ++ u8 *fw_code = ((u8*)fw_header + sizeof(firmware_header_t)); ++ u8 *key_data = ((u8*)fw_cert + sizeof(secure_boot_certificate_header_t)); ++ u8 *content_data = key_data + fw_cert->key_size; + + write_memory(resources, fw_addresses->boot_fw_header, fw_header, sizeof(firmware_header_t)); + +@@ -487,13 +551,11 @@ static void hailo_write_core_firmware(st + write_memory(resources, fw_addresses->core_fw_header, fw_header, sizeof(firmware_header_t)); + } + +-void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources) ++void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 address) + { +- const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); + u32 pcie_finished = 1; + +- write_memory(resources, fw_addresses->boot_fw_trigger, +- (void*)&pcie_finished, sizeof(pcie_finished)); ++ write_memory(resources, address, (void*)&pcie_finished, sizeof(pcie_finished)); + } + + u32 hailo_get_boot_status(struct hailo_pcie_resources *resources) +@@ -501,8 +563,7 @@ u32 hailo_get_boot_status(struct hailo_p + u32 boot_status = 0; + const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); + +- read_memory(resources, fw_addresses->boot_status, +- &boot_status, sizeof(boot_status)); ++ read_memory(resources, fw_addresses->boot_status, &boot_status, sizeof(boot_status)); + + return boot_status; + } +@@ -517,11 +578,11 @@ u32 hailo_get_boot_status(struct hailo_p + */ + static int FW_VALIDATION__validate_fw_headers(uintptr_t firmware_base_address, size_t firmware_size, + firmware_header_t **out_app_firmware_header, firmware_header_t **out_core_firmware_header, +- secure_boot_certificate_t **out_firmware_cert, enum hailo_board_type board_type) ++ secure_boot_certificate_header_t **out_firmware_cert, enum hailo_board_type board_type) + { + firmware_header_t *app_firmware_header = NULL; + firmware_header_t *core_firmware_header = NULL; +- secure_boot_certificate_t *firmware_cert = NULL; ++ secure_boot_certificate_header_t *firmware_cert = NULL; + int err = -EINVAL; + u32 consumed_firmware_offset = 0; + +@@ -571,25 +632,25 @@ exit: + return err; + } + +-static int write_single_file(struct hailo_pcie_resources *resources, const struct hailo_file_batch *files_batch, struct device *dev) ++static int write_single_file(struct hailo_pcie_resources *resources, const struct hailo_file_batch *file_info, struct device *dev) + { + const struct firmware *firmware = NULL; + firmware_header_t *app_firmware_header = NULL; +- secure_boot_certificate_t *firmware_cert = NULL; ++ secure_boot_certificate_header_t *firmware_cert = NULL; + firmware_header_t *core_firmware_header = NULL; + int err = 0; + +- err = request_firmware_direct(&firmware, files_batch->filename, dev); ++ err = request_firmware_direct(&firmware, file_info->filename, dev); + if (err < 0) { + return err; + } + +- if (firmware->size > files_batch->max_size) { ++ if (firmware->size > file_info->max_size) { + release_firmware(firmware); + return -EFBIG; + } + +- if (files_batch->has_header) { ++ if (file_info->has_header) { + err = FW_VALIDATION__validate_fw_headers((uintptr_t)firmware->data, firmware->size, + &app_firmware_header, &core_firmware_header, &firmware_cert, resources->board_type); + if (err < 0) { +@@ -598,8 +659,11 @@ static int write_single_file(struct hail + } + + hailo_write_app_firmware(resources, app_firmware_header, firmware_cert); ++ if (file_info->has_core) { ++ hailo_write_core_firmware(resources, core_firmware_header); ++ } + } else { +- write_memory(resources, files_batch->address, (void*)firmware->data, firmware->size); ++ write_memory(resources, file_info->address, (void*)firmware->data, firmware->size); + } + + release_firmware(firmware); +@@ -632,31 +696,13 @@ int hailo_pcie_write_firmware_batch(stru + dev_notice(dev, "File %s written successfully\n", files_batch[file_index].filename); + } + +- return 0; +-} +- +-int hailo_pcie_write_firmware(struct hailo_pcie_resources *resources, const void *fw_data, size_t fw_size) +-{ +- firmware_header_t *app_firmware_header = NULL; +- secure_boot_certificate_t *firmware_cert = NULL; +- firmware_header_t *core_firmware_header = NULL; +- +- int err = FW_VALIDATION__validate_fw_headers((uintptr_t)fw_data, fw_size, +- &app_firmware_header, &core_firmware_header, &firmware_cert, resources->board_type); +- if (err < 0) { +- return err; +- } +- +- hailo_write_app_firmware(resources, app_firmware_header, firmware_cert); +- hailo_write_core_firmware(resources, core_firmware_header); +- +- hailo_trigger_firmware_boot(resources); ++ hailo_trigger_firmware_boot(resources, compat[resources->board_type].stages[stage].trigger_address); + + return 0; + } + + // TODO: HRT-14147 - remove this function +-bool hailo_pcie_is_device_ready_for_boot(struct hailo_pcie_resources *resources) ++static bool hailo_pcie_is_device_ready_for_boot(struct hailo_pcie_resources *resources) + { + return hailo_get_boot_status(resources) == BOOT_STATUS_UNINITIALIZED; + } +@@ -691,32 +737,6 @@ bool hailo_pcie_wait_for_firmware(struct + return false; + } + +-int hailo_pcie_write_config_common(struct hailo_pcie_resources *resources, const void* config_data, +- const size_t config_size, const struct hailo_config_constants *config_consts) +-{ +- if (config_size > config_consts->max_size) { +- return -EINVAL; +- } +- +- write_memory(resources, config_consts->address, config_data, (u32)config_size); +- return 0; +-} +- +-const struct hailo_config_constants* hailo_pcie_get_board_config_constants(const enum hailo_board_type board_type) { +- BUG_ON(board_type >= HAILO_BOARD_TYPE_COUNT || board_type < 0); +- return &compat[board_type].board_cfg; +-} +- +-const struct hailo_config_constants* hailo_pcie_get_user_config_constants(const enum hailo_board_type board_type) { +- BUG_ON(board_type >= HAILO_BOARD_TYPE_COUNT || board_type < 0); +- return &compat[board_type].fw_cfg; +-} +- +-const char* hailo_pcie_get_fw_filename(const enum hailo_board_type board_type) { +- BUG_ON(board_type >= HAILO_BOARD_TYPE_COUNT || board_type < 0); +- return compat[board_type].fw_filename; +-} +- + void hailo_pcie_update_channel_interrupts_mask(struct hailo_pcie_resources* resources, u32 channels_bitmap) + { + size_t i = 0; +@@ -745,7 +765,7 @@ void hailo_pcie_enable_interrupts(struct + hailo_resource_write32(&resources->config, BCS_SOURCE_INTERRUPT_PER_CHANNEL, 0xFFFFFFFF); + + mask |= (BCS_ISTATUS_HOST_FW_IRQ_CONTROL_MASK | BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION | +- BCS_ISTATUS_HOST_DRIVER_DOWN | BCS_ISTATUS_SOC_CONNECT_ACCEPTED); ++ BCS_ISTATUS_HOST_DRIVER_DOWN | BCS_ISTATUS_SOC_CONNECT_ACCEPTED | BCS_ISTATUS_SOC_CLOSED_IRQ); + hailo_resource_write32(&resources->config, BSC_IMASK_HOST, mask); + } + +@@ -754,45 +774,15 @@ void hailo_pcie_disable_interrupts(struc + hailo_resource_write32(&resources->config, BSC_IMASK_HOST, 0); + } + +-long hailo_pcie_read_firmware_log(struct hailo_pcie_resources *resources, struct hailo_read_log_params *params) +-{ +- long err = 0; +- struct hailo_resource log_resource = {resources->fw_access.address, DEBUG_BUFFER_TOTAL_SIZE}; +- +- if (HAILO_CPU_ID_CPU0 == params->cpu_id) { +- log_resource.address += PCIE_APP_CPU_DEBUG_OFFSET; +- } else if (HAILO_CPU_ID_CPU1 == params->cpu_id) { +- log_resource.address += PCIE_CORE_CPU_DEBUG_OFFSET; +- } else { +- return -EINVAL; +- } +- +- if (0 == params->buffer_size) { +- params->read_bytes = 0; +- return 0; +- } +- +- err = hailo_read_firmware_log(&log_resource, params); +- if (0 != err) { +- return err; +- } +- +- return 0; +-} +- + static int direct_memory_transfer(struct hailo_pcie_resources *resources, + struct hailo_memory_transfer_params *params) + { +- if (params->address > U32_MAX) { +- return -EFAULT; +- } +- + switch (params->transfer_direction) { + case TRANSFER_READ: +- read_memory(resources, (u32)params->address, params->buffer, (u32)params->count); ++ read_memory(resources, params->address, params->buffer, (u32)params->count); + break; + case TRANSFER_WRITE: +- write_memory(resources, (u32)params->address, params->buffer, (u32)params->count); ++ write_memory(resources, params->address, params->buffer, (u32)params->count); + break; + default: + return -EINVAL; +@@ -845,16 +835,18 @@ int hailo_set_device_type(struct hailo_p + return 0; + } + +-// On PCIe, just return the address +-static u64 encode_dma_address(dma_addr_t dma_address, u8 channel_id) ++// On PCIe, just return the start address ++u64 hailo_pcie_encode_desc_dma_address_range(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id) + { + (void)channel_id; +- return (u64)dma_address; ++ (void)dma_address_end; ++ (void)step; ++ return (u64)dma_address_start; + } + + struct hailo_vdma_hw hailo_pcie_vdma_hw = { + .hw_ops = { +- .encode_desc_dma_address = encode_dma_address ++ .encode_desc_dma_address_range = hailo_pcie_encode_desc_dma_address_range, + }, + .ddr_data_id = HAILO_PCIE_HOST_DMA_DATA_ID, + .device_interrupts_bitmask = HAILO_PCIE_DMA_DEVICE_INTERRUPTS_BITMASK, +@@ -862,11 +854,19 @@ struct hailo_vdma_hw hailo_pcie_vdma_hw + .src_channels_bitmask = HAILO_PCIE_DMA_SRC_CHANNELS_BITMASK, + }; + +-void hailo_soc_write_soc_connect(struct hailo_pcie_resources *resources) ++void hailo_pcie_soc_write_request(struct hailo_pcie_resources *resources, ++ const struct hailo_pcie_soc_request *request) + { + const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); +- const u32 soc_connect_value = FW_ACCESS_SOC_CONNECT_MASK; ++ BUILD_BUG_ON_MSG((sizeof(*request) % sizeof(u32)) != 0, "Request must be a multiple of 4 bytes"); + +- // Write shutdown flag to FW +- hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, soc_connect_value); +-} +\ No newline at end of file ++ hailo_resource_write_buffer(&resources->fw_access, 0, sizeof(*request), (void*)request); ++ hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, FW_ACCESS_SOC_CONTROL_MASK); ++} ++ ++void hailo_pcie_soc_read_response(struct hailo_pcie_resources *resources, ++ struct hailo_pcie_soc_response *response) ++{ ++ BUILD_BUG_ON_MSG((sizeof(*response) % sizeof(u32)) != 0, "Request must be a multiple of 4 bytes"); ++ hailo_resource_read_buffer(&resources->fw_access, 0, sizeof(*response), response); ++} +--- a/drivers/media/pci/hailo/common/pcie_common.h ++++ b/drivers/media/pci/hailo/common/pcie_common.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_COMMON_PCIE_COMMON_H_ +@@ -12,6 +12,7 @@ + #include "fw_operation.h" + #include "utils.h" + #include "vdma_common.h" ++#include "soc_structs.h" + + #include + #include +@@ -21,6 +22,7 @@ + #define BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION (0x02000000) + #define BCS_ISTATUS_HOST_DRIVER_DOWN (0x08000000) + #define BCS_ISTATUS_SOC_CONNECT_ACCEPTED (0x10000000) ++#define BCS_ISTATUS_SOC_CLOSED_IRQ (0x20000000) + #define BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK (0x000000FF) + #define BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK (0x0000FF00) + +@@ -42,7 +44,7 @@ + #define PCI_DEVICE_ID_HAILO_HAILO15 0x45C4 + #define PCI_DEVICE_ID_HAILO_PLUTO 0x43a2 + +-typedef u32 hailo_ptr_t; ++typedef u64 hailo_ptr_t; + + struct hailo_pcie_resources { + struct hailo_resource config; // BAR0 +@@ -63,7 +65,8 @@ struct hailo_atr_config { + enum loading_stages { + FIRST_STAGE = 0, + SECOND_STAGE = 1, +- MAX_LOADING_STAGES = 2 ++ SECOND_STAGE_LINUX_IN_EMMC = 2, ++ MAX_LOADING_STAGES = 3 + }; + + enum hailo_pcie_interrupt_masks { +@@ -71,6 +74,7 @@ enum hailo_pcie_interrupt_masks { + FW_NOTIFICATION = BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION, + DRIVER_DOWN = BCS_ISTATUS_HOST_DRIVER_DOWN, + SOC_CONNECT_ACCEPTED = BCS_ISTATUS_SOC_CONNECT_ACCEPTED, ++ SOC_CLOSED_IRQ = BCS_ISTATUS_SOC_CLOSED_IRQ, + VDMA_SRC_IRQ_MASK = BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK, + VDMA_DEST_IRQ_MASK = BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK + }; +@@ -80,18 +84,13 @@ struct hailo_pcie_interrupt_source { + u32 vdma_channels_bitmap; + }; + +-struct hailo_config_constants { +- const char *filename; +- u32 address; +- size_t max_size; +-}; +- + struct hailo_file_batch { + const char *filename; + u32 address; + size_t max_size; + bool is_mandatory; + bool has_header; ++ bool has_core; + }; + + // TODO: HRT-6144 - Align Windows/Linux to QNX +@@ -130,27 +129,15 @@ void hailo_pcie_disable_interrupts(struc + int hailo_pcie_write_firmware_control(struct hailo_pcie_resources *resources, const struct hailo_fw_control *command); + int hailo_pcie_read_firmware_control(struct hailo_pcie_resources *resources, struct hailo_fw_control *command); + +-int hailo_pcie_write_firmware(struct hailo_pcie_resources *resources, const void *fw_data, size_t fw_size); + int hailo_pcie_write_firmware_batch(struct device *dev, struct hailo_pcie_resources *resources, u32 stage); + bool hailo_pcie_is_firmware_loaded(struct hailo_pcie_resources *resources); + bool hailo_pcie_wait_for_firmware(struct hailo_pcie_resources *resources); + +-int hailo_pcie_read_firmware_notification(struct hailo_pcie_resources *resources, +- struct hailo_d2h_notification *notification); +- +-int hailo_pcie_write_config_common(struct hailo_pcie_resources *resources, const void* config_data, +- const size_t config_size, const struct hailo_config_constants *config_consts); +-const struct hailo_config_constants* hailo_pcie_get_board_config_constants(const enum hailo_board_type board_type); +-const struct hailo_config_constants* hailo_pcie_get_user_config_constants(const enum hailo_board_type board_type); +-const char* hailo_pcie_get_fw_filename(const enum hailo_board_type board_type); +- +-long hailo_pcie_read_firmware_log(struct hailo_pcie_resources *resources, struct hailo_read_log_params *params); + int hailo_pcie_memory_transfer(struct hailo_pcie_resources *resources, struct hailo_memory_transfer_params *params); + + bool hailo_pcie_is_device_connected(struct hailo_pcie_resources *resources); + void hailo_pcie_write_firmware_driver_shutdown(struct hailo_pcie_resources *resources); +-void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len); +-void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources); ++void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 address); + + int hailo_set_device_type(struct hailo_pcie_resources *resources); + +@@ -159,7 +146,12 @@ u32 hailo_get_boot_status(struct hailo_p + int hailo_pcie_configure_atr_table(struct hailo_resource *bridge_config, u64 trsl_addr, u32 atr_index); + void hailo_pcie_read_atr_table(struct hailo_resource *bridge_config, struct hailo_atr_config *atr, u32 atr_index); + +-void hailo_soc_write_soc_connect(struct hailo_pcie_resources *resources); ++u64 hailo_pcie_encode_desc_dma_address_range(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id); ++ ++void hailo_pcie_soc_write_request(struct hailo_pcie_resources *resources, ++ const struct hailo_pcie_soc_request *request); ++void hailo_pcie_soc_read_response(struct hailo_pcie_resources *resources, ++ struct hailo_pcie_soc_response *response); + + #ifdef __cplusplus + } +--- /dev/null ++++ b/drivers/media/pci/hailo/common/soc_structs.h +@@ -0,0 +1,79 @@ ++// SPDX-License-Identifier: MIT ++/** ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ ++/** ++ * Contains definitions for pcie soc to pcie ep communication ++ */ ++ ++#ifndef __HAILO_COMMON_SOC_STRUCTS__ ++#define __HAILO_COMMON_SOC_STRUCTS__ ++ ++#include ++ ++#pragma pack(push, 1) ++ ++struct hailo_pcie_soc_connect_request { ++ u16 port; ++}; ++ ++struct hailo_pcie_soc_connect_response { ++ u8 input_channel_index; ++ u8 output_channel_index; ++}; ++ ++ ++struct hailo_pcie_soc_close_request { ++ u32 channels_bitmap; ++}; ++ ++struct hailo_pcie_soc_close_response { ++ u8 reserved; ++}; ++ ++enum hailo_pcie_soc_control_code { ++ // Start from big initial value to ensure the right code was used (using 0 ++ // as initiale may cause confusion if the code was not set correctly). ++ HAILO_PCIE_SOC_CONTROL_CODE_CONNECT = 0x100, ++ HAILO_PCIE_SOC_CONTROL_CODE_CLOSE, ++ HAILO_PCIE_SOC_CONTROL_CODE_INVALID, ++}; ++ ++#define HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES (16) ++#define HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES (16) ++ ++// IRQ to signal the PCIe that the EP was closed/released ++#define PCI_EP_SOC_CLOSED_IRQ (0x00000020) ++#define PCI_EP_SOC_CONNECT_RESPONSE (0x00000010) ++ ++struct hailo_pcie_soc_request { ++ u32 control_code; ++ union { ++ struct hailo_pcie_soc_connect_request connect; ++ struct hailo_pcie_soc_close_request close; ++ u8 pad[HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES]; ++ }; ++}; ++ ++struct hailo_pcie_soc_response { ++ u32 control_code; ++ s32 status; ++ union { ++ struct hailo_pcie_soc_connect_response connect; ++ struct hailo_pcie_soc_close_response close; ++ u8 pad[HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES]; ++ }; ++}; ++ ++#pragma pack(pop) ++ ++// Compile time validate function. Don't need to call it. ++static inline void __validate_soc_struct_sizes(void) ++{ ++ BUILD_BUG_ON_MSG(sizeof(struct hailo_pcie_soc_request) != ++ sizeof(u32) + HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES, "Invalid request size"); ++ BUILD_BUG_ON_MSG(sizeof(struct hailo_pcie_soc_response) != ++ sizeof(u32) + sizeof(s32) + HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES, "Invalid response size"); ++} ++ ++#endif /* __HAILO_COMMON_SOC_STRUCTS__ */ +\ No newline at end of file +--- a/drivers/media/pci/hailo/common/utils.h ++++ b/drivers/media/pci/hailo/common/utils.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_DRIVER_UTILS_H_ +@@ -8,6 +8,11 @@ + + #include + ++#define DWORD_SIZE (4) ++#define WORD_SIZE (2) ++#define BYTE_SIZE (1) ++#define BITS_IN_BYTE (8) ++ + #define hailo_clear_bit(bit, pval) { *(pval) &= ~(1 << bit); } + #define hailo_test_bit(pos,var_addr) ((*var_addr) & (1<<(pos))) + +@@ -50,6 +55,22 @@ static inline uint8_t ceil_log2(uint32_t + return result; + } + ++// Gets the nearest power of 2 >= value, for any value <= MAX_POWER_OF_2_VALUE. Otherwise POWER_OF_2_ERROR is returned. ++#define MAX_POWER_OF_2_VALUE (0x80000000) ++#define POWER_OF_2_ERROR ((uint32_t)-1) ++static inline uint32_t get_nearest_powerof_2(uint32_t value) ++{ ++ uint32_t power_of_2 = 1; ++ if (value > MAX_POWER_OF_2_VALUE) { ++ return POWER_OF_2_ERROR; ++ } ++ ++ while (value > power_of_2) { ++ power_of_2 <<= 1; ++ } ++ return power_of_2; ++} ++ + #ifndef DIV_ROUND_UP + #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) + #endif +--- a/drivers/media/pci/hailo/common/vdma_common.c ++++ b/drivers/media/pci/hailo/common/vdma_common.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "vdma_common.h" +@@ -62,11 +62,6 @@ + #define VDMA_CHANNEL_NUM_PROCESSED_MASK ((1 << VDMA_CHANNEL_NUM_PROCESSED_WIDTH) - 1) + #define VDMA_CHANNEL_NUM_ONGOING_MASK VDMA_CHANNEL_NUM_PROCESSED_MASK + +-#define DWORD_SIZE (4) +-#define WORD_SIZE (2) +-#define BYTE_SIZE (1) +-#define BITS_IN_BYTE (8) +- + #define TIMESTAMPS_CIRC_SPACE(timestamp_list) \ + CIRC_SPACE((timestamp_list).head, (timestamp_list).tail, CHANNEL_IRQ_TIMESTAMPS_SIZE) + #define TIMESTAMPS_CIRC_CNT(timestamp_list) \ +@@ -150,7 +145,7 @@ static bool validate_last_desc_status(st + return true; + } + +-void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size, ++static void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size, + u8 data_id) + { + descriptor->PageSize_DescControl = (u32)((page_size << DESCRIPTOR_PAGE_SIZE_SHIFT) + +@@ -174,33 +169,45 @@ static int program_descriptors_in_chunk( + u32 max_desc_index, + u8 channel_id) + { +- const u32 desc_per_chunk = DIV_ROUND_UP(chunk_size, desc_list->desc_page_size); ++ const u16 page_size = desc_list->desc_page_size; ++ const u8 ddr_data_id = vdma_hw->ddr_data_id; ++ const u32 descs_to_program = DIV_ROUND_UP(chunk_size, page_size); ++ const u32 starting_desc_index = desc_index; ++ const u32 residue_size = chunk_size % page_size; + struct hailo_vdma_descriptor *dma_desc = NULL; +- u16 size_to_program = 0; +- u32 index = 0; + u64 encoded_addr = 0; + +- for (index = 0; index < desc_per_chunk; index++) { +- if (desc_index > max_desc_index) { +- return -ERANGE; +- } +- +- encoded_addr = vdma_hw->hw_ops.encode_desc_dma_address(chunk_addr, channel_id); +- if (INVALID_VDMA_ADDRESS == encoded_addr) { +- return -EFAULT; +- } ++ if (descs_to_program == 0) { ++ // Nothing to program ++ return 0; ++ } + +- dma_desc = &desc_list->desc_list[desc_index % desc_list->desc_count]; +- size_to_program = chunk_size > desc_list->desc_page_size ? +- desc_list->desc_page_size : (u16)chunk_size; +- hailo_vdma_program_descriptor(dma_desc, encoded_addr, size_to_program, vdma_hw->ddr_data_id); ++ // We iterate through descriptors [desc_index, desc_index + descs_to_program) ++ if (desc_index + descs_to_program > max_desc_index + 1) { ++ return -ERANGE; ++ } + +- chunk_addr += size_to_program; +- chunk_size -= size_to_program; +- desc_index++; ++ encoded_addr = vdma_hw->hw_ops.encode_desc_dma_address_range(chunk_addr, chunk_addr + chunk_size, page_size, channel_id); ++ if (INVALID_VDMA_ADDRESS == encoded_addr) { ++ return -EFAULT; + } + +- return (int)desc_per_chunk; ++ // Program all descriptors except the last one ++ for (desc_index = starting_desc_index; desc_index < starting_desc_index + descs_to_program - 1; desc_index++) { ++ // 'desc_index & desc_list_len_mask' is used instead of modulo; see hailo_vdma_descriptors_list documentation. ++ hailo_vdma_program_descriptor( ++ &desc_list->desc_list[desc_index & desc_list->desc_count_mask], ++ encoded_addr, page_size, ddr_data_id); ++ encoded_addr += page_size; ++ } ++ ++ // Handle the last descriptor outside of the loop ++ // 'desc_index & desc_list_len_mask' is used instead of modulo; see hailo_vdma_descriptors_list documentation. ++ dma_desc = &desc_list->desc_list[desc_index & desc_list->desc_count_mask]; ++ hailo_vdma_program_descriptor(dma_desc, encoded_addr, ++ (residue_size == 0) ? page_size : (u16)residue_size, ddr_data_id); ++ ++ return (int)descs_to_program; + } + + static unsigned long get_interrupts_bitmask(struct hailo_vdma_hw *vdma_hw, +@@ -236,11 +243,11 @@ static int bind_and_program_descriptors_ + { + const u8 channel_id = get_channel_id(channel_index); + int desc_programmed = 0; ++ int descs_programmed_in_chunk = 0; + u32 max_desc_index = 0; + u32 chunk_size = 0; + struct scatterlist *sg_entry = NULL; + unsigned int i = 0; +- int ret = 0; + size_t buffer_current_offset = 0; + dma_addr_t chunk_start_addr = 0; + u32 program_size = buffer->size; +@@ -272,14 +279,14 @@ static int bind_and_program_descriptors_ + (u32)(sg_dma_len(sg_entry)); + chunk_size = min((u32)program_size, chunk_size); + +- ret = program_descriptors_in_chunk(vdma_hw, chunk_start_addr, chunk_size, desc_list, ++ descs_programmed_in_chunk = program_descriptors_in_chunk(vdma_hw, chunk_start_addr, chunk_size, desc_list, + starting_desc, max_desc_index, channel_id); +- if (ret < 0) { +- return ret; ++ if (descs_programmed_in_chunk < 0) { ++ return descs_programmed_in_chunk; + } + +- desc_programmed += ret; +- starting_desc = starting_desc + ret; ++ desc_programmed += descs_programmed_in_chunk; ++ starting_desc = starting_desc + descs_programmed_in_chunk; + program_size -= chunk_size; + buffer_current_offset += sg_dma_len(sg_entry); + } +@@ -583,21 +590,23 @@ void hailo_vdma_engine_disable_channels( + engine->enabled_channels &= ~bitmap; + + for_each_vdma_channel(engine, channel, channel_index) { +- channel_state_init(&channel->state); ++ if (hailo_test_bit(channel_index, &bitmap)) { ++ channel_state_init(&channel->state); + +- while (ONGOING_TRANSFERS_CIRC_CNT(channel->ongoing_transfers) > 0) { +- struct hailo_ongoing_transfer transfer; +- ongoing_transfer_pop(channel, &transfer); +- +- if (channel->last_desc_list == NULL) { +- pr_err("Channel %d has ongoing transfers but no desc list\n", channel->index); +- continue; ++ while (ONGOING_TRANSFERS_CIRC_CNT(channel->ongoing_transfers) > 0) { ++ struct hailo_ongoing_transfer transfer; ++ ongoing_transfer_pop(channel, &transfer); ++ ++ if (channel->last_desc_list == NULL) { ++ pr_err("Channel %d has ongoing transfers but no desc list\n", channel->index); ++ continue; ++ } ++ ++ clear_dirty_descs(channel, &transfer); + } + +- clear_dirty_descs(channel, &transfer); ++ channel->last_desc_list = NULL; + } +- +- channel->last_desc_list = NULL; + } + } + +--- a/drivers/media/pci/hailo/common/vdma_common.h ++++ b/drivers/media/pci/hailo/common/vdma_common.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_COMMON_VDMA_COMMON_H_ +@@ -30,7 +30,13 @@ struct hailo_vdma_descriptor { + + struct hailo_vdma_descriptors_list { + struct hailo_vdma_descriptor *desc_list; +- u32 desc_count; // Must be power of 2 if is_circular is set. ++ // Must be power of 2 if is_circular is set. ++ u32 desc_count; ++ // The nearest power of 2 to desc_count (including desc_count), minus 1. ++ // * If the list is circular, then 'index & desc_count_mask' can be used instead of modulo. ++ // * Otherwise, we can't wrap around the list anyway. However, for any index < desc_count, 'index & desc_count_mask' ++ // will return the same value. ++ u32 desc_count_mask; + u16 desc_page_size; + bool is_circular; + }; +@@ -113,9 +119,10 @@ struct hailo_vdma_engine { + }; + + struct hailo_vdma_hw_ops { +- // Accepts some dma_addr_t mapped to the device and encodes it using +- // hw specific encode. returns INVALID_VDMA_ADDRESS on failure. +- u64 (*encode_desc_dma_address)(dma_addr_t dma_address, u8 channel_id); ++ // Accepts start, end and step of an address range (of type dma_addr_t). ++ // Returns the encoded base address or INVALID_VDMA_ADDRESS if the range/step is invalid. ++ // All addresses in the range of [returned_addr, returned_addr + step, returned_addr + 2*step, ..., dma_address_end) are valid. ++ u64 (*encode_desc_dma_address_range)(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id); + }; + + struct hailo_vdma_hw { +@@ -136,12 +143,9 @@ struct hailo_vdma_hw { + for (index = 0, element = &array[index]; index < size; index++, element = &array[index]) + + #define for_each_vdma_channel(engine, channel, channel_index) \ +- _for_each_element_array(engine->channels, MAX_VDMA_CHANNELS_PER_ENGINE, \ ++ _for_each_element_array((engine)->channels, MAX_VDMA_CHANNELS_PER_ENGINE, \ + channel, channel_index) + +-void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size, +- u8 data_id); +- + /** + * Program the given descriptors list to map the given buffer. + * +--- a/drivers/media/pci/hailo/src/fops.c ++++ b/drivers/media/pci/hailo/src/fops.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include +@@ -19,14 +19,14 @@ + #include + #endif + +-#include "utils.h" + #include "fops.h" + #include "vdma_common.h" + #include "utils/logs.h" + #include "vdma/memory.h" + #include "vdma/ioctl.h" + #include "utils/compact.h" +-#include "pci_soc_ioctl.h" ++#include "nnc.h" ++#include "soc.h" + + + #if LINUX_VERSION_CODE >= KERNEL_VERSION( 4, 13, 0 ) +@@ -48,13 +48,6 @@ + // On pcie driver there is only one dma engine + #define DEFAULT_VDMA_ENGINE_INDEX (0) + +-#if !defined(HAILO_EMULATOR) +-#define DEFAULT_SHUTDOWN_TIMEOUT_MS (5) +-#else /* !defined(HAILO_EMULATOR) */ +-#define DEFAULT_SHUTDOWN_TIMEOUT_MS (1000) +-#endif /* !defined(HAILO_EMULATOR) */ +- +-static long hailo_add_notification_wait(struct hailo_pcie_board *board, struct file *filp); + + static struct hailo_file_context *create_file_context(struct hailo_pcie_board *board, struct file *filp) + { +@@ -124,7 +117,7 @@ int hailo_pcie_fops_open(struct inode *i + + previous_power_state = pBoard->pDev->current_state; + if (PCI_D0 != previous_power_state) { +- hailo_info(pBoard, "Waking up board"); ++ hailo_info(pBoard, "Waking up board change state from %d to PCI_D0\n", previous_power_state); + err = pci_set_power_state(pBoard->pDev, PCI_D0); + if (err < 0) { + hailo_err(pBoard, "Failed waking up board %d", err); +@@ -148,7 +141,11 @@ int hailo_pcie_fops_open(struct inode *i + interrupts_enabled_by_filp = true; + } + +- err = hailo_add_notification_wait(pBoard, filp); ++ if (pBoard->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) { ++ err = hailo_nnc_file_context_init(pBoard, context); ++ } else { ++ err = hailo_soc_file_context_init(pBoard, context); ++ } + if (err < 0) { + goto l_release_irq; + } +@@ -166,6 +163,7 @@ l_release_irq: + + l_revert_power_state: + if (pBoard->pDev->current_state != previous_power_state) { ++ hailo_info(pBoard, "Power changing state from %d to %d\n", previous_power_state, pBoard->pDev->current_state); + if (pci_set_power_state(pBoard->pDev, previous_power_state) < 0) { + hailo_err(pBoard, "Failed setting power state back to %d\n", (int)previous_power_state); + } +@@ -180,34 +178,6 @@ l_exit: + return err; + } + +-int hailo_pcie_driver_down(struct hailo_pcie_board *board) +-{ +- long completion_result = 0; +- int err = 0; +- +- reinit_completion(&board->driver_down.reset_completed); +- +- hailo_pcie_write_firmware_driver_shutdown(&board->pcie_resources); +- +- // Wait for response +- completion_result = +- wait_for_completion_timeout(&board->driver_down.reset_completed, msecs_to_jiffies(DEFAULT_SHUTDOWN_TIMEOUT_MS)); +- if (completion_result <= 0) { +- if (0 == completion_result) { +- hailo_err(board, "hailo_pcie_driver_down, timeout waiting for shutdown response (timeout_ms=%d)\n", DEFAULT_SHUTDOWN_TIMEOUT_MS); +- err = -ETIMEDOUT; +- } else { +- hailo_info(board, "hailo_pcie_driver_down, wait for completion failed with err=%ld (process was interrupted or killed)\n", +- completion_result); +- err = completion_result; +- } +- goto l_exit; +- } +- +-l_exit: +- return err; +-} +- + int hailo_pcie_fops_release(struct inode *inode, struct file *filp) + { + struct hailo_pcie_board *board = (struct hailo_pcie_board *)filp->private_data; +@@ -234,12 +204,10 @@ int hailo_pcie_fops_release(struct inode + hailo_err(board, "Invalid file context\n"); + } + +- hailo_pcie_clear_notification_wait_list(board, filp); +- +- if (filp == board->vdma.used_by_filp) { +- if (hailo_pcie_driver_down(board)) { +- hailo_err(board, "Failed sending FW shutdown event"); +- } ++ if (board->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) { ++ hailo_nnc_file_context_finalize(board, context); ++ } else { ++ hailo_soc_file_context_finalize(board, context); + } + + hailo_vdma_file_context_finalize(&context->vdma_context, &board->vdma, filp); +@@ -250,6 +218,7 @@ int hailo_pcie_fops_release(struct inode + hailo_disable_interrupts(board); + + if (power_mode_enabled()) { ++ hailo_info(board, "Power change state to PCI_D3hot\n"); + if (board->pDev && pci_set_power_state(board->pDev, PCI_D3hot) < 0) { + hailo_err(board, "Failed setting power state to D3hot"); + } +@@ -301,44 +270,23 @@ static long hailo_memory_transfer_ioctl( + return err; + } + +-static long hailo_read_log_ioctl(struct hailo_pcie_board *pBoard, unsigned long arg) +-{ +- long err = 0; +- struct hailo_read_log_params params; +- +- if (copy_from_user(¶ms, (void __user*)arg, sizeof(params))) { +- hailo_err(pBoard, "HAILO_READ_LOG, copy_from_user fail\n"); +- return -ENOMEM; +- } +- +- if (0 > (err = hailo_pcie_read_firmware_log(&pBoard->pcie_resources, ¶ms))) { +- hailo_err(pBoard, "HAILO_READ_LOG, reading from log failed with error: %ld \n", err); +- return err; +- } +- +- if (copy_to_user((void*)arg, ¶ms, sizeof(params))) { +- return -ENOMEM; +- } +- +- return 0; +-} +- + static void firmware_notification_irq_handler(struct hailo_pcie_board *board) + { + struct hailo_notification_wait *notif_wait_cursor = NULL; + int err = 0; + unsigned long irq_saved_flags = 0; + +- spin_lock_irqsave(&board->notification_read_spinlock, irq_saved_flags); +- err = hailo_pcie_read_firmware_notification(&board->pcie_resources, &board->notification_cache); +- spin_unlock_irqrestore(&board->notification_read_spinlock, irq_saved_flags); ++ spin_lock_irqsave(&board->nnc.notification_read_spinlock, irq_saved_flags); ++ err = hailo_pcie_read_firmware_notification(&board->pcie_resources.fw_access, &board->nnc.notification_cache); ++ spin_unlock_irqrestore(&board->nnc.notification_read_spinlock, irq_saved_flags); + + if (err < 0) { + hailo_err(board, "Failed reading firmware notification"); + } + else { ++ // TODO: HRT-14502 move interrupt handling to nnc + rcu_read_lock(); +- list_for_each_entry_rcu(notif_wait_cursor, &board->notification_wait_list, notification_wait_list) ++ list_for_each_entry_rcu(notif_wait_cursor, &board->nnc.notification_wait_list, notification_wait_list) + { + complete(¬if_wait_cursor->notification_completion); + } +@@ -374,7 +322,7 @@ irqreturn_t hailo_irqhandler(int irq, vo + + // wake fw_control if needed + if (irq_source.interrupt_bitmask & FW_CONTROL) { +- complete(&board->fw_control.completion); ++ complete(&board->nnc.fw_control.completion); + } + + // wake driver_down if needed +@@ -392,7 +340,14 @@ irqreturn_t hailo_irqhandler(int irq, vo + } + + if (irq_source.interrupt_bitmask & SOC_CONNECT_ACCEPTED) { +- complete_all(&board->soc_connect_accepted); ++ complete_all(&board->soc.control_resp_ready); ++ } ++ ++ if (irq_source.interrupt_bitmask & SOC_CLOSED_IRQ) { ++ hailo_info(board, "hailo_irqhandler - SOC_CLOSED_IRQ\n"); ++ // always use bitmap=0xFFFFFFFF - it is ok to wake all interrupts since each handler will check if the stream was aborted or not. ++ hailo_vdma_wakeup_interrupts(&board->vdma, &board->vdma.vdma_engines[DEFAULT_VDMA_ENGINE_INDEX], ++ 0xFFFFFFFF); + } + + if (0 != irq_source.vdma_channels_bitmap) { +@@ -404,170 +359,11 @@ irqreturn_t hailo_irqhandler(int irq, vo + return return_value; + } + +-static long hailo_get_notification_wait_thread(struct hailo_pcie_board *pBoard, struct file *filp, +- struct hailo_notification_wait **current_waiting_thread) +-{ +- struct hailo_notification_wait *cursor = NULL; +- // note: safe to access without rcu because the notification_wait_list is closed only on file release +- list_for_each_entry(cursor, &pBoard->notification_wait_list, notification_wait_list) +- { +- if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) { +- *current_waiting_thread = cursor; +- return 0; +- } +- } +- +- return -EFAULT; +-} +- +-static long hailo_add_notification_wait(struct hailo_pcie_board *board, struct file *filp) +-{ +- struct hailo_notification_wait *new_notification_wait = NULL; +- if (!(new_notification_wait = kmalloc(sizeof(*new_notification_wait), GFP_KERNEL))) { +- hailo_err(board, "Failed to allocate notification wait structure.\n"); +- return -ENOMEM; +- } +- new_notification_wait->tgid = current->tgid; +- new_notification_wait->filp = filp; +- new_notification_wait->is_disabled = false; +- init_completion(&new_notification_wait->notification_completion); +- list_add_rcu(&new_notification_wait->notification_wait_list, &board->notification_wait_list); +- return 0; +-} +- +-static long hailo_read_notification_ioctl(struct hailo_pcie_board *pBoard, unsigned long arg, struct file *filp, +- bool* should_up_board_mutex) +-{ +- long err = 0; +- struct hailo_notification_wait *current_waiting_thread = NULL; +- struct hailo_d2h_notification *notification = &pBoard->notification_to_user; +- unsigned long irq_saved_flags; +- +- err = hailo_get_notification_wait_thread(pBoard, filp, ¤t_waiting_thread); +- if (0 != err) { +- goto l_exit; +- } +- up(&pBoard->mutex); +- +- if (0 > (err = wait_for_completion_interruptible(¤t_waiting_thread->notification_completion))) { +- hailo_info(pBoard, +- "HAILO_READ_NOTIFICATION - wait_for_completion_interruptible error. err=%ld. tgid=%d (process was interrupted or killed)\n", +- err, current_waiting_thread->tgid); +- *should_up_board_mutex = false; +- goto l_exit; +- } +- +- if (down_interruptible(&pBoard->mutex)) { +- hailo_info(pBoard, "HAILO_READ_NOTIFICATION - down_interruptible error (process was interrupted or killed)\n"); +- *should_up_board_mutex = false; +- err = -ERESTARTSYS; +- goto l_exit; +- } +- +- // Check if was disabled +- if (current_waiting_thread->is_disabled) { +- hailo_info(pBoard, "HAILO_READ_NOTIFICATION, can't find notification wait for tgid=%d\n", current->tgid); +- err = -EINVAL; +- goto l_exit; +- } +- +- reinit_completion(¤t_waiting_thread->notification_completion); +- +- spin_lock_irqsave(&pBoard->notification_read_spinlock, irq_saved_flags); +- notification->buffer_len = pBoard->notification_cache.buffer_len; +- memcpy(notification->buffer, pBoard->notification_cache.buffer, notification->buffer_len); +- spin_unlock_irqrestore(&pBoard->notification_read_spinlock, irq_saved_flags); +- +- if (copy_to_user((void __user*)arg, notification, sizeof(*notification))) { +- hailo_err(pBoard, "HAILO_READ_NOTIFICATION copy_to_user fail\n"); +- err = -ENOMEM; +- goto l_exit; +- } +- +-l_exit: +- return err; +-} +- +-static long hailo_disable_notification(struct hailo_pcie_board *pBoard, struct file *filp) +-{ +- struct hailo_notification_wait *cursor = NULL; +- +- hailo_info(pBoard, "HAILO_DISABLE_NOTIFICATION: disable notification"); +- rcu_read_lock(); +- list_for_each_entry_rcu(cursor, &pBoard->notification_wait_list, notification_wait_list) { +- if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) { +- cursor->is_disabled = true; +- complete(&cursor->notification_completion); +- break; +- } +- } +- rcu_read_unlock(); +- +- return 0; +-} +- +-static int hailo_fw_control(struct hailo_pcie_board *pBoard, unsigned long arg, bool* should_up_board_mutex) +-{ +- struct hailo_fw_control *command = &pBoard->fw_control.command; +- long completion_result = 0; +- int err = 0; +- +- up(&pBoard->mutex); +- *should_up_board_mutex = false; +- +- if (down_interruptible(&pBoard->fw_control.mutex)) { +- hailo_info(pBoard, "hailo_fw_control down_interruptible fail tgid:%d (process was interrupted or killed)\n", current->tgid); +- return -ERESTARTSYS; +- } +- +- if (copy_from_user(command, (void __user*)arg, sizeof(*command))) { +- hailo_err(pBoard, "hailo_fw_control, copy_from_user fail\n"); +- err = -ENOMEM; +- goto l_exit; +- } +- +- reinit_completion(&pBoard->fw_control.completion); +- +- err = hailo_pcie_write_firmware_control(&pBoard->pcie_resources, command); +- if (err < 0) { +- hailo_err(pBoard, "Failed writing fw control to pcie\n"); +- goto l_exit; +- } +- +- // Wait for response +- completion_result = wait_for_completion_interruptible_timeout(&pBoard->fw_control.completion, msecs_to_jiffies(command->timeout_ms)); +- if (completion_result <= 0) { +- if (0 == completion_result) { +- hailo_err(pBoard, "hailo_fw_control, timeout waiting for control (timeout_ms=%d)\n", command->timeout_ms); +- err = -ETIMEDOUT; +- } else { +- hailo_info(pBoard, "hailo_fw_control, wait for completion failed with err=%ld (process was interrupted or killed)\n", completion_result); +- err = -EINTR; +- } +- goto l_exit; +- } +- +- err = hailo_pcie_read_firmware_control(&pBoard->pcie_resources, command); +- if (err < 0) { +- hailo_err(pBoard, "Failed reading fw control from pcie\n"); +- goto l_exit; +- } +- +- if (copy_to_user((void __user*)arg, command, sizeof(*command))) { +- hailo_err(pBoard, "hailo_fw_control, copy_to_user fail\n"); +- err = -ENOMEM; +- goto l_exit; +- } +- +-l_exit: +- up(&pBoard->fw_control.mutex); +- return err; +-} +- + static long hailo_query_device_properties(struct hailo_pcie_board *board, unsigned long arg) + { + struct hailo_device_properties props = { + .desc_max_page_size = board->desc_max_page_size, ++ .board_type = board->pcie_resources.board_type, + .allocation_mode = board->allocation_mode, + .dma_type = HAILO_DMA_TYPE_PCIE, + .dma_engines_count = board->vdma.vdma_engines_count, +@@ -618,24 +414,6 @@ static long hailo_general_ioctl(struct h + } + } + +-static long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg, +- struct file *filp, bool *should_up_board_mutex) +-{ +- switch (cmd) { +- case HAILO_FW_CONTROL: +- return hailo_fw_control(board, arg, should_up_board_mutex); +- case HAILO_READ_NOTIFICATION: +- return hailo_read_notification_ioctl(board, arg, filp, should_up_board_mutex); +- case HAILO_DISABLE_NOTIFICATION: +- return hailo_disable_notification(board, filp); +- case HAILO_READ_LOG: +- return hailo_read_log_ioctl(board, arg); +- default: +- hailo_err(board, "Invalid nnc ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); +- return -ENOTTY; +- } +-} +- + long hailo_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg) + { + long err = 0; +@@ -694,7 +472,7 @@ long hailo_pcie_fops_unlockedioctl(struc + hailo_err(board, "Ioctl %d is not supported on this accelerator type\n", _IOC_TYPE(cmd)); + err = -EINVAL; + } else { +- err = hailo_soc_ioctl(board, &context->vdma_context, &board->vdma, cmd, arg); ++ err = hailo_soc_ioctl(board, context, &board->vdma, cmd, arg); + } + break; + case HAILO_NNC_IOCTL_MAGIC: +--- a/drivers/media/pci/hailo/src/fops.h ++++ b/drivers/media/pci/hailo/src/fops.h +@@ -1,16 +1,17 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_PCI_FOPS_H_ + #define _HAILO_PCI_FOPS_H_ + ++#include "pcie.h" ++ + int hailo_pcie_fops_open(struct inode* inode, struct file* filp); + int hailo_pcie_fops_release(struct inode* inode, struct file* filp); + long hailo_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg); + int hailo_pcie_fops_mmap(struct file* filp, struct vm_area_struct *vma); +-int hailo_pcie_driver_down(struct hailo_pcie_board *board); + void hailo_pcie_ep_init(struct hailo_pcie_board *board); + + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) +--- /dev/null ++++ b/drivers/media/pci/hailo/src/nnc.c +@@ -0,0 +1,299 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/** ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ ++/** ++ * A Hailo PCIe NNC device is a device contains a NNC (neural network core) and some basic FW. ++ * The device supports sending controls, receiving notification and reading the FW log. ++ */ ++ ++#include "nnc.h" ++#include "hailo_ioctl_common.h" ++ ++#include "utils/logs.h" ++#include "utils/compact.h" ++ ++#include ++ ++#if !defined(HAILO_EMULATOR) ++#define DEFAULT_SHUTDOWN_TIMEOUT_MS (5) ++#else /* !defined(HAILO_EMULATOR) */ ++#define DEFAULT_SHUTDOWN_TIMEOUT_MS (1000) ++#endif /* !defined(HAILO_EMULATOR) */ ++ ++void hailo_nnc_init(struct hailo_pcie_nnc *nnc) ++{ ++ sema_init(&nnc->fw_control.mutex, 1); ++ spin_lock_init(&nnc->notification_read_spinlock); ++ init_completion(&nnc->fw_control.completion); ++ INIT_LIST_HEAD(&nnc->notification_wait_list); ++ memset(&nnc->notification_cache, 0, sizeof(nnc->notification_cache)); ++} ++ ++void hailo_nnc_finalize(struct hailo_pcie_nnc *nnc) ++{ ++ struct hailo_notification_wait *cursor = NULL; ++ ++ // Lock rcu_read_lock and send notification_completion to wake anyone waiting on the notification_wait_list when removed ++ rcu_read_lock(); ++ list_for_each_entry_rcu(cursor, &nnc->notification_wait_list, notification_wait_list) { ++ cursor->is_disabled = true; ++ complete(&cursor->notification_completion); ++ } ++ rcu_read_unlock(); ++} ++ ++static int hailo_fw_control(struct hailo_pcie_board *board, unsigned long arg, bool* should_up_board_mutex) ++{ ++ struct hailo_fw_control *command = &board->nnc.fw_control.command; ++ long completion_result = 0; ++ int err = 0; ++ ++ up(&board->mutex); ++ *should_up_board_mutex = false; ++ ++ if (down_interruptible(&board->nnc.fw_control.mutex)) { ++ hailo_info(board, "hailo_fw_control down_interruptible fail tgid:%d (process was interrupted or killed)\n", current->tgid); ++ return -ERESTARTSYS; ++ } ++ ++ if (copy_from_user(command, (void __user*)arg, sizeof(*command))) { ++ hailo_err(board, "hailo_fw_control, copy_from_user fail\n"); ++ err = -ENOMEM; ++ goto l_exit; ++ } ++ ++ reinit_completion(&board->nnc.fw_control.completion); ++ ++ err = hailo_pcie_write_firmware_control(&board->pcie_resources, command); ++ if (err < 0) { ++ hailo_err(board, "Failed writing fw control to pcie\n"); ++ goto l_exit; ++ } ++ ++ // Wait for response ++ completion_result = wait_for_completion_interruptible_timeout(&board->nnc.fw_control.completion, msecs_to_jiffies(command->timeout_ms)); ++ if (completion_result <= 0) { ++ if (0 == completion_result) { ++ hailo_err(board, "hailo_fw_control, timeout waiting for control (timeout_ms=%d)\n", command->timeout_ms); ++ err = -ETIMEDOUT; ++ } else { ++ hailo_info(board, "hailo_fw_control, wait for completion failed with err=%ld (process was interrupted or killed)\n", completion_result); ++ err = -EINTR; ++ } ++ goto l_exit; ++ } ++ ++ err = hailo_pcie_read_firmware_control(&board->pcie_resources, command); ++ if (err < 0) { ++ hailo_err(board, "Failed reading fw control from pcie\n"); ++ goto l_exit; ++ } ++ ++ if (copy_to_user((void __user*)arg, command, sizeof(*command))) { ++ hailo_err(board, "hailo_fw_control, copy_to_user fail\n"); ++ err = -ENOMEM; ++ goto l_exit; ++ } ++ ++l_exit: ++ up(&board->nnc.fw_control.mutex); ++ return err; ++} ++ ++static long hailo_get_notification_wait_thread(struct hailo_pcie_board *board, struct file *filp, ++ struct hailo_notification_wait **current_waiting_thread) ++{ ++ struct hailo_notification_wait *cursor = NULL; ++ // note: safe to access without rcu because the notification_wait_list is closed only on file release ++ list_for_each_entry(cursor, &board->nnc.notification_wait_list, notification_wait_list) ++ { ++ if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) { ++ *current_waiting_thread = cursor; ++ return 0; ++ } ++ } ++ ++ return -EFAULT; ++} ++ ++static long hailo_read_notification_ioctl(struct hailo_pcie_board *board, unsigned long arg, struct file *filp, ++ bool* should_up_board_mutex) ++{ ++ long err = 0; ++ struct hailo_notification_wait *current_waiting_thread = NULL; ++ struct hailo_d2h_notification *notification = &board->nnc.notification_to_user; ++ unsigned long irq_saved_flags; ++ ++ err = hailo_get_notification_wait_thread(board, filp, ¤t_waiting_thread); ++ if (0 != err) { ++ goto l_exit; ++ } ++ up(&board->mutex); ++ ++ if (0 > (err = wait_for_completion_interruptible(¤t_waiting_thread->notification_completion))) { ++ hailo_info(board, ++ "HAILO_READ_NOTIFICATION - wait_for_completion_interruptible error. err=%ld. tgid=%d (process was interrupted or killed)\n", ++ err, current_waiting_thread->tgid); ++ *should_up_board_mutex = false; ++ goto l_exit; ++ } ++ ++ if (down_interruptible(&board->mutex)) { ++ hailo_info(board, "HAILO_READ_NOTIFICATION - down_interruptible error (process was interrupted or killed)\n"); ++ *should_up_board_mutex = false; ++ err = -ERESTARTSYS; ++ goto l_exit; ++ } ++ ++ // Check if was disabled ++ if (current_waiting_thread->is_disabled) { ++ hailo_info(board, "HAILO_READ_NOTIFICATION, can't find notification wait for tgid=%d\n", current->tgid); ++ err = -EINVAL; ++ goto l_exit; ++ } ++ ++ reinit_completion(¤t_waiting_thread->notification_completion); ++ ++ spin_lock_irqsave(&board->nnc.notification_read_spinlock, irq_saved_flags); ++ notification->buffer_len = board->nnc.notification_cache.buffer_len; ++ memcpy(notification->buffer, board->nnc.notification_cache.buffer, notification->buffer_len); ++ spin_unlock_irqrestore(&board->nnc.notification_read_spinlock, irq_saved_flags); ++ ++ if (copy_to_user((void __user*)arg, notification, sizeof(*notification))) { ++ hailo_err(board, "HAILO_READ_NOTIFICATION copy_to_user fail\n"); ++ err = -ENOMEM; ++ goto l_exit; ++ } ++ ++l_exit: ++ return err; ++} ++ ++static long hailo_disable_notification(struct hailo_pcie_board *board, struct file *filp) ++{ ++ struct hailo_notification_wait *cursor = NULL; ++ ++ hailo_info(board, "HAILO_DISABLE_NOTIFICATION: disable notification"); ++ rcu_read_lock(); ++ list_for_each_entry_rcu(cursor, &board->nnc.notification_wait_list, notification_wait_list) { ++ if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) { ++ cursor->is_disabled = true; ++ complete(&cursor->notification_completion); ++ break; ++ } ++ } ++ rcu_read_unlock(); ++ ++ return 0; ++} ++ ++static long hailo_read_log_ioctl(struct hailo_pcie_board *board, unsigned long arg) ++{ ++ long err = 0; ++ struct hailo_read_log_params params; ++ ++ if (copy_from_user(¶ms, (void __user*)arg, sizeof(params))) { ++ hailo_err(board, "HAILO_READ_LOG, copy_from_user fail\n"); ++ return -ENOMEM; ++ } ++ ++ if (0 > (err = hailo_pcie_read_firmware_log(&board->pcie_resources.fw_access, ¶ms))) { ++ hailo_err(board, "HAILO_READ_LOG, reading from log failed with error: %ld \n", err); ++ return err; ++ } ++ ++ if (copy_to_user((void*)arg, ¶ms, sizeof(params))) { ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg, ++ struct file *filp, bool *should_up_board_mutex) ++{ ++ switch (cmd) { ++ case HAILO_FW_CONTROL: ++ return hailo_fw_control(board, arg, should_up_board_mutex); ++ case HAILO_READ_NOTIFICATION: ++ return hailo_read_notification_ioctl(board, arg, filp, should_up_board_mutex); ++ case HAILO_DISABLE_NOTIFICATION: ++ return hailo_disable_notification(board, filp); ++ case HAILO_READ_LOG: ++ return hailo_read_log_ioctl(board, arg); ++ default: ++ hailo_err(board, "Invalid nnc ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); ++ return -ENOTTY; ++ } ++} ++ ++ ++static int add_notification_wait(struct hailo_pcie_board *board, struct file *filp) ++{ ++ struct hailo_notification_wait *wait = kmalloc(sizeof(*wait), GFP_KERNEL); ++ if (!wait) { ++ hailo_err(board, "Failed to allocate notification wait structure.\n"); ++ return -ENOMEM; ++ } ++ wait->tgid = current->tgid; ++ wait->filp = filp; ++ wait->is_disabled = false; ++ init_completion(&wait->notification_completion); ++ list_add_rcu(&wait->notification_wait_list, &board->nnc.notification_wait_list); ++ return 0; ++} ++ ++int hailo_nnc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context) ++{ ++ return add_notification_wait(board, context->filp); ++} ++ ++static void clear_notification_wait_list(struct hailo_pcie_board *board, struct file *filp) ++{ ++ struct hailo_notification_wait *cur = NULL, *next = NULL; ++ list_for_each_entry_safe(cur, next, &board->nnc.notification_wait_list, notification_wait_list) { ++ if (cur->filp == filp) { ++ list_del_rcu(&cur->notification_wait_list); ++ synchronize_rcu(); ++ kfree(cur); ++ } ++ } ++} ++ ++int hailo_nnc_driver_down(struct hailo_pcie_board *board) ++{ ++ long completion_result = 0; ++ int err = 0; ++ ++ reinit_completion(&board->driver_down.reset_completed); ++ ++ hailo_pcie_write_firmware_driver_shutdown(&board->pcie_resources); ++ ++ // Wait for response ++ completion_result = ++ wait_for_completion_timeout(&board->driver_down.reset_completed, msecs_to_jiffies(DEFAULT_SHUTDOWN_TIMEOUT_MS)); ++ if (completion_result <= 0) { ++ if (0 == completion_result) { ++ hailo_err(board, "hailo_nnc_driver_down, timeout waiting for shutdown response (timeout_ms=%d)\n", DEFAULT_SHUTDOWN_TIMEOUT_MS); ++ err = -ETIMEDOUT; ++ } else { ++ hailo_info(board, "hailo_nnc_driver_down, wait for completion failed with err=%ld (process was interrupted or killed)\n", ++ completion_result); ++ err = completion_result; ++ } ++ goto l_exit; ++ } ++ ++l_exit: ++ return err; ++} ++ ++void hailo_nnc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context) ++{ ++ clear_notification_wait_list(board, context->filp); ++ ++ if (context->filp == board->vdma.used_by_filp) { ++ hailo_nnc_driver_down(board); ++ } ++} +\ No newline at end of file +--- /dev/null ++++ b/drivers/media/pci/hailo/src/nnc.h +@@ -0,0 +1,22 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/** ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ ++ ++#ifndef _HAILO_PCI_NNC_H_ ++#define _HAILO_PCI_NNC_H_ ++ ++#include "pcie.h" ++ ++void hailo_nnc_init(struct hailo_pcie_nnc *nnc); ++void hailo_nnc_finalize(struct hailo_pcie_nnc *nnc); ++ ++long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg, ++ struct file *filp, bool *should_up_board_mutex); ++ ++int hailo_nnc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context); ++void hailo_nnc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context); ++ ++int hailo_nnc_driver_down(struct hailo_pcie_board *board); ++ ++#endif /* _HAILO_PCI_NNC_H_ */ +\ No newline at end of file +--- a/drivers/media/pci/hailo/src/pci_soc_ioctl.c ++++ /dev/null +@@ -1,155 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/** +- * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. +- **/ +-#include "pci_soc_ioctl.h" +- +-#include "utils.h" +-#include "vdma_common.h" +-#include "utils/logs.h" +-#include "vdma/memory.h" +- +-#define PCI_SOC_VDMA_ENGINE_INDEX (0) +-#define PCI_SOC_WAIT_FOR_CONNECT_TIMEOUT_MS (10000) +- +-long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context, +- struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg) +-{ +- switch (cmd) { +- case HAILO_SOC_CONNECT: +- return hailo_soc_connect_ioctl(board, context, controller, arg); +- case HAILO_SOC_CLOSE: +- return hailo_soc_close_ioctl(board, controller, arg); +- default: +- hailo_err(board, "Invalid pcie EP ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); +- return -ENOTTY; +- } +-} +- +-long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context, +- struct hailo_vdma_controller *controller, unsigned long arg) +-{ +- struct hailo_soc_connect_params params; +- struct hailo_vdma_channel *input_channel = NULL; +- struct hailo_vdma_channel *output_channel = NULL; +- struct hailo_vdma_engine *vdma_engine = NULL; +- struct hailo_descriptors_list_buffer *input_descriptors_buffer = NULL; +- struct hailo_descriptors_list_buffer *output_descriptors_buffer = NULL; +- uint8_t depth = 0; +- int err = 0; +- long completion_result = 0; +- +- if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { +- hailo_err(board, "copy_from_user fail\n"); +- return -ENOMEM; +- } +- +- // TODO: have pci_ep choose the channel indexes the soc will use - for now use 0 and 16 +- params.input_channel_index = 0; +- params.output_channel_index = 16; +- +- reinit_completion(&board->soc_connect_accepted); +- hailo_soc_write_soc_connect(&board->pcie_resources); +- +- // Wait for completion +- completion_result = wait_for_completion_interruptible_timeout(&board->soc_connect_accepted, +- msecs_to_jiffies(PCI_SOC_WAIT_FOR_CONNECT_TIMEOUT_MS)); +- if (0 > completion_result) { +- if (0 == completion_result) { +- hailo_err(board, "Timeout waiting for connect to be accepted (timeout_ms=%d)\n", PCI_SOC_WAIT_FOR_CONNECT_TIMEOUT_MS); +- return -ETIMEDOUT; +- } else { +- hailo_info(board, "soc connect failed with err=%ld (process was interrupted or killed)\n", +- completion_result); +- return -EINTR; +- } +- } +- +- vdma_engine = &controller->vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX]; +- input_channel = &vdma_engine->channels[params.input_channel_index]; +- output_channel = &vdma_engine->channels[params.output_channel_index]; +- +- input_descriptors_buffer = hailo_vdma_find_descriptors_buffer(context, params.input_desc_handle); +- output_descriptors_buffer = hailo_vdma_find_descriptors_buffer(context, params.output_desc_handle); +- if (NULL == input_descriptors_buffer || NULL == output_descriptors_buffer) { +- hailo_dev_err(&board->pDev->dev, "input / output descriptors buffer not found \n"); +- return -EINVAL; +- } +- +- // Make sure channels that we are accepting are not already enabled +- if (0 != (vdma_engine->enabled_channels & params.input_channel_index) || +- 0 != (vdma_engine->enabled_channels & params.output_channel_index)) { +- hailo_dev_err(&board->pDev->dev, "Trying to accept already enabled channels\n"); +- return -EINVAL; +- } +- +- if (!is_powerof2((size_t)input_descriptors_buffer->desc_list.desc_count) || +- !is_powerof2((size_t)output_descriptors_buffer->desc_list.desc_count)) { +- hailo_dev_err(&board->pDev->dev, "Invalid desc list size\n"); +- return -EINVAL; +- } +- +- // configure and start input channel +- depth = ceil_log2(input_descriptors_buffer->desc_list.desc_count); +- // DMA Direction is only to get channel index - so +- err = hailo_vdma_start_channel(input_channel->host_regs, input_descriptors_buffer->dma_address, depth, +- board->vdma.hw->ddr_data_id); +- if (err < 0) { +- hailo_dev_err(&board->pDev->dev, "Error starting vdma input channel index %u\n", params.input_channel_index); +- return -EINVAL; +- } +- +- // configure and start output channel +- depth = ceil_log2(output_descriptors_buffer->desc_list.desc_count); +- // DMA Direction is only to get channel index - so +- err = hailo_vdma_start_channel(output_channel->host_regs, output_descriptors_buffer->dma_address, depth, +- board->vdma.hw->ddr_data_id); +- if (err < 0) { +- hailo_dev_err(&board->pDev->dev, "Error starting vdma output channel index %u\n", params.output_channel_index); +- // Close input channel +- hailo_vdma_stop_channel(input_channel->host_regs); +- return -EINVAL; +- } +- +- if (copy_to_user((void *)arg, ¶ms, sizeof(params))) { +- hailo_dev_err(&board->pDev->dev, "copy_to_user fail\n"); +- return -ENOMEM; +- } +- +- return 0; +-} +- +-long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, unsigned long arg) +-{ +- struct hailo_soc_close_params params; +- struct hailo_vdma_channel *input_channel = NULL; +- struct hailo_vdma_channel *output_channel = NULL; +- struct hailo_vdma_engine *vdma_engine = NULL; +- +- if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { +- hailo_dev_err(&board->pDev->dev, "copy_from_user fail\n"); +- return -ENOMEM; +- } +- +- vdma_engine = &controller->vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX]; +- +- if (!hailo_check_channel_index(params.input_channel_index, controller->hw->src_channels_bitmask, true)) { +- hailo_dev_err(&board->pDev->dev, "Invalid input channel index %u\n", params.input_channel_index); +- return -EINVAL; +- } +- +- if (!hailo_check_channel_index(params.output_channel_index, controller->hw->src_channels_bitmask, false)) { +- hailo_dev_err(&board->pDev->dev, "Invalid output channel index %u\n", params.output_channel_index); +- return -EINVAL; +- } +- +- input_channel = &vdma_engine->channels[params.input_channel_index]; +- output_channel = &vdma_engine->channels[params.output_channel_index]; +- +- // Close channels +- hailo_vdma_stop_channel(input_channel->host_regs); +- hailo_vdma_stop_channel(output_channel->host_regs); +- +- hailo_pcie_write_firmware_driver_shutdown(&board->pcie_resources); +- return 0; +-} +\ No newline at end of file +--- a/drivers/media/pci/hailo/src/pcie.c ++++ b/drivers/media/pci/hailo/src/pcie.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include +@@ -22,6 +22,8 @@ + + #include "hailo_ioctl_common.h" + #include "pcie.h" ++#include "nnc.h" ++#include "soc.h" + #include "fops.h" + #include "sysfs.h" + #include "utils/logs.h" +@@ -40,11 +42,12 @@ enum hailo_allocate_driver_buffer_driver + HAILO_FORCE_BUFFER_FROM_DRIVER = 2, + }; + +-//Debug flag ++// Debug flag + static int force_desc_page_size = 0; + static bool g_is_power_mode_enabled = true; + static int force_allocation_from_driver = HAILO_NO_FORCE_BUFFER; + static bool force_hailo15_legacy_mode = false; ++static bool force_boot_linux_from_eemc = false; + + #define DEVICE_NODE_NAME "hailo" + static int char_major = 0; +@@ -206,7 +209,7 @@ static int hailo_pcie_disable_aspm(struc + /* Double-check ASPM control. If not disabled by the above, the + * BIOS is preventing that from happening (or CONFIG_PCIEASPM is + * not enabled); override by writing PCI config space directly. +- */ ++ */ + err = pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &pdev_aspmc); + if (err < 0) { + hailo_err(board, "Couldn't read LNKCTL capability\n"); +@@ -288,101 +291,59 @@ static void hailo_pcie_remove_board(stru + up(&g_hailo_add_board_mutex); + } + +-static int hailo_write_config(struct hailo_pcie_resources *resources, struct device *dev, +- const struct hailo_config_constants *config_consts) +-{ +- const struct firmware *config = NULL; +- int err = 0; +- +- if (NULL == config_consts->filename) { +- // Config not supported for platform +- return 0; +- } +- +- err = request_firmware_direct(&config, config_consts->filename, dev); +- if (err < 0) { +- hailo_dev_info(dev, "Config %s not found\n", config_consts->filename); +- return 0; +- } +- +- hailo_dev_notice(dev, "Writing config %s\n", config_consts->filename); +- +- err = hailo_pcie_write_config_common(resources, config->data, config->size, config_consts); +- if (err < 0) { +- if (-EINVAL == err) { +- hailo_dev_warn(dev, "Config size %zu is bigger than max %zu\n", config->size, config_consts->max_size); +- } +- release_firmware(config); +- return err; +- } +- +- release_firmware(config); +- return 0; +-} +- + static bool wait_for_firmware_completion(struct completion *fw_load_completion) + { + return (0 != wait_for_completion_timeout(fw_load_completion, msecs_to_jiffies(FIRMWARE_WAIT_TIMEOUT_MS))); + } + +-static int hailo_load_firmware(struct hailo_pcie_resources *resources, ++static int hailo_load_soc_firmware(struct hailo_pcie_resources *resources, + struct device *dev, struct completion *fw_load_completion) + { +- const struct firmware *firmware = NULL; +- int err = 0; + u32 boot_status = 0; ++ int err = 0; ++ u32 second_stage = force_boot_linux_from_eemc ? SECOND_STAGE_LINUX_IN_EMMC : SECOND_STAGE; + + if (hailo_pcie_is_firmware_loaded(resources)) { +- hailo_dev_warn(dev, "Firmware was already loaded\n"); ++ hailo_dev_warn(dev, "Firmware batch was already loaded\n"); + return 0; + } + +- reinit_completion(fw_load_completion); +- +- err = hailo_write_config(resources, dev, hailo_pcie_get_board_config_constants(resources->board_type)); +- if (err < 0) { +- hailo_dev_err(dev, "Failed writing board config"); +- return err; +- } ++ init_completion(fw_load_completion); + +- err = hailo_write_config(resources, dev, hailo_pcie_get_user_config_constants(resources->board_type)); ++ err = hailo_pcie_write_firmware_batch(dev, resources, FIRST_STAGE); + if (err < 0) { +- hailo_dev_err(dev, "Failed writing fw config"); ++ hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err); + return err; + } + +- // read firmware file +- err = request_firmware_direct(&firmware, hailo_pcie_get_fw_filename(resources->board_type), dev); +- if (err < 0) { +- hailo_dev_warn(dev, "Firmware file not found (/lib/firmware/%s), please upload the firmware manually \n", +- hailo_pcie_get_fw_filename(resources->board_type)); +- return 0; ++ if (!wait_for_firmware_completion(fw_load_completion)) { ++ boot_status = hailo_get_boot_status(resources); ++ hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status); ++ return -ETIMEDOUT; + } ++ reinit_completion(fw_load_completion); + +- err = hailo_pcie_write_firmware(resources, firmware->data, firmware->size); ++ err = hailo_pcie_write_firmware_batch(dev, resources, second_stage); + if (err < 0) { +- hailo_dev_err(dev, "Failed writing firmware. err %d\n", err); +- release_firmware(firmware); ++ hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err); + return err; + } + +- release_firmware(firmware); +- + if (!wait_for_firmware_completion(fw_load_completion)) { + boot_status = hailo_get_boot_status(resources); + hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status); + return -ETIMEDOUT; + } + +- hailo_dev_notice(dev, "Firmware was loaded successfully\n"); ++ hailo_dev_notice(dev, "Firmware Batch loaded successfully\n"); ++ + return 0; + } + +-static int hailo_load_firmware_batch(struct hailo_pcie_resources *resources, ++static int hailo_load_nnc_firmware(struct hailo_pcie_resources *resources, + struct device *dev, struct completion *fw_load_completion) + { + u32 boot_status = 0; +- u32 pcie_finished = 1; + int err = 0; + + if (hailo_pcie_is_firmware_loaded(resources)) { +@@ -398,31 +359,13 @@ static int hailo_load_firmware_batch(str + return err; + } + +- hailo_trigger_firmware_boot(resources); +- + if (!wait_for_firmware_completion(fw_load_completion)) { + boot_status = hailo_get_boot_status(resources); + hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status); + return -ETIMEDOUT; + } +- reinit_completion(fw_load_completion); + +- err = hailo_pcie_write_firmware_batch(dev, resources, SECOND_STAGE); +- if (err < 0) { +- hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err); +- return err; +- } +- +- // TODO: HRT-13838 - Remove, move address to compat, make write_memory static +- write_memory(resources, 0x84000000, (void*)&pcie_finished, sizeof(pcie_finished)); +- +- if (!wait_for_firmware_completion(fw_load_completion)) { +- boot_status = hailo_get_boot_status(resources); +- hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status); +- return -ETIMEDOUT; +- } +- +- hailo_dev_notice(dev, "Firmware Batch loaded successfully\n"); ++ hailo_dev_notice(dev, "Firmware loaded successfully\n"); + + return 0; + } +@@ -439,15 +382,13 @@ static int hailo_activate_board(struct h + return err; + } + +- switch (board->pcie_resources.board_type) { +- case HAILO_BOARD_TYPE_HAILO10H: +- err = hailo_load_firmware_batch(&board->pcie_resources, &board->pDev->dev, ++ switch (board->pcie_resources.accelerator_type) { ++ case HAILO_ACCELERATOR_TYPE_SOC: ++ err = hailo_load_soc_firmware(&board->pcie_resources, &board->pDev->dev, + &board->fw_loaded_completion); + break; +- case HAILO_BOARD_TYPE_HAILO10H_LEGACY: +- case HAILO_BOARD_TYPE_PLUTO: +- case HAILO_BOARD_TYPE_HAILO8: +- err = hailo_load_firmware(&board->pcie_resources, &board->pDev->dev, ++ case HAILO_ACCELERATOR_TYPE_NNC: ++ err = hailo_load_nnc_firmware(&board->pcie_resources, &board->pDev->dev, + &board->fw_loaded_completion); + break; + default: +@@ -464,6 +405,7 @@ static int hailo_activate_board(struct h + + if (power_mode_enabled()) { + // Setting the device to low power state, until the user opens the device ++ hailo_info(board, "Power change state to PCI_D3hot\n"); + err = pci_set_power_state(board->pDev, PCI_D3hot); + if (err < 0) { + hailo_err(board, "Set power state failed %d\n", err); +@@ -755,21 +697,17 @@ static int hailo_pcie_probe(struct pci_d + + pBoard->interrupts_enabled = false; + init_completion(&pBoard->fw_loaded_completion); +- init_completion(&pBoard->soc_connect_accepted); + + sema_init(&pBoard->mutex, 1); + atomic_set(&pBoard->ref_count, 0); + INIT_LIST_HEAD(&pBoard->open_files_list); + +- sema_init(&pBoard->fw_control.mutex, 1); +- spin_lock_init(&pBoard->notification_read_spinlock); +- init_completion(&pBoard->fw_control.completion); ++ // Init both soc and nnc, since the interrupts are shared. ++ hailo_nnc_init(&pBoard->nnc); ++ hailo_soc_init(&pBoard->soc); + + init_completion(&pBoard->driver_down.reset_completed); + +- INIT_LIST_HEAD(&pBoard->notification_wait_list); +- +- memset(&pBoard->notification_cache, 0, sizeof(pBoard->notification_cache)); + memset(&pBoard->memory_transfer_params, 0, sizeof(pBoard->memory_transfer_params)); + + err = hailo_pcie_vdma_controller_init(&pBoard->vdma, &pBoard->pDev->dev, +@@ -832,7 +770,6 @@ probe_exit: + static void hailo_pcie_remove(struct pci_dev* pDev) + { + struct hailo_pcie_board* pBoard = (struct hailo_pcie_board*) pci_get_drvdata(pDev); +- struct hailo_notification_wait *cursor = NULL; + + pci_notice(pDev, "Remove: Releasing board\n"); + +@@ -864,13 +801,7 @@ static void hailo_pcie_remove(struct pci + + pci_set_drvdata(pDev, NULL); + +- // Lock rcu_read_lock and send notification_completion to wake anyone waiting on the notification_wait_list when removed +- rcu_read_lock(); +- list_for_each_entry_rcu(cursor, &pBoard->notification_wait_list, notification_wait_list) { +- cursor->is_disabled = true; +- complete(&cursor->notification_completion); +- } +- rcu_read_unlock(); ++ hailo_nnc_finalize(&pBoard->nnc); + + up(&pBoard->mutex); + +@@ -889,6 +820,15 @@ static void hailo_pcie_remove(struct pci + + } + ++inline int driver_down(struct hailo_pcie_board *board) ++{ ++ if (board->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) { ++ return hailo_nnc_driver_down(board); ++ } else { ++ return hailo_soc_driver_down(board); ++ } ++} ++ + #ifdef CONFIG_PM_SLEEP + static int hailo_pcie_suspend(struct device *dev) + { +@@ -899,17 +839,16 @@ static int hailo_pcie_suspend(struct dev + // lock board to wait for any pending operations + down(&board->mutex); + +- // Disable all interrupts. All interrupts from Hailo chip would be masked. +- hailo_disable_interrupts(board); +- +- // Close all vDMA channels + if (board->vdma.used_by_filp != NULL) { +- err = hailo_pcie_driver_down(board); ++ err = driver_down(board); + if (err < 0) { + dev_notice(dev, "Error while trying to call FW to close vdma channels\n"); + } + } + ++ // Disable all interrupts. All interrupts from Hailo chip would be masked. ++ hailo_disable_interrupts(board); ++ + // Un validate all activae file contexts so every new action would return error to the user. + list_for_each_entry(cur, &board->open_files_list, open_files_list) { + cur->is_valid = false; +@@ -919,8 +858,8 @@ static int hailo_pcie_suspend(struct dev + up(&board->mutex); + + dev_notice(dev, "PM's suspend\n"); +- // Continue system suspend +- return err; ++ // Success Oriented - Continue system suspend even in case of error (otherwise system will not suspend correctly) ++ return 0; + } + + static int hailo_pcie_resume(struct device *dev) +@@ -930,10 +869,10 @@ static int hailo_pcie_resume(struct devi + + if ((err = hailo_activate_board(board)) < 0) { + dev_err(dev, "Failed activating board %d\n", err); +- return err; + } + + dev_notice(dev, "PM's resume\n"); ++ // Success Oriented - Continue system resume even in case of error (otherwise system will not suspend correctly) + return 0; + } + #endif /* CONFIG_PM_SLEEP */ +@@ -954,7 +893,7 @@ static void hailo_pci_reset_prepare(stru + down(&board->mutex); + if (board->vdma.used_by_filp != NULL) { + // Try to close all vDMA channels before reset +- err = hailo_pcie_driver_down(board); ++ err = driver_down(board); + if (err < 0) { + pci_err(pdev, "Error while trying to call FW to close vdma channels (errno %d)\n", err); + } +@@ -1088,6 +1027,9 @@ MODULE_PARM_DESC(force_desc_page_size, " + module_param(force_hailo15_legacy_mode, bool, S_IRUGO); + MODULE_PARM_DESC(force_hailo15_legacy_mode, "Forces work with Hailo15 in legacy mode(relevant for emulators)"); + ++module_param(force_boot_linux_from_eemc, bool, S_IRUGO); ++MODULE_PARM_DESC(force_boot_linux_from_eemc, "Boot the linux image from eemc (Requires special Image)"); ++ + MODULE_AUTHOR("Hailo Technologies Ltd."); + MODULE_DESCRIPTION("Hailo PCIe driver"); + MODULE_LICENSE("GPL v2"); +--- a/drivers/media/pci/hailo/src/pcie.h ++++ b/drivers/media/pci/hailo/src/pcie.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_PCI_PCIE_H_ +@@ -41,6 +41,19 @@ struct hailo_fw_boot { + }; + + ++struct hailo_pcie_nnc { ++ struct hailo_fw_control_info fw_control; ++ ++ spinlock_t notification_read_spinlock; ++ struct list_head notification_wait_list; ++ struct hailo_d2h_notification notification_cache; ++ struct hailo_d2h_notification notification_to_user; ++}; ++ ++struct hailo_pcie_soc { ++ struct completion control_resp_ready; ++}; ++ + // Context for each open file handle + // TODO: store board and use as actual context + struct hailo_file_context { +@@ -48,6 +61,7 @@ struct hailo_file_context { + struct file *filp; + struct hailo_vdma_file_context vdma_context; + bool is_valid; ++ u32 soc_used_channels_bitmap; + }; + + struct hailo_pcie_board { +@@ -57,21 +71,17 @@ struct hailo_pcie_board { + atomic_t ref_count; + struct list_head open_files_list; + struct hailo_pcie_resources pcie_resources; +- struct hailo_fw_control_info fw_control; ++ struct hailo_pcie_nnc nnc; ++ struct hailo_pcie_soc soc; + struct hailo_pcie_driver_down_info driver_down; + struct semaphore mutex; + struct hailo_vdma_controller vdma; +- spinlock_t notification_read_spinlock; +- struct list_head notification_wait_list; +- struct hailo_d2h_notification notification_cache; +- struct hailo_d2h_notification notification_to_user; ++ + struct hailo_memory_transfer_params memory_transfer_params; + u32 desc_max_page_size; + enum hailo_allocation_mode allocation_mode; + struct completion fw_loaded_completion; + bool interrupts_enabled; +- // Only needed in accelerator type soc +- struct completion soc_connect_accepted; + }; + + bool power_mode_enabled(void); +--- /dev/null ++++ b/drivers/media/pci/hailo/src/soc.c +@@ -0,0 +1,244 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/** ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ ++/** ++ * A Hailo PCIe NNC device is a device contains a full SoC over PCIe. The SoC contains NNC (neural network core) and ++ * some application processor (pci_ep). ++ */ ++ ++#include "soc.h" ++ ++#include "vdma_common.h" ++#include "utils/logs.h" ++#include "vdma/memory.h" ++ ++#include ++ ++#define PCI_SOC_VDMA_ENGINE_INDEX (0) ++#define PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS (1000) ++#define PCI_SOC_INPUT_CHANNEL_BITMASK (0x000000FF) ++ ++void hailo_soc_init(struct hailo_pcie_soc *soc) ++{ ++ init_completion(&soc->control_resp_ready); ++} ++ ++long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, ++ struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ case HAILO_SOC_CONNECT: ++ return hailo_soc_connect_ioctl(board, context, controller, arg); ++ case HAILO_SOC_CLOSE: ++ return hailo_soc_close_ioctl(board, controller, context, arg); ++ default: ++ hailo_err(board, "Invalid pcie EP ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); ++ return -ENOTTY; ++ } ++} ++ ++static int soc_control(struct hailo_pcie_board *board, ++ const struct hailo_pcie_soc_request *request, ++ struct hailo_pcie_soc_response *response) ++{ ++ int ret = 0; ++ reinit_completion(&board->soc.control_resp_ready); ++ ++ hailo_pcie_soc_write_request(&board->pcie_resources, request); ++ ++ ret = wait_for_completion_interruptible_timeout(&board->soc.control_resp_ready, ++ msecs_to_jiffies(PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS)); ++ if (ret <= 0) { ++ if (0 == ret) { ++ hailo_err(board, "Timeout waiting for soc control (timeout_ms=%d)\n", PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS); ++ return -ETIMEDOUT; ++ } else { ++ hailo_info(board, "soc control failed with err=%d (process was interrupted or killed)\n", ++ ret); ++ return ret; ++ } ++ } ++ ++ hailo_pcie_soc_read_response(&board->pcie_resources, response); ++ ++ if (response->status < 0) { ++ hailo_err(board, "soc control failed with status=%d\n", response->status); ++ return response->status; ++ } ++ ++ if (response->control_code != request->control_code) { ++ hailo_err(board, "Invalid response control code %d (expected %d)\n", ++ response->control_code, request->control_code); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, ++ struct hailo_vdma_controller *controller, unsigned long arg) ++{ ++ struct hailo_pcie_soc_request request = {0}; ++ struct hailo_pcie_soc_response response = {0}; ++ struct hailo_soc_connect_params params; ++ struct hailo_vdma_channel *input_channel = NULL; ++ struct hailo_vdma_channel *output_channel = NULL; ++ struct hailo_vdma_engine *vdma_engine = &controller->vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX]; ++ struct hailo_descriptors_list_buffer *input_descriptors_buffer = NULL; ++ struct hailo_descriptors_list_buffer *output_descriptors_buffer = NULL; ++ uint8_t depth = 0; ++ int err = 0; ++ ++ if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { ++ hailo_err(board, "copy_from_user fail\n"); ++ return -ENOMEM; ++ } ++ ++ request = (struct hailo_pcie_soc_request) { ++ .control_code = HAILO_PCIE_SOC_CONTROL_CODE_CONNECT, ++ .connect = { ++ .port = params.port_number ++ } ++ }; ++ err = soc_control(board, &request, &response); ++ if (err < 0) { ++ return err; ++ } ++ ++ params.input_channel_index = response.connect.input_channel_index; ++ params.output_channel_index = response.connect.output_channel_index; ++ ++ if (!hailo_check_channel_index(params.input_channel_index, controller->hw->src_channels_bitmask, true)) { ++ hailo_dev_err(&board->pDev->dev, "Invalid input channel index %u\n", params.input_channel_index); ++ return -EINVAL; ++ } ++ ++ if (!hailo_check_channel_index(params.output_channel_index, controller->hw->src_channels_bitmask, false)) { ++ hailo_dev_err(&board->pDev->dev, "Invalid output channel index %u\n", params.output_channel_index); ++ return -EINVAL; ++ } ++ ++ input_channel = &vdma_engine->channels[params.input_channel_index]; ++ output_channel = &vdma_engine->channels[params.output_channel_index]; ++ ++ input_descriptors_buffer = hailo_vdma_find_descriptors_buffer(&context->vdma_context, params.input_desc_handle); ++ output_descriptors_buffer = hailo_vdma_find_descriptors_buffer(&context->vdma_context, params.output_desc_handle); ++ if (NULL == input_descriptors_buffer || NULL == output_descriptors_buffer) { ++ hailo_dev_err(&board->pDev->dev, "input / output descriptors buffer not found \n"); ++ return -EINVAL; ++ } ++ ++ if (!is_powerof2((size_t)input_descriptors_buffer->desc_list.desc_count) || ++ !is_powerof2((size_t)output_descriptors_buffer->desc_list.desc_count)) { ++ hailo_dev_err(&board->pDev->dev, "Invalid desc list size\n"); ++ return -EINVAL; ++ } ++ ++ // configure and start input channel ++ depth = ceil_log2(input_descriptors_buffer->desc_list.desc_count); ++ // DMA Direction is only to get channel index - so ++ err = hailo_vdma_start_channel(input_channel->host_regs, input_descriptors_buffer->dma_address, depth, ++ board->vdma.hw->ddr_data_id); ++ if (err < 0) { ++ hailo_dev_err(&board->pDev->dev, "Error starting vdma input channel index %u\n", params.input_channel_index); ++ return -EINVAL; ++ } ++ ++ // Store the input channels state in bitmap (open) ++ hailo_set_bit(params.input_channel_index, &context->soc_used_channels_bitmap); ++ ++ // configure and start output channel ++ depth = ceil_log2(output_descriptors_buffer->desc_list.desc_count); ++ // DMA Direction is only to get channel index - so ++ err = hailo_vdma_start_channel(output_channel->host_regs, output_descriptors_buffer->dma_address, depth, ++ board->vdma.hw->ddr_data_id); ++ if (err < 0) { ++ hailo_dev_err(&board->pDev->dev, "Error starting vdma output channel index %u\n", params.output_channel_index); ++ // Close input channel ++ hailo_vdma_stop_channel(input_channel->host_regs); ++ return -EINVAL; ++ } ++ ++ // Store the output channels state in bitmap (open) ++ hailo_set_bit(params.output_channel_index, &context->soc_used_channels_bitmap); ++ ++ if (copy_to_user((void *)arg, ¶ms, sizeof(params))) { ++ hailo_dev_err(&board->pDev->dev, "copy_to_user fail\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static int close_channels(struct hailo_pcie_board *board, u32 channels_bitmap) ++{ ++ struct hailo_pcie_soc_request request = {0}; ++ struct hailo_pcie_soc_response response = {0}; ++ struct hailo_vdma_engine *engine = &board->vdma.vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX]; ++ struct hailo_vdma_channel *channel = NULL; ++ u8 channel_index = 0; ++ ++ hailo_info(board, "Closing channels bitmap 0x%x\n", channels_bitmap); ++ for_each_vdma_channel(engine, channel, channel_index) { ++ if (hailo_test_bit(channel_index, &channels_bitmap)) { ++ hailo_vdma_stop_channel(channel->host_regs); ++ } ++ } ++ ++ request = (struct hailo_pcie_soc_request) { ++ .control_code = HAILO_PCIE_SOC_CONTROL_CODE_CLOSE, ++ .close = { ++ .channels_bitmap = channels_bitmap ++ } ++ }; ++ return soc_control(board, &request, &response); ++} ++ ++long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, ++ struct hailo_file_context *context, unsigned long arg) ++{ ++ struct hailo_soc_close_params params; ++ u32 channels_bitmap = 0; ++ int err = 0; ++ ++ if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { ++ hailo_dev_err(&board->pDev->dev, "copy_from_user fail\n"); ++ return -ENOMEM; ++ } ++ ++ // TOOD: check channels are connected ++ ++ channels_bitmap = (1 << params.input_channel_index) | (1 << params.output_channel_index); ++ ++ err = close_channels(board, channels_bitmap); ++ if (0 != err) { ++ hailo_dev_err(&board->pDev->dev, "Error closing channels\n"); ++ return err; ++ } ++ ++ // Store the channel state in bitmap (closed) ++ hailo_clear_bit(params.input_channel_index, &context->soc_used_channels_bitmap); ++ hailo_clear_bit(params.output_channel_index, &context->soc_used_channels_bitmap); ++ ++ return err; ++} ++ ++int hailo_soc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context) ++{ ++ // Nothing to init yet ++ return 0; ++} ++ ++void hailo_soc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context) ++{ ++ // close only channels connected by this (by bitmap) ++ if (context->soc_used_channels_bitmap != 0) { ++ close_channels(board, context->soc_used_channels_bitmap); ++ } ++} ++ ++int hailo_soc_driver_down(struct hailo_pcie_board *board) ++{ ++ return close_channels(board, 0xFFFFFFFF); ++} +\ No newline at end of file +--- a/drivers/media/pci/hailo/src/pci_soc_ioctl.h ++++ /dev/null +@@ -1,19 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/** +- * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. +- **/ +- +-#ifndef _HAILO_PCI_SOC_IOCTL_H_ +-#define _HAILO_PCI_SOC_IOCTL_H_ +- +-#include "vdma/ioctl.h" +-#include "pcie.h" +- +- +-long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context, +- struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg); +-long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context, +- struct hailo_vdma_controller *controller, unsigned long arg); +-long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, unsigned long arg); +- +-#endif // _HAILO_PCI_SOC_IOCTL_H_ +\ No newline at end of file +--- /dev/null ++++ b/drivers/media/pci/hailo/src/soc.h +@@ -0,0 +1,26 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/** ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ ++ ++#ifndef _HAILO_PCI_SOC_IOCTL_H_ ++#define _HAILO_PCI_SOC_IOCTL_H_ ++ ++#include "vdma/ioctl.h" ++#include "pcie.h" ++ ++ ++void hailo_soc_init(struct hailo_pcie_soc *soc); ++ ++long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, ++ struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg); ++long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, ++ struct hailo_vdma_controller *controller, unsigned long arg); ++long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, struct hailo_file_context *context, unsigned long arg); ++ ++int hailo_soc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context); ++void hailo_soc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context); ++ ++int hailo_soc_driver_down(struct hailo_pcie_board *board); ++ ++#endif // _HAILO_PCI_SOC_IOCTL_H_ +\ No newline at end of file +--- a/drivers/media/pci/hailo/src/sysfs.c ++++ b/drivers/media/pci/hailo/src/sysfs.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "sysfs.h" +--- a/drivers/media/pci/hailo/src/sysfs.h ++++ b/drivers/media/pci/hailo/src/sysfs.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_PCI_SYSFS_H_ +--- a/drivers/media/pci/hailo/src/utils.c ++++ /dev/null +@@ -1,26 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. +- **/ +- +-#include +-#include +-#include +-#include +- +-#include "pcie.h" +-#include "utils.h" +-#include "utils/logs.h" +- +- +-void hailo_pcie_clear_notification_wait_list(struct hailo_pcie_board *pBoard, struct file *filp) +-{ +- struct hailo_notification_wait *cur = NULL, *next = NULL; +- list_for_each_entry_safe(cur, next, &pBoard->notification_wait_list, notification_wait_list) { +- if (cur->filp == filp) { +- list_del_rcu(&cur->notification_wait_list); +- synchronize_rcu(); +- kfree(cur); +- } +- } +-} +--- a/drivers/media/pci/hailo/utils/compact.h ++++ b/drivers/media/pci/hailo/utils/compact.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_PCI_COMPACT_H_ +--- a/drivers/media/pci/hailo/utils/fw_common.h ++++ b/drivers/media/pci/hailo/utils/fw_common.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_LINUX_COMMON_H_ +--- a/drivers/media/pci/hailo/utils/integrated_nnc_utils.c ++++ b/drivers/media/pci/hailo/utils/integrated_nnc_utils.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "integrated_nnc_utils.h" +@@ -43,11 +43,19 @@ int hailo_ioremap_shmem(struct platform_ + void __iomem * remap_ptr; + + shmem = of_parse_phandle(pdev->dev.of_node, "shmem", index); ++ if (!shmem) { ++ hailo_dev_err(&pdev->dev, "Failed to find shmem node index: %d in device tree\n", index); ++ return -ENODEV; ++ } ++ + ret = of_address_to_resource(shmem, 0, &res); + if (ret) { + hailo_dev_err(&pdev->dev, "hailo_ioremap_shmem, failed to get memory (index: %d)\n", index); ++ of_node_put(shmem); + return ret; + } ++ ++ // Decrement the refcount of the node + of_node_put(shmem); + + remap_ptr = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); +--- a/drivers/media/pci/hailo/utils/integrated_nnc_utils.h ++++ b/drivers/media/pci/hailo/utils/integrated_nnc_utils.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _INTEGRATED_NNC_UTILS_H_ +--- a/drivers/media/pci/hailo/utils/logs.c ++++ b/drivers/media/pci/hailo/utils/logs.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "logs.h" +--- a/drivers/media/pci/hailo/utils/logs.h ++++ b/drivers/media/pci/hailo/utils/logs.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _COMMON_LOGS_H_ +--- a/drivers/media/pci/hailo/vdma/ioctl.c ++++ b/drivers/media/pci/hailo/vdma/ioctl.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "ioctl.h" +@@ -12,7 +12,7 @@ + #include + + +-long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg) ++long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context) + { + struct hailo_vdma_enable_channels_params input; + struct hailo_vdma_engine *engine = NULL; +@@ -40,12 +40,15 @@ long hailo_vdma_enable_channels_ioctl(st + hailo_vdma_update_interrupts_mask(controller, engine_index); + hailo_dev_info(controller->dev, "Enabled interrupts for engine %u, channels bitmap 0x%x\n", + engine_index, channels_bitmap); ++ ++ // Update the context with the enabled channels bitmap ++ context->enabled_channels_bitmap[engine_index] |= channels_bitmap; + } + + return 0; + } + +-long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg) ++long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context) + { + struct hailo_vdma_disable_channels_params input; + struct hailo_vdma_engine *engine = NULL; +@@ -77,6 +80,9 @@ long hailo_vdma_disable_channels_ioctl(s + + hailo_dev_info(controller->dev, "Disabled channels for engine %u, bitmap 0x%x\n", + engine_index, channels_bitmap); ++ ++ // Update the context with the disabled channels bitmap ++ context->enabled_channels_bitmap[engine_index] &= ~channels_bitmap; + } + + // Wake up threads waiting +@@ -204,7 +210,7 @@ long hailo_vdma_buffer_map_ioctl(struct + return -EFAULT; + } + +- hailo_dev_info(controller->dev, "address %lx tgid %d size: %zu\n", ++ hailo_dev_dbg(controller->dev, "address %lx tgid %d size: %zu\n", + buf_info.user_address, current->tgid, buf_info.size); + + direction = get_dma_direction(buf_info.data_direction); +@@ -231,7 +237,7 @@ long hailo_vdma_buffer_map_ioctl(struct + } + + list_add(&mapped_buffer->mapped_user_buffer_list, &context->mapped_user_buffer_list); +- hailo_dev_info(controller->dev, "buffer %lx (handle %zu) is mapped\n", ++ hailo_dev_dbg(controller->dev, "buffer %lx (handle %zu) is mapped\n", + buf_info.user_address, buf_info.mapped_handle); + return 0; + } +@@ -247,7 +253,7 @@ long hailo_vdma_buffer_unmap_ioctl(struc + return -EFAULT; + } + +- hailo_dev_info(controller->dev, "unmap user buffer handle %zu\n", buffer_unmap_params.mapped_handle); ++ hailo_dev_dbg(controller->dev, "unmap user buffer handle %zu\n", buffer_unmap_params.mapped_handle); + + mapped_buffer = hailo_vdma_find_mapped_user_buffer(context, buffer_unmap_params.mapped_handle); + if (mapped_buffer == NULL) { +--- a/drivers/media/pci/hailo/vdma/ioctl.h ++++ b/drivers/media/pci/hailo/vdma/ioctl.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_VDMA_IOCTL_H_ +@@ -8,8 +8,8 @@ + + #include "vdma/vdma.h" + +-long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg); +-long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg); ++long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context); ++long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context); + long hailo_vdma_interrupts_wait_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, + struct semaphore *mutex, bool *should_up_board_mutex); + +--- a/drivers/media/pci/hailo/vdma/memory.c ++++ b/drivers/media/pci/hailo/vdma/memory.c +@@ -1,11 +1,12 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #define pr_fmt(fmt) "hailo: " fmt + + #include "memory.h" ++#include "utils.h" + #include "utils/compact.h" + + #include +@@ -316,6 +317,11 @@ int hailo_desc_list_create(struct device + size_t buffer_size = 0; + const u64 align = VDMA_DESCRIPTOR_LIST_ALIGN; //First addr must be aligned on 64 KB (from the VDMA registers documentation) + ++ if (MAX_POWER_OF_2_VALUE < descriptors_count) { ++ dev_err(dev, "Invalid descriptors count %u\n", descriptors_count); ++ return -EINVAL; ++ } ++ + buffer_size = descriptors_count * sizeof(struct hailo_vdma_descriptor); + buffer_size = ALIGN(buffer_size, align); + +@@ -323,7 +329,7 @@ int hailo_desc_list_create(struct device + &descriptors->dma_address, GFP_KERNEL | __GFP_ZERO); + if (descriptors->kernel_address == NULL) { + dev_err(dev, "Failed to allocate descriptors list, desc_count 0x%x, buffer_size 0x%zx, This failure means there is not a sufficient amount of CMA memory " +- "(contiguous physical memory), This usually is caused by lack of general system memory. Please check you have sufficent memory.\n", ++ "(contiguous physical memory), This usually is caused by lack of general system memory. Please check you have sufficient memory.\n", + descriptors_count, buffer_size); + return -ENOMEM; + } +@@ -333,6 +339,8 @@ int hailo_desc_list_create(struct device + + descriptors->desc_list.desc_list = descriptors->kernel_address; + descriptors->desc_list.desc_count = descriptors_count; ++ // No need to check the return value of get_nearest_powerof_2 because we already checked the input ++ descriptors->desc_list.desc_count_mask = is_circular ? (descriptors_count - 1) : (get_nearest_powerof_2(descriptors_count) - 1); + descriptors->desc_list.desc_page_size = desc_page_size; + descriptors->desc_list.is_circular = is_circular; + +--- a/drivers/media/pci/hailo/vdma/memory.h ++++ b/drivers/media/pci/hailo/vdma/memory.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + /** + * vDMA memory utility (including allocation and mappings) +--- a/drivers/media/pci/hailo/vdma/vdma.c ++++ b/drivers/media/pci/hailo/vdma/vdma.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #define pr_fmt(fmt) "hailo: " fmt +@@ -105,6 +105,9 @@ void hailo_vdma_file_context_init(struct + INIT_LIST_HEAD(&context->descriptors_buffer_list); + INIT_LIST_HEAD(&context->vdma_low_memory_buffer_list); + INIT_LIST_HEAD(&context->continuous_buffer_list); ++ ++ BUILD_BUG_ON_MSG(MAX_VDMA_CHANNELS_PER_ENGINE > sizeof(context->enabled_channels_bitmap[0]) * BITS_IN_BYTE, ++ "Unexpected amount of VDMA channels per engine"); + } + + void hailo_vdma_update_interrupts_mask(struct hailo_vdma_controller *controller, +@@ -119,21 +122,22 @@ void hailo_vdma_file_context_finalize(st + { + size_t engine_index = 0; + struct hailo_vdma_engine *engine = NULL; +- const u32 channels_bitmap = 0xFFFFFFFF; // disable all channel interrupts + unsigned long irq_saved_flags = 0; + // In case of FLR, the vdma registers will be NULL + const bool is_device_up = (NULL != controller->dev); + +- if (filp == controller->used_by_filp) { +- for_each_vdma_engine(controller, engine, engine_index) { +- hailo_vdma_engine_disable_channels(engine, channels_bitmap); ++ for_each_vdma_engine(controller, engine, engine_index) { ++ if (context->enabled_channels_bitmap[engine_index]) { ++ hailo_dev_info(controller->dev, "Disabling channels for engine %zu, channels bitmap 0x%x\n", engine_index, ++ context->enabled_channels_bitmap[engine_index]); ++ hailo_vdma_engine_disable_channels(engine, context->enabled_channels_bitmap[engine_index]); + + if (is_device_up) { + hailo_vdma_update_interrupts_mask(controller, engine_index); + } + + spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags); +- hailo_vdma_engine_clear_channel_interrupts(engine, channels_bitmap); ++ hailo_vdma_engine_clear_channel_interrupts(engine, context->enabled_channels_bitmap[engine_index]); + spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags); + } + } +@@ -148,10 +152,21 @@ void hailo_vdma_file_context_finalize(st + } + } + ++void hailo_vdma_wakeup_interrupts(struct hailo_vdma_controller *controller, struct hailo_vdma_engine *engine, ++ u32 channels_bitmap) ++{ ++ unsigned long irq_saved_flags = 0; ++ ++ spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags); ++ hailo_vdma_engine_set_channel_interrupts(engine, channels_bitmap); ++ spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags); ++ ++ wake_up_interruptible_all(&controller->interrupts_wq); ++} ++ + void hailo_vdma_irq_handler(struct hailo_vdma_controller *controller, + size_t engine_index, u32 channels_bitmap) + { +- unsigned long irq_saved_flags = 0; + struct hailo_vdma_engine *engine = NULL; + + BUG_ON(engine_index >= controller->vdma_engines_count); +@@ -159,11 +174,7 @@ void hailo_vdma_irq_handler(struct hailo + + hailo_vdma_engine_push_timestamps(engine, channels_bitmap); + +- spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags); +- hailo_vdma_engine_set_channel_interrupts(engine, channels_bitmap); +- spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags); +- +- wake_up_interruptible_all(&controller->interrupts_wq); ++ hailo_vdma_wakeup_interrupts(controller, engine, channels_bitmap); + } + + long hailo_vdma_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, +@@ -171,9 +182,9 @@ long hailo_vdma_ioctl(struct hailo_vdma_ + { + switch (cmd) { + case HAILO_VDMA_ENABLE_CHANNELS: +- return hailo_vdma_enable_channels_ioctl(controller, arg); ++ return hailo_vdma_enable_channels_ioctl(controller, arg, context); + case HAILO_VDMA_DISABLE_CHANNELS: +- return hailo_vdma_disable_channels_ioctl(controller, arg); ++ return hailo_vdma_disable_channels_ioctl(controller, arg, context); + case HAILO_VDMA_INTERRUPTS_WAIT: + return hailo_vdma_interrupts_wait_ioctl(controller, arg, mutex, should_up_board_mutex); + case HAILO_VDMA_INTERRUPTS_READ_TIMESTAMPS: +--- a/drivers/media/pci/hailo/vdma/vdma.h ++++ b/drivers/media/pci/hailo/vdma/vdma.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + /** + * Hailo vdma engine definitions +@@ -130,6 +130,7 @@ struct hailo_vdma_file_context { + struct list_head descriptors_buffer_list; + struct list_head vdma_low_memory_buffer_list; + struct list_head continuous_buffer_list; ++ u32 enabled_channels_bitmap[MAX_VDMA_ENGINES]; + }; + + +@@ -145,6 +146,8 @@ void hailo_vdma_file_context_init(struct + void hailo_vdma_file_context_finalize(struct hailo_vdma_file_context *context, + struct hailo_vdma_controller *controller, struct file *filp); + ++void hailo_vdma_wakeup_interrupts(struct hailo_vdma_controller *controller, struct hailo_vdma_engine *engine, ++ u32 channels_bitmap); + void hailo_vdma_irq_handler(struct hailo_vdma_controller *controller, size_t engine_index, + u32 channels_bitmap); + diff --git a/target/linux/bcm27xx/patches-6.6/950-1398-dtoverlays-enable-SPI-CS-active-high.patch b/target/linux/bcm27xx/patches-6.6/950-1398-dtoverlays-enable-SPI-CS-active-high.patch new file mode 100644 index 0000000000..ecd26a78b8 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1398-dtoverlays-enable-SPI-CS-active-high.patch @@ -0,0 +1,108 @@ +From dbf12796d1368286672529d7b03f81066a8c36f3 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Mon, 18 Nov 2024 10:55:33 +0100 +Subject: [PATCH] dtoverlays: enable SPI CS active-high + +The documentation isn't very clear explaining how to enable SPI CS +active-high and it takes a long time to understand it. Adding a specific +overlay as a simple example on how to invert this signal can help +understand the solution. + +Link: https://forums.raspberrypi.com/viewtopic.php?t=378222 +Signed-off-by: Iker Pedrosa +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 8 +++ + .../overlays/spi0-1cs-inverted-overlay.dts | 59 +++++++++++++++++++ + 3 files changed, 68 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/spi0-1cs-inverted-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -259,6 +259,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + spi-rtc.dtbo \ + spi0-0cs.dtbo \ + spi0-1cs.dtbo \ ++ spi0-1cs-inverted.dtbo \ + spi0-2cs.dtbo \ + spi1-1cs.dtbo \ + spi1-2cs.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -4438,6 +4438,14 @@ Params: cs0_pin GPIO pin + it for other uses. + + ++Name: spi0-1cs-inverted ++Info: Only use one CS pin for SPI0 and set to active-high ++Load: dtoverlay=spi0-1cs-inverted,= ++Params: cs0_pin GPIO pin for CS0 (default 8) ++ no_miso Don't claim and use the MISO pin (9), freeing ++ it for other uses. ++ ++ + Name: spi0-2cs + Info: Change the CS pins for SPI0 + Load: dtoverlay=spi0-2cs,= +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/spi0-1cs-inverted-overlay.dts +@@ -0,0 +1,59 @@ ++/dts-v1/; ++/plugin/; ++ ++/* ++ * There are some devices that need an inverted Chip Select (CS) to select the ++ * device signal, as an example the AZDelivery 12864 display. That means that ++ * the CS polarity is active-high. To invert the CS signal the DT needs to set ++ * the cs-gpio to GPIO_ACTIVE_HIGH (0) in the controller and set the ++ * spi-cs-high in the peripheral property. On top of that, since this is a ++ * display the DT also needs to specify the write-only property. ++*/ ++ ++#include ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&spi0_cs_pins>; ++ frag0: __overlay__ { ++ brcm,pins = <8>; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&spi0>; ++ frag1: __overlay__ { ++ cs-gpios = <&gpio 8 GPIO_ACTIVE_HIGH>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spidev1>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ ++ fragment@3 { ++ target = <&spi0_pins>; ++ __dormant__ { ++ brcm,pins = <10 11>; ++ }; ++ }; ++ ++ fragment@4 { ++ target = <&spidev0>; ++ __overlay__ { ++ spi-cs-high; ++ }; ++ }; ++ ++ __overrides__ { ++ cs0_pin = <&frag0>,"brcm,pins:0", ++ <&frag1>,"cs-gpios:4"; ++ no_miso = <0>,"=3"; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-6.6/950-1399-drm-vc4-hvs-Defer-updating-the-enable_bg_fill-until-.patch b/target/linux/bcm27xx/patches-6.6/950-1399-drm-vc4-hvs-Defer-updating-the-enable_bg_fill-until-.patch new file mode 100644 index 0000000000..8f8af00c9f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1399-drm-vc4-hvs-Defer-updating-the-enable_bg_fill-until-.patch @@ -0,0 +1,64 @@ +From 57b528e557890f25e010b6bc7356b5a716c79db2 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 12 Nov 2024 17:58:52 +0000 +Subject: [PATCH] drm/vc4: hvs: Defer updating the enable_bg_fill until vblank + +The register to enable/disable background fill was being set +from atomic flush, however that will be applied immediately and +can be a while before the vblank. If it was required for the +current frame but not for the next one, that can result in +corruption for part of the current frame. + +Store the state in vc4_hvs, and update it on vblank. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/vc4/vc4_drv.h | 2 ++ + drivers/gpu/drm/vc4/vc4_hvs.c | 18 ++++++++++-------- + 2 files changed, 12 insertions(+), 8 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -339,6 +339,8 @@ struct vc4_hvs { + unsigned int enabled: 1; + } eof_irq[HVS_NUM_CHANNELS]; + ++ bool bg_fill[HVS_NUM_CHANNELS]; ++ + unsigned long max_core_rate; + + /* Memory manager for CRTCs to allocate space in the display +--- a/drivers/gpu/drm/vc4/vc4_hvs.c ++++ b/drivers/gpu/drm/vc4/vc4_hvs.c +@@ -1470,14 +1470,7 @@ void vc4_hvs_atomic_flush(struct drm_crt + /* This sets a black background color fill, as is the case + * with other DRM drivers. + */ +- if (enable_bg_fill) +- HVS_WRITE(SCALER6_DISPX_CTRL1(channel), +- HVS_READ(SCALER6_DISPX_CTRL1(channel)) | +- SCALER6(DISPX_CTRL1_BGENB)); +- else +- HVS_WRITE(SCALER6_DISPX_CTRL1(channel), +- HVS_READ(SCALER6_DISPX_CTRL1(channel)) & +- ~SCALER6(DISPX_CTRL1_BGENB)); ++ hvs->bg_fill[channel] = enable_bg_fill; + } else { + /* we can actually run with a lower core clock when background + * fill is enabled on VC4_GEN_5 so leave it enabled always. +@@ -1662,6 +1655,15 @@ static irqreturn_t vc6_hvs_eof_irq_handl + if (hvs->eof_irq[i].desc != irq) + continue; + ++ if (hvs->bg_fill[i]) ++ HVS_WRITE(SCALER6_DISPX_CTRL1(i), ++ HVS_READ(SCALER6_DISPX_CTRL1(i)) | ++ SCALER6(DISPX_CTRL1_BGENB)); ++ else ++ HVS_WRITE(SCALER6_DISPX_CTRL1(i), ++ HVS_READ(SCALER6_DISPX_CTRL1(i)) & ++ ~SCALER6(DISPX_CTRL1_BGENB)); ++ + vc4_hvs_schedule_dlist_sweep(hvs, i); + return IRQ_HANDLED; + } diff --git a/target/linux/bcm27xx/patches-6.6/950-1400-misc-rp1-pio-Add-FIFO-related-methods.patch b/target/linux/bcm27xx/patches-6.6/950-1400-misc-rp1-pio-Add-FIFO-related-methods.patch new file mode 100644 index 0000000000..da21526c21 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1400-misc-rp1-pio-Add-FIFO-related-methods.patch @@ -0,0 +1,215 @@ +From dd2394360860d15146c96635796a75b05bb32b61 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 19 Nov 2024 09:25:34 +0000 +Subject: [PATCH] misc: rp1-pio: Add FIFO-related methods + +Add support for querying the FIFO status and clearing the TX FIFO. + +Signed-off-by: Phil Elwell +--- + drivers/misc/rp1-fw-pio.h | 3 ++ + drivers/misc/rp1-pio.c | 24 +++++++++ + include/linux/pio_rp1.h | 89 ++++++++++++++++++++++++++++++++++ + include/uapi/misc/rp1_pio_if.h | 13 ++++- + 4 files changed, 128 insertions(+), 1 deletion(-) + +--- a/drivers/misc/rp1-fw-pio.h ++++ b/drivers/misc/rp1-fw-pio.h +@@ -47,6 +47,9 @@ enum rp1_pio_ops { + READ_HW, // src address, len -> data bytes + WRITE_HW, // dst address, data + ++ PIO_SM_FIFO_STATE, // u16 sm, u8 tx -> u16 level, u8 empty, u8 full ++ PIO_SM_DRAIN_TX, // u16 sm ++ + PIO_COUNT + }; + +--- a/drivers/misc/rp1-pio.c ++++ b/drivers/misc/rp1-pio.c +@@ -476,6 +476,28 @@ int rp1_pio_sm_set_dmactrl(struct rp1_pi + } + EXPORT_SYMBOL_GPL(rp1_pio_sm_set_dmactrl); + ++int rp1_pio_sm_fifo_state(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_fifo_state_args *args = param; ++ const int level_offset = offsetof(struct rp1_pio_sm_fifo_state_args, level); ++ int ret; ++ ++ ret = rp1_pio_message_resp(client->pio, PIO_SM_FIFO_STATE, args, sizeof(*args), ++ &args->level, NULL, sizeof(*args) - level_offset); ++ if (ret >= 0) ++ return level_offset + ret; ++ return ret; ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_fifo_state); ++ ++int rp1_pio_sm_drain_tx(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_clear_fifos_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_DRAIN_TX, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_drain_tx); ++ + int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_init_args *args = param; +@@ -848,6 +870,8 @@ struct handler_info { + HANDLER(SM_PUT, sm_put), + HANDLER(SM_GET, sm_get), + HANDLER(SM_SET_DMACTRL, sm_set_dmactrl), ++ HANDLER(SM_FIFO_STATE, sm_fifo_state), ++ HANDLER(SM_DRAIN_TX, sm_drain_tx), + + HANDLER(GPIO_INIT, gpio_init), + HANDLER(GPIO_SET_FUNCTION, gpio_set_function), +--- a/include/linux/pio_rp1.h ++++ b/include/linux/pio_rp1.h +@@ -200,6 +200,8 @@ int rp1_pio_sm_enable_sync(struct rp1_pi + int rp1_pio_sm_put(struct rp1_pio_client *client, void *param); + int rp1_pio_sm_get(struct rp1_pio_client *client, void *param); + int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_fifo_state(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_drain_tx(struct rp1_pio_client *client, void *param); + int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param); + int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param); + int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param); +@@ -551,6 +553,15 @@ static inline int pio_sm_set_dmactrl(str + return rp1_pio_sm_set_dmactrl(client, &args); + }; + ++static inline int pio_sm_drain_tx_fifo(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_clear_fifos_args args = { .sm = sm }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_drain_tx(client, &args); ++}; ++ + static inline int pio_sm_put(struct rp1_pio_client *client, uint sm, uint32_t data) + { + struct rp1_pio_sm_put_args args = { .sm = (uint16_t)sm, .blocking = false, .data = data }; +@@ -587,6 +598,84 @@ static inline uint32_t pio_sm_get_blocki + return args.data; + } + ++static inline int pio_sm_is_rx_fifo_empty(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.empty; ++ return ret; ++}; ++ ++static inline int pio_sm_is_rx_fifo_full(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.full; ++ return ret; ++}; ++ ++static inline int pio_sm_rx_fifo_level(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.level; ++ return ret; ++}; ++ ++static inline int pio_sm_is_tx_fifo_empty(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.empty; ++ return ret; ++}; ++ ++static inline int pio_sm_is_tx_fifo_full(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.full; ++ return ret; ++}; ++ ++static inline int pio_sm_tx_fifo_level(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.level; ++ return ret; ++}; ++ + static inline void sm_config_set_out_pins(pio_sm_config *c, uint out_base, uint out_count) + { + if (bad_params_if(NULL, out_base >= RP1_PIO_GPIO_COUNT || +--- a/include/uapi/misc/rp1_pio_if.h ++++ b/include/uapi/misc/rp1_pio_if.h +@@ -114,7 +114,7 @@ struct rp1_pio_sm_get_args { + uint16_t sm; + uint8_t blocking; + uint8_t rsvd; +- uint32_t data; /* IN/OUT */ ++ uint32_t data; /* OUT */ + }; + + struct rp1_pio_sm_set_dmactrl_args { +@@ -124,6 +124,15 @@ struct rp1_pio_sm_set_dmactrl_args { + uint32_t ctrl; + }; + ++struct rp1_pio_sm_fifo_state_args { ++ uint16_t sm; ++ uint8_t tx; ++ uint8_t rsvd; ++ uint16_t level; /* OUT */ ++ uint8_t empty; /* OUT */ ++ uint8_t full; /* OUT */ ++}; ++ + struct rp1_gpio_init_args { + uint16_t gpio; + }; +@@ -195,6 +204,8 @@ struct rp1_access_hw_args { + #define PIO_IOC_SM_PUT _IOW(PIO_IOC_MAGIC, 41, struct rp1_pio_sm_put_args) + #define PIO_IOC_SM_GET _IOWR(PIO_IOC_MAGIC, 42, struct rp1_pio_sm_get_args) + #define PIO_IOC_SM_SET_DMACTRL _IOW(PIO_IOC_MAGIC, 43, struct rp1_pio_sm_set_dmactrl_args) ++#define PIO_IOC_SM_FIFO_STATE _IOW(PIO_IOC_MAGIC, 44, struct rp1_pio_sm_fifo_state_args) ++#define PIO_IOC_SM_DRAIN_TX _IOW(PIO_IOC_MAGIC, 45, struct rp1_pio_sm_clear_fifos_args) + + #define PIO_IOC_GPIO_INIT _IOW(PIO_IOC_MAGIC, 50, struct rp1_gpio_init_args) + #define PIO_IOC_GPIO_SET_FUNCTION _IOW(PIO_IOC_MAGIC, 51, struct rp1_gpio_set_function_args) diff --git a/target/linux/bcm27xx/patches-6.6/950-1401-overlays-Enable-Raspberry-Touch-2-rotation-with-over.patch b/target/linux/bcm27xx/patches-6.6/950-1401-overlays-Enable-Raspberry-Touch-2-rotation-with-over.patch new file mode 100644 index 0000000000..1a709577eb --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1401-overlays-Enable-Raspberry-Touch-2-rotation-with-over.patch @@ -0,0 +1,31 @@ +From fa6ad4bcad4e8db18493a4af640b4b5c95434e70 Mon Sep 17 00:00:00 2001 +From: Just a nerd <157698061+foonerd@users.noreply.github.com> +Date: Wed, 20 Nov 2024 14:08:48 +0000 +Subject: [PATCH] overlays: Enable Raspberry Touch 2 rotation with overlay + +See: https://github.com/raspberrypi/linux/pull/6480 +Signed-off-by: foonerd +--- + arch/arm/boot/dts/overlays/README | 1 + + arch/arm/boot/dts/overlays/vc4-kms-dsi-ili9881-7inch-overlay.dts | 1 + + 2 files changed, 2 insertions(+) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -5249,6 +5249,7 @@ Params: sizex Touchscr + invy Touchscreen inverted y axis + swapxy Touchscreen swapped x y axis + disable_touch Disables the touch screen overlay driver ++ rotation Display rotation {0,90,180,270} (default 0) + dsi0 Use DSI0 and i2c_csi_dsi0 (rather than + the default DSI1 and i2c_csi_dsi). + +--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-ili9881-7inch-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-ili9881-7inch-overlay.dts +@@ -118,5 +118,6 @@ + invy = <0>, "+11"; + swapxy = <>911>,"touchscreen-swapped-x-y?"; + disable_touch = <>911>, "status=disabled"; ++ rotation = <&dsi_panel>, "rotation:0"; + }; + }; diff --git a/target/linux/bcm27xx/patches-6.6/950-1402-rp1-pio-Add-missing-static-inline-s.patch b/target/linux/bcm27xx/patches-6.6/950-1402-rp1-pio-Add-missing-static-inline-s.patch new file mode 100644 index 0000000000..643e83b314 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1402-rp1-pio-Add-missing-static-inline-s.patch @@ -0,0 +1,42 @@ +From df8a2f6dc114b2c5c7685a069f717f2b06186b74 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 20 Nov 2024 16:23:06 +0000 +Subject: [PATCH] rp1-pio: Add missing 'static inline's + +Avoid some duplicate symbol errors by adding some missing +'static inline' decorations. + +Signed-off-by: Phil Elwell +--- + include/linux/pio_rp1.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/include/linux/pio_rp1.h ++++ b/include/linux/pio_rp1.h +@@ -247,7 +247,7 @@ static inline bool pio_can_add_program_a + return !rp1_pio_can_add_program(client, &args); + } + +-uint pio_add_program(struct rp1_pio_client *client, const pio_program_t *program) ++static inline uint pio_add_program(struct rp1_pio_client *client, const pio_program_t *program) + { + struct rp1_pio_add_program_args args; + int offset; +@@ -367,7 +367,7 @@ static inline int pio_sm_set_config(stru + return rp1_pio_sm_set_config(client, &args); + } + +-int pio_sm_exec(struct rp1_pio_client *client, uint sm, uint instr) ++static inline int pio_sm_exec(struct rp1_pio_client *client, uint sm, uint instr) + { + struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = false }; + +@@ -377,7 +377,7 @@ int pio_sm_exec(struct rp1_pio_client *c + return rp1_pio_sm_exec(client, &args); + } + +-int pio_sm_exec_wait_blocking(struct rp1_pio_client *client, uint sm, uint instr) ++static inline int pio_sm_exec_wait_blocking(struct rp1_pio_client *client, uint sm, uint instr) + { + struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = true }; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1403-misc-rp1-pio-Back-port-some-6.11-build-fixes.patch b/target/linux/bcm27xx/patches-6.6/950-1403-misc-rp1-pio-Back-port-some-6.11-build-fixes.patch new file mode 100644 index 0000000000..92bde521d2 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1403-misc-rp1-pio-Back-port-some-6.11-build-fixes.patch @@ -0,0 +1,39 @@ +From d1f0c94e974a5f26d210b1d13a6ef9543bee4984 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 21 Nov 2024 11:11:48 +0000 +Subject: [PATCH] misc: rp1-pio: Back-port some 6.11 build fixes + +Porting rp1-pio to rpi-6.11.y uncovered a few missing #includes and a +difference of const-ness. Although not needed here, back-porting the +resulting changes makes the driver more "correct" and may prevent a +future merge conflict. + +Signed-off-by: Phil Elwell +--- + drivers/misc/rp1-pio.c | 3 +++ + include/linux/pio_rp1.h | 2 +- + 2 files changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/misc/rp1-pio.c ++++ b/drivers/misc/rp1-pio.c +@@ -22,6 +22,9 @@ + #include + #include + #include ++#include ++#include ++#include + #include + #include + #include +--- a/include/linux/pio_rp1.h ++++ b/include/linux/pio_rp1.h +@@ -176,7 +176,7 @@ typedef rp1_pio_sm_config pio_sm_config; + typedef struct rp1_pio_client *PIO; + + void pio_set_error(struct rp1_pio_client *client, int err); +-int pio_get_error(struct rp1_pio_client *client); ++int pio_get_error(const struct rp1_pio_client *client); + void pio_clear_error(struct rp1_pio_client *client); + + int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param); diff --git a/target/linux/bcm27xx/patches-6.6/950-1404-Adding-Pimidi-kernel-module.patch b/target/linux/bcm27xx/patches-6.6/950-1404-Adding-Pimidi-kernel-module.patch new file mode 100644 index 0000000000..fd46f25c20 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1404-Adding-Pimidi-kernel-module.patch @@ -0,0 +1,1167 @@ +From 8a6f640708627ac8ebf79f88793038933f169198 Mon Sep 17 00:00:00 2001 +From: Giedrius +Date: Thu, 21 Nov 2024 08:04:02 +0000 +Subject: [PATCH] Adding Pimidi kernel module. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Giedrius Trainavičius +--- + sound/drivers/Kconfig | 10 + + sound/drivers/Makefile | 2 + + sound/drivers/pimidi.c | 1113 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 1125 insertions(+) + create mode 100644 sound/drivers/pimidi.c + +--- a/sound/drivers/Kconfig ++++ b/sound/drivers/Kconfig +@@ -263,4 +263,14 @@ config SND_AC97_POWER_SAVE_DEFAULT + + See SND_AC97_POWER_SAVE for more details. + ++config SND_PIMIDI ++ tristate "Pimidi driver" ++ depends on SND_SEQUENCER && CRC8 ++ select SND_RAWMIDI ++ help ++ Say Y here to include support for Blokas Pimidi. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called snd-pimidi. ++ + endif # SND_DRIVERS +--- a/sound/drivers/Makefile ++++ b/sound/drivers/Makefile +@@ -9,6 +9,7 @@ snd-aloop-objs := aloop.o + snd-mtpav-objs := mtpav.o + snd-mts64-objs := mts64.o + snd-pcmtest-objs := pcmtest.o ++snd-pimidi-objs := pimidi.o + snd-portman2x4-objs := portman2x4.o + snd-serial-u16550-objs := serial-u16550.o + snd-serial-generic-objs := serial-generic.o +@@ -23,6 +24,7 @@ obj-$(CONFIG_SND_SERIAL_U16550) += snd-s + obj-$(CONFIG_SND_SERIAL_GENERIC) += snd-serial-generic.o + obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o + obj-$(CONFIG_SND_MTS64) += snd-mts64.o ++obj-$(CONFIG_SND_PIMIDI) += snd-pimidi.o + obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o + + obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/ +--- /dev/null ++++ b/sound/drivers/pimidi.c +@@ -0,0 +1,1113 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Pimidi Linux kernel module. ++ * Copyright (C) 2017-2024 Vilniaus Blokas UAB, https://blokas.io/ ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; version 2 of the ++ * License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define PIMIDI_LOG_IMPL(instance, log_func, msg, ...) log_func("pimidi(%s)[%c]: " msg "\n", \ ++ __func__, (instance) ? (instance)->d + '0' : 'G', ## __VA_ARGS__) ++ ++#ifdef PIMIDI_DEBUG ++# define printd(instance, ...) PIMIDI_LOG_IMPL(instance, pr_alert, __VA_ARGS__) ++# define printd_rl(instance, ...) PIMIDI_LOG_IMPL(instance, pr_alert_ratelimited, __VA_ARGS__) ++# define printd_g(...) printd((struct pimidi_instance *)NULL, __VA_ARGS__) ++#else ++# define printd(instance, ...) do {} while (0) ++# define printd_rl(instance, ...) do {} while (0) ++# define printd_g(...) do {} while (0) ++#endif ++ ++#define printe(instance, ...) PIMIDI_LOG_IMPL(instance, pr_err, __VA_ARGS__) ++#define printe_rl(instance, ...) PIMIDI_LOG_IMPL(instance, pr_err_ratelimited, __VA_ARGS__) ++#define printi(instance, ...) PIMIDI_LOG_IMPL(instance, pr_info, __VA_ARGS__) ++#define printw(instance, ...) PIMIDI_LOG_IMPL(instance, pr_warn, __VA_ARGS__) ++#define printw_rl(instance, ...) PIMIDI_LOG_IMPL(instance, pr_warn_ratelimited, __VA_ARGS__) ++ ++#define printe_g(...) printe((struct pimidi_instance *)NULL, __VA_ARGS__) ++#define printi_g(...) printi((struct pimidi_instance *)NULL, __VA_ARGS__) ++ ++DECLARE_CRC8_TABLE(pimidi_crc8_table); ++enum { PIMIDI_CRC8_POLYNOMIAL = 0x83 }; ++enum { PIMIDI_MAX_DEVICES = 4 }; ++enum { PIMIDI_MAX_PACKET_SIZE = 17 }; ++enum { PIMIDI_PORTS = 2 }; ++ ++struct pimidi_shared { ++ // lock protects the shared reset_gpio and devices list. ++ struct mutex lock; ++ struct gpio_desc *reset_gpio; ++ struct workqueue_struct *work_queue; ++ struct list_head devices; ++}; ++ ++static struct pimidi_shared pimidi_global = { ++ .devices = LIST_HEAD_INIT(pimidi_global.devices), ++}; ++ ++struct pimidi_version_t { ++ u8 hwrev; ++ u8 major; ++ u8 minor; ++ u8 build; ++}; ++ ++enum { PIMIDI_IN_FIFO_SIZE = 4096 }; ++ ++struct pimidi_midi_port { ++ // in_lock protects the input substream. ++ struct mutex in_lock; ++ // out_lock protects the output substream. ++ struct mutex out_lock; ++ DECLARE_KFIFO(in_fifo, uint8_t, PIMIDI_IN_FIFO_SIZE); ++ unsigned int last_output_at; ++ unsigned int output_buffer_used_in_millibytes; ++ struct work_struct in_handler; ++ struct delayed_work out_handler; ++ unsigned long enabled_streams; ++ unsigned int tx_cnt; ++ unsigned int rx_cnt; ++}; ++ ++struct pimidi_instance { ++ struct list_head list; ++ struct i2c_client *i2c_client; ++ struct pimidi_version_t version; ++ char serial[11]; ++ char d; ++ struct gpio_desc *data_ready_gpio; ++ ++ struct work_struct drdy_handler; ++ ++ // comm_lock serializes I2C communication. ++ struct mutex comm_lock; ++ char *rx_buf; ++ size_t rx_len; ++ int rx_status; ++ struct completion *rx_completion; ++ ++ struct snd_rawmidi *rawmidi; ++ struct pimidi_midi_port midi_port[PIMIDI_PORTS]; ++ bool stopping; ++}; ++ ++static struct snd_rawmidi_substream *pimidi_find_substream(struct snd_rawmidi *rawmidi, ++ int stream, ++ int number ++ ) ++{ ++ struct snd_rawmidi_substream *substream; ++ ++ list_for_each_entry(substream, &rawmidi->streams[stream].substreams, list) { ++ if (substream->number == number) ++ return substream; ++ } ++ return NULL; ++} ++ ++static void pimidi_midi_in_handler(struct pimidi_instance *instance, int port) ++{ ++ int i, n, err; ++ ++ printd(instance, "(%d)", port); ++ ++ struct pimidi_midi_port *midi_port = &instance->midi_port[port]; ++ ++ if (!test_bit(SNDRV_RAWMIDI_STREAM_INPUT, &midi_port->enabled_streams)) { ++ printd(instance, "Input not enabled for %d", port); ++ return; ++ } ++ ++ u8 data[512]; ++ ++ n = kfifo_out_peek(&midi_port->in_fifo, data, sizeof(data)); ++ printd(instance, "Peeked %d MIDI bytes", n); ++ ++ mutex_lock(&midi_port->in_lock); ++ struct snd_rawmidi_substream *substream = ++ pimidi_find_substream(instance->rawmidi, ++ SNDRV_RAWMIDI_STREAM_INPUT, ++ port); ++ ++ err = snd_rawmidi_receive(substream, data, n); ++ if (err > 0) ++ midi_port->rx_cnt += err; ++ mutex_unlock(&midi_port->in_lock); ++ ++ for (i = 0; i < err; ++i) ++ kfifo_skip(&midi_port->in_fifo); ++ ++ if (n != err) ++ printw_rl(instance, ++ "Not all MIDI data consumed for port %d: %d / %d", port, err, n); ++ ++ if (!kfifo_is_empty(&midi_port->in_fifo) && !instance->stopping) ++ queue_work(pimidi_global.work_queue, &midi_port->in_handler); ++ ++ printd(instance, "Done"); ++} ++ ++static void pimidi_midi_in_handler_0(struct work_struct *work) ++{ ++ pimidi_midi_in_handler(container_of(work, struct pimidi_instance, midi_port[0].in_handler), ++ 0); ++} ++ ++static void pimidi_midi_in_handler_1(struct work_struct *work) ++{ ++ pimidi_midi_in_handler(container_of(work, struct pimidi_instance, midi_port[1].in_handler), ++ 1); ++} ++ ++static void pimidi_midi_out_handler(struct pimidi_instance *instance, int port) ++{ ++ printd(instance, "(%d)", port); ++ if (!test_bit(SNDRV_RAWMIDI_STREAM_OUTPUT, &instance->midi_port[port].enabled_streams)) { ++ printd(instance, "Output not enabled for %d", port); ++ return; ++ } ++ ++ struct pimidi_midi_port *midi_port = &instance->midi_port[port]; ++ ++ struct snd_rawmidi_substream *substream = ++ pimidi_find_substream(instance->rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, port); ++ ++ mutex_lock(&midi_port->out_lock); ++ ++ enum { MIDI_MILLI_BYTES_PER_JIFFY = 3125000 / HZ }; ++ enum { MIDI_MAX_OUTPUT_BUFFER_SIZE_IN_MILLIBYTES = ++ (512 - PIMIDI_MAX_PACKET_SIZE - 1) * 1000 }; ++ ++ unsigned int now = jiffies; ++ unsigned int millibytes_became_available = ++ (MIDI_MILLI_BYTES_PER_JIFFY) * (now - midi_port->last_output_at); ++ ++ midi_port->output_buffer_used_in_millibytes = ++ midi_port->output_buffer_used_in_millibytes <= ++ millibytes_became_available ? 0 : midi_port->output_buffer_used_in_millibytes - ++ millibytes_became_available; ++ ++ unsigned int output_buffer_available = ++ (MIDI_MAX_OUTPUT_BUFFER_SIZE_IN_MILLIBYTES ++ - midi_port->output_buffer_used_in_millibytes) ++ / 1000; ++ ++ u8 buffer[PIMIDI_MAX_PACKET_SIZE]; ++ int n, batch, err; ++ ++ for (batch = 0; batch < 3; ++batch) { ++ if (output_buffer_available == 0) ++ printd(instance, "Buffer full"); ++ ++ printd(instance, "Buffer available: %u (%u +%u, %u -> %u, dt %u) (%u) @ %u", ++ output_buffer_available, midi_port->output_buffer_used_in_millibytes, ++ millibytes_became_available, midi_port->last_output_at, now, ++ now - midi_port->last_output_at, midi_port->tx_cnt, HZ); ++ midi_port->last_output_at = now; ++ ++ n = output_buffer_available ++ ? snd_rawmidi_transmit_peek(substream, buffer + 1, ++ min(output_buffer_available, ++ sizeof(buffer) - 2)) ++ : 0; ++ if (n > 0) { ++ printd(instance, "Peeked: %d", n); ++ snd_rawmidi_transmit_ack(substream, n); ++ ++ buffer[0] = (port << 4) | n; ++ buffer[n + 1] = ~crc8(pimidi_crc8_table, buffer, n + 1, CRC8_INIT_VALUE); ++ ++#ifdef PIMIDI_DEBUG ++ pr_debug("%s[%d]: Sending %d bytes:", __func__, instance->d, n + 2); ++ int i; ++ ++ for (i = 0; i < n + 2; ++i) ++ pr_cont(" %02x", buffer[i]); ++ ++ pr_cont("\n"); ++#endif ++ mutex_lock(&instance->comm_lock); ++ err = i2c_master_send(instance->i2c_client, buffer, n + 2); ++ mutex_unlock(&instance->comm_lock); ++ ++ if (err < 0) { ++ printe(instance, ++ "Error occurred when sending MIDI data over I2C! (%d)", ++ err); ++ goto cleanup; ++ } ++ ++ midi_port->tx_cnt += n; ++ midi_port->output_buffer_used_in_millibytes += n * 1000; ++ output_buffer_available -= n; ++ } else if (n < 0) { ++ err = n; ++ printe(instance, "snd_rawmidi_transmit_peek returned error %d!", err); ++ goto cleanup; ++ } else { ++ break; ++ } ++ } ++ ++ printd(instance, "Checking if empty %p", substream); ++ if (!snd_rawmidi_transmit_empty(substream) && !instance->stopping) { ++ unsigned int delay = 1; ++ ++ if (output_buffer_available == 0) ++ delay = 125000 / MIDI_MILLI_BYTES_PER_JIFFY; ++ printd(instance, "Queue more work after %u jiffies", delay); ++ mod_delayed_work(pimidi_global.work_queue, &midi_port->out_handler, delay); ++ } ++ ++cleanup: ++ mutex_unlock(&midi_port->out_lock); ++ printd(instance, "Done"); ++} ++ ++static void pimidi_midi_out_handler_0(struct work_struct *work) ++{ ++ pimidi_midi_out_handler(container_of(work, struct pimidi_instance, ++ midi_port[0].out_handler.work), 0); ++} ++ ++static void pimidi_midi_out_handler_1(struct work_struct *work) ++{ ++ pimidi_midi_out_handler(container_of(work, struct pimidi_instance, ++ midi_port[1].out_handler.work), 1); ++} ++ ++static void pimidi_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) ++{ ++ struct pimidi_instance *instance = substream->rmidi->private_data; ++ ++ printd(instance, "(%d, %d, %d)", substream->stream, substream->number, up); ++ ++ if (up == 0) { ++ clear_bit(substream->stream, ++ &instance->midi_port[substream->number].enabled_streams); ++ } else { ++ set_bit(substream->stream, ++ &instance->midi_port[substream->number].enabled_streams); ++ if (!delayed_work_pending(&instance->midi_port[substream->number].out_handler)) { ++ printd(instance, "Queueing work"); ++ queue_delayed_work(pimidi_global.work_queue, ++ &instance->midi_port[substream->number].out_handler, 0); ++ } ++ } ++} ++ ++static void pimidi_midi_output_drain(struct snd_rawmidi_substream *substream) ++{ ++ struct pimidi_instance *instance = substream->rmidi->private_data; ++ ++ printd(instance, "(%d, %d)", substream->stream, substream->number); ++ ++ printd(instance, "Begin draining!"); ++ ++ queue_delayed_work(pimidi_global.work_queue, ++ &instance->midi_port[substream->number].out_handler, 0); ++ ++ unsigned long deadline = jiffies + 5 * HZ; ++ ++ do { ++ printd(instance, "Before flush"); ++ while (delayed_work_pending(&instance->midi_port[substream->number].out_handler)) ++ flush_delayed_work(&instance->midi_port[substream->number].out_handler); ++ printd(instance, "Flushed"); ++ } while (!snd_rawmidi_transmit_empty(substream) && time_before(jiffies, deadline)); ++ ++ printd(instance, "Done!"); ++} ++ ++static int pimidi_midi_output_close(struct snd_rawmidi_substream *substream) ++{ ++ struct pimidi_instance *instance = substream->rmidi->private_data; ++ struct pimidi_midi_port *midi_port = &instance->midi_port[substream->number]; ++ ++ mutex_lock(&midi_port->out_lock); ++ clear_bit(substream->stream, &midi_port->enabled_streams); ++ mutex_unlock(&midi_port->out_lock); ++ return 0; ++} ++ ++static int pimidi_midi_input_close(struct snd_rawmidi_substream *substream) ++{ ++ struct pimidi_instance *instance = substream->rmidi->private_data; ++ struct pimidi_midi_port *midi_port = &instance->midi_port[substream->number]; ++ ++ mutex_lock(&midi_port->in_lock); ++ clear_bit(substream->stream, &midi_port->enabled_streams); ++ mutex_unlock(&midi_port->in_lock); ++ return 0; ++} ++ ++static void pimidi_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) ++{ ++ struct pimidi_instance *instance = substream->rmidi->private_data; ++ ++ printd(instance, "(%d, %d, %d)", substream->stream, substream->number, up); ++ ++ if (up == 0) { ++ clear_bit(substream->stream, ++ &instance->midi_port[substream->number].enabled_streams); ++ cancel_work_sync(&instance->midi_port[substream->number].in_handler); ++ } else { ++ set_bit(substream->stream, ++ &instance->midi_port[substream->number].enabled_streams); ++ if (!instance->stopping) ++ queue_work(pimidi_global.work_queue, ++ &instance->midi_port[substream->number].in_handler); ++ } ++} ++ ++static void pimidi_get_port_info(struct snd_rawmidi *rmidi, int number, ++ struct snd_seq_port_info *seq_port_info) ++{ ++ printd_g("%p, %d, %p", rmidi, number, seq_port_info); ++ seq_port_info->type = ++ SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | ++ SNDRV_SEQ_PORT_TYPE_HARDWARE | ++ SNDRV_SEQ_PORT_TYPE_PORT; ++ strscpy(seq_port_info->name, number == 0 ? "a" : "b", ++ sizeof(seq_port_info->name)); ++ seq_port_info->midi_voices = 0; ++} ++ ++static const struct snd_rawmidi_global_ops pimidi_midi_ops = { ++ .get_port_info = pimidi_get_port_info, ++}; ++ ++static int pimidi_midi_open(struct snd_rawmidi_substream *substream) ++{ ++ printd_g("(%p) stream=%d number=%d", substream, substream->stream, substream->number); ++ return 0; ++} ++ ++static const struct snd_rawmidi_ops pimidi_midi_output_ops = { ++ .open = pimidi_midi_open, ++ .close = pimidi_midi_output_close, ++ .trigger = pimidi_midi_output_trigger, ++ .drain = pimidi_midi_output_drain, ++}; ++ ++static const struct snd_rawmidi_ops pimidi_midi_input_ops = { ++ .open = pimidi_midi_open, ++ .close = pimidi_midi_input_close, ++ .trigger = pimidi_midi_input_trigger, ++}; ++ ++static int pimidi_register(struct pimidi_instance *instance) ++{ ++ int err = 0; ++ ++ mutex_lock(&pimidi_global.lock); ++ printd(instance, "Registering..."); ++ if (!pimidi_global.reset_gpio) { ++ printd_g("Getting reset pin."); ++ pimidi_global.reset_gpio = gpiod_get(&instance->i2c_client->dev, "reset", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(pimidi_global.reset_gpio)) { ++ err = PTR_ERR(pimidi_global.reset_gpio); ++ printe_g("gpiod_get failed: %d", err); ++ pimidi_global.reset_gpio = NULL; ++ mutex_unlock(&pimidi_global.lock); ++ return err; ++ } ++ } ++ list_add_tail(&instance->list, &pimidi_global.devices); ++ mutex_unlock(&pimidi_global.lock); ++ return err; ++} ++ ++static void pimidi_unregister(struct pimidi_instance *instance) ++{ ++ mutex_lock(&pimidi_global.lock); ++ printd(instance, "Unregistering..."); ++ list_del(&instance->list); ++ if (list_empty(&pimidi_global.devices)) { ++ printd_g("Releasing reset pin"); ++ gpiod_put(pimidi_global.reset_gpio); ++ pimidi_global.reset_gpio = NULL; ++ } ++ mutex_unlock(&pimidi_global.lock); ++} ++ ++static void pimidi_perform_reset(void) ++{ ++ mutex_lock(&pimidi_global.lock); ++ ++ printd_g("Performing reset."); ++ ++ struct list_head *p; ++ ++ list_for_each(p, &pimidi_global.devices) { ++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, list); ++ ++ printd(instance, "Pausing..."); ++ instance->stopping = true; ++ disable_irq(instance->i2c_client->irq); ++ cancel_work(&instance->drdy_handler); ++ ++ int i; ++ ++ for (i = 0; i < PIMIDI_PORTS; ++i) { ++ cancel_work(&instance->midi_port[i].in_handler); ++ cancel_delayed_work(&instance->midi_port[i].out_handler); ++ } ++ ++ drain_workqueue(pimidi_global.work_queue); ++ } ++ ++ printd_g("Reset = low"); ++ gpiod_set_value(pimidi_global.reset_gpio, 1); ++ ++ list_for_each(p, &pimidi_global.devices) { ++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, list); ++ ++ if (gpiod_is_active_low(instance->data_ready_gpio)) ++ gpiod_toggle_active_low(instance->data_ready_gpio); ++ gpiod_direction_output(instance->data_ready_gpio, 1); ++ printd(instance, "DRDY high"); ++ } ++ ++ usleep_range(1000, 5000); ++ printd_g("Reset = high"); ++ gpiod_set_value(pimidi_global.reset_gpio, 0); ++ msleep(30); ++ ++ int i; ++ ++ for (i = 0; i < PIMIDI_MAX_DEVICES; ++i) { ++ usleep_range(1000, 3000); ++ list_for_each(p, &pimidi_global.devices) { ++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, ++ list); ++ ++ if (instance->d < i) ++ continue; ++ printd(instance, "DRDY -> %d", !gpiod_get_value(instance->data_ready_gpio)); ++ gpiod_set_value(instance->data_ready_gpio, ++ !gpiod_get_value(instance->data_ready_gpio)); ++ } ++ } ++ usleep_range(16000, 20000); ++ ++ list_for_each(p, &pimidi_global.devices) { ++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, list); ++ ++ if (!gpiod_is_active_low(instance->data_ready_gpio)) ++ gpiod_toggle_active_low(instance->data_ready_gpio); ++ ++ printd(instance, "DRDY input"); ++ gpiod_direction_input(instance->data_ready_gpio); ++ ++ printd(instance, "Resume..."); ++ instance->stopping = false; ++ enable_irq(instance->i2c_client->irq); ++ } ++ ++ printd_g("Reset done."); ++ usleep_range(16000, 20000); ++ ++ mutex_unlock(&pimidi_global.lock); ++} ++ ++static int pimidi_read_version(struct pimidi_version_t *version, struct pimidi_instance *instance) ++{ ++ memset(version, 0, sizeof(*version)); ++ ++ const char cmd[4] = { 0xb2, 0x01, 0x01, 0x95 }; ++ ++ char result[9]; ++ ++ memset(result, 0, sizeof(result)); ++ ++ DECLARE_COMPLETION_ONSTACK(done); ++ ++ mutex_lock(&instance->comm_lock); ++ int err = i2c_master_send(instance->i2c_client, cmd, sizeof(cmd)); ++ ++ if (err < 0) { ++ mutex_unlock(&instance->comm_lock); ++ return err; ++ } ++ instance->rx_buf = result; ++ instance->rx_len = sizeof(result); ++ instance->rx_completion = &done; ++ mutex_unlock(&instance->comm_lock); ++ ++ printd(instance, "Waiting for drdy"); ++ wait_for_completion_io_timeout(&done, msecs_to_jiffies(1000u)); ++ printd(instance, "Done waiting"); ++ ++ if (!completion_done(&done)) { ++ mutex_lock(&instance->comm_lock); ++ instance->rx_buf = NULL; ++ instance->rx_len = 0; ++ instance->rx_status = -ETIMEDOUT; ++ instance->rx_completion = NULL; ++ mutex_unlock(&instance->comm_lock); ++ return -ETIMEDOUT; ++ } ++ ++ if (CRC8_GOOD_VALUE(pimidi_crc8_table) != crc8(pimidi_crc8_table, result, sizeof(result), ++ CRC8_INIT_VALUE)) ++ return -EIO; ++ ++ const char expected[4] = { 0xb7, 0x81, 0x01, 0x00 }; ++ ++ if (memcmp(result, expected, sizeof(expected)) != 0) ++ return -EPROTO; ++ ++ u32 v = ntohl(*(uint32_t *)(result + 4)); ++ ++ version->hwrev = v >> 24; ++ version->major = (v & 0x00ff0000) >> 16; ++ version->minor = (v & 0x0000ff00) >> 8; ++ version->build = v & 0x000000ff; ++ ++ return 0; ++} ++ ++static int pimidi_read_serial(char serial[11], struct pimidi_instance *instance) ++{ ++ memset(serial, 0, sizeof(char[11])); ++ ++ const char cmd[4] = { 0xb2, 0x03, 0x04, 0x97 }; ++ ++ char result[PIMIDI_MAX_PACKET_SIZE]; ++ ++ memset(result, 0, sizeof(result)); ++ ++ DECLARE_COMPLETION_ONSTACK(done); ++ ++ mutex_lock(&instance->comm_lock); ++ int err = i2c_master_send(instance->i2c_client, cmd, sizeof(cmd)); ++ ++ if (err < 0) { ++ mutex_unlock(&instance->comm_lock); ++ return err; ++ } ++ instance->rx_buf = result; ++ instance->rx_len = sizeof(result); ++ instance->rx_completion = &done; ++ mutex_unlock(&instance->comm_lock); ++ ++ printd(instance, "Waiting for drdy"); ++ wait_for_completion_io_timeout(&done, msecs_to_jiffies(1000u)); ++ printd(instance, "Done waiting"); ++ ++ if (!completion_done(&done)) { ++ mutex_lock(&instance->comm_lock); ++ instance->rx_buf = NULL; ++ instance->rx_len = 0; ++ instance->rx_status = -ETIMEDOUT; ++ instance->rx_completion = NULL; ++ mutex_unlock(&instance->comm_lock); ++ printe(instance, "Timed out"); ++ return -ETIMEDOUT; ++ } ++ ++ if (CRC8_GOOD_VALUE(pimidi_crc8_table) != crc8(pimidi_crc8_table, result, ++ (result[0] & 0x0f) + 2, CRC8_INIT_VALUE)) ++ return -EIO; ++ ++ const char expected[4] = { 0xbd, 0x83, 0x04, 0x0a }; ++ ++ if (memcmp(result, expected, sizeof(expected)) != 0) { ++ printe(instance, "Unexpected response: %02x %02x %02x %02x", result[0], result[1], ++ result[2], result[3]); ++ return -EPROTO; ++ } ++ ++ memcpy(serial, result + 4, 10); ++ ++ if (strspn(serial, "\xff") == 10) ++ strscpy(serial, "(unset)", 8); ++ ++ return 0; ++} ++ ++static void pimidi_handle_midi_data(struct pimidi_instance *instance, int port, const uint8_t *data, ++ unsigned int n) ++{ ++ printd(instance, "Handling MIDI data for port %d (%u bytes)", port, n); ++ if (n == 0) ++ return; ++ ++ struct pimidi_midi_port *midi_port = &instance->midi_port[port]; ++ ++ kfifo_in(&midi_port->in_fifo, data, n); ++ ++ if (!instance->stopping) ++ queue_work(pimidi_global.work_queue, &midi_port->in_handler); ++ ++ printd(instance, "Done"); ++} ++ ++static void pimidi_drdy_continue(struct pimidi_instance *instance) ++{ ++ if (instance->stopping) { ++ printd(instance, "Refusing to queue work / enable IRQ due to stopping."); ++ return; ++ } ++ ++ if (gpiod_get_value(instance->data_ready_gpio)) { ++ printd_rl(instance, "Queue work due to DRDY line still low"); ++ queue_work(pimidi_global.work_queue, &instance->drdy_handler); ++ } else { ++ printd_rl(instance, "Enabling irq for more data"); ++ enable_irq(gpiod_to_irq(instance->data_ready_gpio)); ++ } ++} ++ ++static void pimidi_drdy_handler(struct work_struct *work) ++{ ++ struct pimidi_instance *instance = container_of(work, struct pimidi_instance, drdy_handler); ++ ++ printd(instance, "(%p)", work); ++ ++ mutex_lock(&instance->comm_lock); ++ if (!instance->rx_completion) { ++ u8 data[PIMIDI_MAX_PACKET_SIZE]; ++ int n = i2c_master_recv(instance->i2c_client, data, 3); ++ ++ if (n < 0) { ++ printe(instance, "Error reading from device: %d", n); ++ mutex_unlock(&instance->comm_lock); ++ pimidi_drdy_continue(instance); ++ return; ++ } ++ ++ if (data[0] == 0xfe) { ++ printe_rl(instance, "Invalid packet 0x%02x 0x%02x 0x%02x", data[0], data[1], ++ data[2]); ++ mutex_unlock(&instance->comm_lock); ++ pimidi_drdy_continue(instance); ++ return; ++ } ++ ++ int len = (data[0] & 0x0f) + 2; ++ ++ if (len > n) { ++ printd(instance, "Need %d more bytes", len - n); ++ int err = i2c_master_recv(instance->i2c_client, data + n, len - n); ++ ++ if (err < 0) { ++ printe(instance, "Error reading remainder from device: %d", err); ++ mutex_unlock(&instance->comm_lock); ++ pimidi_drdy_continue(instance); ++ return; ++#ifdef PIMIDI_DEBUG ++ } else { ++ pr_debug("Recv_2:"); ++ int i; ++ ++ for (i = n; i < len; ++i) ++ pr_cont(" %02x", data[i]); ++ pr_cont("\n"); ++#endif ++ } ++ } ++ ++ if (CRC8_GOOD_VALUE(pimidi_crc8_table) == crc8(pimidi_crc8_table, data, len, ++ CRC8_INIT_VALUE)) { ++ switch (data[0] & 0xf0) { ++ case 0x00: ++ pimidi_handle_midi_data(instance, 0, data + 1, len - 2); ++ break; ++ case 0x10: ++ pimidi_handle_midi_data(instance, 1, data + 1, len - 2); ++ break; ++ default: ++ printd(instance, "Unhandled command %02x", data[0]); ++ break; ++ } ++ } else { ++ printe(instance, "I2C rx corruption detected."); ++ pr_info("Packet [%d]:", len); ++ int i; ++ ++ for (i = 0; i < len; ++i) ++ pr_cont(" %02x", data[i]); ++ pr_cont("\n"); ++ } ++ ++ mutex_unlock(&instance->comm_lock); ++ } else { ++ printd(instance, "Completing drdy"); ++ instance->rx_status = i2c_master_recv(instance->i2c_client, instance->rx_buf, 3); ++ printd(instance, "Recv_1 %02x %02x %02x", instance->rx_buf[0], instance->rx_buf[1], ++ instance->rx_buf[2]); ++ if (instance->rx_len > 3 && instance->rx_status == 3) { ++ instance->rx_status = i2c_master_recv(instance->i2c_client, ++ instance->rx_buf + 3, ++ instance->rx_len - 3); ++ if (instance->rx_status >= 0) ++ instance->rx_status += 3; ++#ifdef PIMIDI_DEBUG ++ pr_debug("Recv_2:"); ++ int i; ++ ++ for (i = 3; i < instance->rx_len; ++i) ++ pr_cont(" %02x", instance->rx_buf[i]); ++ pr_cont("\n"); ++#endif ++ } ++ struct completion *done = instance->rx_completion; ++ ++ instance->rx_buf = NULL; ++ instance->rx_len = 0; ++ instance->rx_completion = NULL; ++ complete_all(done); ++ mutex_unlock(&instance->comm_lock); ++ } ++ ++ pimidi_drdy_continue(instance); ++} ++ ++static irqreturn_t pimidi_drdy_interrupt_handler(int irq, void *dev_id) ++{ ++ struct pimidi_instance *instance = (struct pimidi_instance *)dev_id; ++ ++ if (instance->stopping) { ++ printd(instance, "DRDY interrupt, but stopping, ignoring..."); ++ return IRQ_HANDLED; ++ } ++ ++ printd(instance, "DRDY interrupt, masking"); ++ disable_irq_nosync(irq); ++ ++ printd(instance, "Queue work due to DRDY interrupt"); ++ queue_work(pimidi_global.work_queue, &instance->drdy_handler); ++ ++ return IRQ_HANDLED; ++} ++ ++static void pimidi_proc_stat_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer) ++{ ++ const unsigned int *d = entry->private_data; ++ ++ snd_iprintf(buffer, "%u\n", *d); ++} ++ ++static void pimidi_proc_serial_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer) ++{ ++ struct pimidi_instance *instance = entry->private_data; ++ ++ snd_iprintf(buffer, "%s\n", instance->serial); ++} ++ ++static void pimidi_proc_version_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer) ++{ ++ struct pimidi_instance *instance = entry->private_data; ++ ++ snd_iprintf(buffer, "%u.%u.%u\n", instance->version.major, instance->version.minor, ++ instance->version.build); ++} ++ ++static void pimidi_proc_hwrev_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer) ++{ ++ struct pimidi_instance *instance = entry->private_data; ++ ++ snd_iprintf(buffer, "%u\n", instance->version.hwrev); ++} ++ ++static int pimidi_i2c_probe(struct i2c_client *client) ++{ ++ struct snd_card *card = NULL; ++ int err, d, i; ++ ++ d = client->addr - 0x20; ++ ++ if (d < 0 || d >= 8) { ++ printe_g("Unexpected device address: %d", client->addr); ++ err = -EINVAL; ++ goto finalize; ++ } ++ ++ err = snd_card_new(&client->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, ++ sizeof(struct pimidi_instance), &card); ++ ++ if (err) { ++ printe_g("snd_card_new failed: %d", err); ++ return err; ++ } ++ ++ struct pimidi_instance *instance = (struct pimidi_instance *)card->private_data; ++ ++ instance->i2c_client = client; ++ instance->d = d; ++ ++ struct snd_rawmidi *rawmidi; ++ ++ err = snd_rawmidi_new(card, card->shortname, 0, 2, 2, &rawmidi); ++ if (err < 0) { ++ printe(instance, "snd_rawmidi_new failed: %d", err); ++ goto finalize; ++ } ++ ++ instance->rawmidi = rawmidi; ++ strscpy(rawmidi->name, "pimidi", sizeof(rawmidi->name)); ++ ++ rawmidi->info_flags = ++ SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; ++ rawmidi->private_data = instance; ++ rawmidi->ops = &pimidi_midi_ops; ++ ++ snd_rawmidi_set_ops(rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &pimidi_midi_output_ops); ++ snd_rawmidi_set_ops(rawmidi, SNDRV_RAWMIDI_STREAM_INPUT, &pimidi_midi_input_ops); ++ ++ instance->data_ready_gpio = devm_gpiod_get(&client->dev, "data-ready", GPIOD_OUT_HIGH); ++ if (IS_ERR(instance->data_ready_gpio)) { ++ err = PTR_ERR(instance->data_ready_gpio); ++ printe(instance, "devm_gpiod_get failed: %d", err); ++ goto finalize; ++ } ++ ++ err = pimidi_register(instance); ++ if (err < 0) { ++ printe(instance, "pimidi_register failed: %d", err); ++ goto finalize; ++ } ++ ++ pimidi_perform_reset(); ++ ++ INIT_WORK(&instance->drdy_handler, pimidi_drdy_handler); ++ mutex_init(&instance->comm_lock); ++ ++ err = devm_request_irq(&client->dev, client->irq, pimidi_drdy_interrupt_handler, ++ IRQF_SHARED | IRQF_TRIGGER_LOW, "data_ready_int", instance); ++ ++ if (err != 0) { ++ printe(instance, "data_available IRQ request failed! %d", err); ++ goto finalize; ++ } ++ ++ err = pimidi_read_version(&instance->version, instance); ++ if (err < 0) { ++ printe(instance, "pimidi_read_version failed: %d", err); ++ goto finalize; ++ } ++ ++ err = pimidi_read_serial(instance->serial, instance); ++ if (err < 0) { ++ printe(instance, "pimidi_read_serial failed: %d", err); ++ goto finalize; ++ } else if (instance->serial[0] != 'P' || instance->serial[1] != 'M' || ++ strlen(instance->serial) != 10) { ++ printe(instance, "Unexpected serial number: %s", instance->serial); ++ err = -EIO; ++ goto finalize; ++ } ++ ++ printi(instance, "pimidi%d hw:%d version %u.%u.%u-%u, serial %s", ++ d, ++ card->number, ++ instance->version.major, ++ instance->version.minor, ++ instance->version.build, ++ instance->version.hwrev, ++ instance->serial ++ ); ++ ++ strscpy(card->driver, "snd-pimidi", sizeof(card->driver)); ++ snprintf(card->shortname, sizeof(card->shortname), "pimidi%d", d); ++ snprintf(card->longname, sizeof(card->longname), "pimidi%d %s", d, instance->serial); ++ snprintf(card->id, sizeof(card->id), "pimidi%d", d); ++ ++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 0)->name, ++ 10u, "pimidi%d-a", d); ++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_INPUT, 0)->name, ++ 10u, "pimidi%d-a", d); ++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 1)->name, ++ 10u, "pimidi%d-b", d); ++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_INPUT, 1)->name, ++ 10u, "pimidi%d-b", d); ++ ++ err = snd_card_ro_proc_new(card, "a-tx", &instance->midi_port[0].tx_cnt, ++ pimidi_proc_stat_show); ++ err = snd_card_ro_proc_new(card, "a-rx", &instance->midi_port[0].rx_cnt, ++ pimidi_proc_stat_show); ++ err = snd_card_ro_proc_new(card, "b-tx", &instance->midi_port[1].tx_cnt, ++ pimidi_proc_stat_show); ++ err = snd_card_ro_proc_new(card, "b-rx", &instance->midi_port[1].rx_cnt, ++ pimidi_proc_stat_show); ++ err = snd_card_ro_proc_new(card, "serial", instance, pimidi_proc_serial_show); ++ err = snd_card_ro_proc_new(card, "version", instance, pimidi_proc_version_show); ++ err = snd_card_ro_proc_new(card, "hwrev", instance, pimidi_proc_hwrev_show); ++ if (err < 0) { ++ printe(instance, "snd_card_ro_proc_new failed: %d", err); ++ goto finalize; ++ } ++ ++ err = snd_card_register(card); ++ if (err < 0) { ++ printe(instance, "snd_card_register failed: %d", err); ++ goto finalize; ++ } ++ ++finalize: ++ if (err) { ++ instance->stopping = true; ++ cancel_work_sync(&instance->drdy_handler); ++ mutex_destroy(&instance->comm_lock); ++ pimidi_unregister(instance); ++ snd_card_free(card); ++ return err; ++ } ++ ++ for (i = 0; i < PIMIDI_PORTS; ++i) { ++ struct pimidi_midi_port *port = &instance->midi_port[i]; ++ ++ mutex_init(&port->in_lock); ++ mutex_init(&port->out_lock); ++ INIT_WORK(&port->in_handler, ++ i == 0 ? pimidi_midi_in_handler_0 : pimidi_midi_in_handler_1); ++ INIT_DELAYED_WORK(&port->out_handler, ++ i == 0 ? pimidi_midi_out_handler_0 : pimidi_midi_out_handler_1); ++ INIT_KFIFO(port->in_fifo); ++ port->last_output_at = jiffies; ++ } ++ ++ i2c_set_clientdata(client, card); ++ return 0; ++} ++ ++static void pimidi_i2c_remove(struct i2c_client *client) ++{ ++ printd_g("(%p)", client); ++ ++ int i; ++ struct snd_card *card = i2c_get_clientdata(client); ++ ++ if (card) { ++ printi_g("Unloading hw:%d %s", card->number, card->longname); ++ struct pimidi_instance *instance = (struct pimidi_instance *)card->private_data; ++ ++ instance->stopping = true; ++ i2c_set_clientdata(client, NULL); ++ devm_free_irq(&client->dev, client->irq, instance); ++ cancel_work_sync(&instance->drdy_handler); ++ ++ for (i = 0; i < PIMIDI_PORTS; ++i) { ++ cancel_work_sync(&instance->midi_port[i].in_handler); ++ cancel_delayed_work_sync(&instance->midi_port[i].out_handler); ++ mutex_destroy(&instance->midi_port[i].out_lock); ++ mutex_destroy(&instance->midi_port[i].in_lock); ++ kfifo_free(&instance->midi_port[i].in_fifo); ++ } ++ ++ mutex_destroy(&instance->comm_lock); ++ pimidi_unregister(instance); ++ snd_card_free(card); ++ } ++} ++ ++static const struct i2c_device_id pimidi_i2c_ids[] = { ++ { "pimidi", 0 }, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, pimidi_i2c_ids); ++ ++static const struct of_device_id pimidi_i2c_dt_ids[] = { ++ { .compatible = "blokaslabs,pimidi", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, pimidi_i2c_dt_ids); ++ ++static struct i2c_driver pimidi_i2c_driver = { ++ .driver = { ++ .name = "pimidi", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(pimidi_i2c_dt_ids), ++ }, ++ .probe = pimidi_i2c_probe, ++ .remove = pimidi_i2c_remove, ++ .id_table = pimidi_i2c_ids, ++}; ++ ++int pimidi_module_init(void) ++{ ++ int err = 0; ++ ++ mutex_init(&pimidi_global.lock); ++ ++ INIT_LIST_HEAD(&pimidi_global.devices); ++ ++ pimidi_global.work_queue = create_singlethread_workqueue("pimidi"); ++ if (!pimidi_global.work_queue) { ++ err = -ENOMEM; ++ goto cleanup; ++ } ++ ++ err = i2c_add_driver(&pimidi_i2c_driver); ++ if (err < 0) ++ goto cleanup; ++ ++ crc8_populate_msb(pimidi_crc8_table, PIMIDI_CRC8_POLYNOMIAL); ++ ++ return 0; ++ ++cleanup: ++ mutex_destroy(&pimidi_global.lock); ++ return err; ++} ++ ++void pimidi_module_exit(void) ++{ ++ i2c_del_driver(&pimidi_i2c_driver); ++ mutex_lock(&pimidi_global.lock); ++ if (pimidi_global.reset_gpio) { ++ gpiod_put(pimidi_global.reset_gpio); ++ pimidi_global.reset_gpio = NULL; ++ } ++ mutex_unlock(&pimidi_global.lock); ++ ++ destroy_workqueue(pimidi_global.work_queue); ++ pimidi_global.work_queue = NULL; ++ ++ mutex_destroy(&pimidi_global.lock); ++} ++ ++module_init(pimidi_module_init); ++module_exit(pimidi_module_exit); ++ ++MODULE_AUTHOR("Giedrius Trainavi\xc4\x8dius "); ++MODULE_DESCRIPTION("MIDI driver for Blokas Pimidi, https://blokas.io/"); ++MODULE_LICENSE("GPL"); ++ ++/* vim: set ts=8 sw=8 noexpandtab: */ diff --git a/target/linux/bcm27xx/patches-6.6/950-1406-Adding-pimidi-overlay.dts.patch b/target/linux/bcm27xx/patches-6.6/950-1406-Adding-pimidi-overlay.dts.patch new file mode 100644 index 0000000000..ab46dbdb98 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1406-Adding-pimidi-overlay.dts.patch @@ -0,0 +1,100 @@ +From 75ab92b077602734458f0a77e19a3599be29b93b Mon Sep 17 00:00:00 2001 +From: Giedrius +Date: Thu, 21 Nov 2024 08:05:49 +0000 +Subject: [PATCH] Adding pimidi-overlay.dts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Giedrius Trainavičius +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 8 +++ + arch/arm/boot/dts/overlays/pimidi-overlay.dts | 54 +++++++++++++++++++ + 3 files changed, 63 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/pimidi-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -203,6 +203,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + pifi-dac-zero.dtbo \ + pifi-mini-210.dtbo \ + piglow.dtbo \ ++ pimidi.dtbo \ + pineboards-hat-ai.dtbo \ + pineboards-hatdrive-poe-plus.dtbo \ + piscreen.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -3701,6 +3701,14 @@ Load: dtoverlay=piglow + Params: + + ++Name: pimidi ++Info: Configures the Blokas Labs Pimidi card ++Load: dtoverlay=pimidi,= ++Params: sel The position used for the sel rotary switch. ++ Each unit in the stack must be set on a unique ++ position. If param is omitted, sel=0 is assumed. ++ ++ + Name: pineboards-hat-ai + Info: Pineboards Hat Ai! overlay for the Google Coral Edge TPU + Load: dtoverlay=pineboards-hat-ai +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pimidi-overlay.dts +@@ -0,0 +1,54 @@ ++/* ++ * Pimidi Linux kernel module. ++ * Copyright (C) 2017-2024 Vilniaus Blokas UAB, https://blokas.io/ ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; version 2 of the ++ * License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++#include ++#include ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&i2c_arm>; ++ __overlay__ { ++ status = "okay"; ++ clock-frequency=<1000000>; ++ ++ pimidi_ctrl: pimidi_ctrl@20 { ++ compatible = "blokaslabs,pimidi"; ++ ++ reg = <0x20>; ++ status = "okay"; ++ ++ interrupt-parent = <&gpio>; ++ interrupts = <23 IRQ_TYPE_LEVEL_LOW>; ++ interrupt-names = "data_ready"; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ ++ data-ready-gpios = <&gpio 23 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; ++ reset-gpios = <&gpio 22 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ sel = <&pimidi_ctrl>,"reg:0{0=0x20,1=0x21,2=0x22,3=0x23}", ++ <&pimidi_ctrl>,"data-ready-gpios:4{0=23,1=5,2=6,3=27}", ++ <&pimidi_ctrl>,"interrupts:0{0=23,1=5,2=6,3=27}"; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-6.6/950-1411-staging-vchiq_arm-Add-36-bit-address-support.patch b/target/linux/bcm27xx/patches-6.6/950-1411-staging-vchiq_arm-Add-36-bit-address-support.patch new file mode 100644 index 0000000000..871b2769b1 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1411-staging-vchiq_arm-Add-36-bit-address-support.patch @@ -0,0 +1,256 @@ +From a1e4b72997dc3ef423b6f510bfead470475750d4 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 1 Nov 2018 17:31:37 +0000 +Subject: [PATCH] staging: vchiq_arm: 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), giving a 36-bit address range. + +Manage the split between addresses for the VPU and addresses for the +40-bit DMA controller with a dedicated DMA device pointer that on non- +BCM2711 platforms is the same as the main VCHIQ device. This allows +the VCHIQ node to stay in the usual place in the DT. + +Signed-off-by: Phil Elwell +--- + .../interface/vchiq_arm/vchiq_arm.c | 125 +++++++++++++----- + 1 file changed, 90 insertions(+), 35 deletions(-) + +--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c ++++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +@@ -73,6 +73,7 @@ static struct platform_device *bcm2835_i + + struct vchiq_drvdata { + const unsigned int cache_line_size; ++ const bool use_36bit_addrs; + struct rpi_firmware *fw; + }; + +@@ -118,6 +119,11 @@ struct vchiq_arm_state { + int first_connect; + }; + ++static struct vchiq_drvdata bcm2711_drvdata = { ++ .cache_line_size = 64, ++ .use_36bit_addrs = true, ++}; ++ + struct vchiq_pagelist_info { + struct pagelist *pagelist; + size_t pagelist_buffer_size; +@@ -142,10 +148,12 @@ static void __iomem *g_regs; + * of 32. + */ + static unsigned int g_cache_line_size = 32; ++static unsigned int g_use_36bit_addrs = 0; + static unsigned int g_fragments_size; + static char *g_fragments_base; + static char *g_free_fragments; + static struct semaphore g_free_fragments_sema; ++static struct device *g_dma_dev; + + static DEFINE_SEMAPHORE(g_free_fragments_mutex, 1); + +@@ -175,7 +183,7 @@ static void + cleanup_pagelistinfo(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagelistinfo) + { + if (pagelistinfo->scatterlist_mapped) { +- dma_unmap_sg(instance->state->dev, pagelistinfo->scatterlist, ++ dma_unmap_sg(g_dma_dev, pagelistinfo->scatterlist, + pagelistinfo->num_pages, pagelistinfo->dma_dir); + } + +@@ -335,7 +343,7 @@ create_pagelist(struct vchiq_instance *i + count -= len; + } + +- dma_buffers = dma_map_sg(instance->state->dev, ++ dma_buffers = dma_map_sg(g_dma_dev, + scatterlist, + num_pages, + pagelistinfo->dma_dir); +@@ -349,22 +357,61 @@ create_pagelist(struct vchiq_instance *i + + /* 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 (is_adjacent_block(addrs, addr, k)) +- 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 = 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 */ +@@ -408,7 +455,7 @@ free_pagelist(struct vchiq_instance *ins + * NOTE: dma_unmap_sg must be called before the + * cpu can touch any of the data/pages. + */ +- dma_unmap_sg(instance->state->dev, pagelistinfo->scatterlist, ++ dma_unmap_sg(g_dma_dev, pagelistinfo->scatterlist, + pagelistinfo->num_pages, pagelistinfo->dma_dir); + pagelistinfo->scatterlist_mapped = 0; + +@@ -463,6 +510,7 @@ free_pagelist(struct vchiq_instance *ins + static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) + { + struct device *dev = &pdev->dev; ++ struct device *dma_dev = NULL; + struct vchiq_drvdata *drvdata = platform_get_drvdata(pdev); + struct rpi_firmware *fw = drvdata->fw; + struct vchiq_slot_zero *vchiq_slot_zero; +@@ -484,6 +532,24 @@ static int vchiq_platform_init(struct pl + g_cache_line_size = drvdata->cache_line_size; + g_fragments_size = 2 * g_cache_line_size; + ++ if (drvdata->use_36bit_addrs) { ++ struct device_node *dma_node = ++ of_find_compatible_node(NULL, NULL, "brcm,bcm2711-dma"); ++ ++ if (dma_node) { ++ struct platform_device *pdev; ++ ++ pdev = of_find_device_by_node(dma_node); ++ if (pdev) ++ dma_dev = &pdev->dev; ++ of_node_put(dma_node); ++ g_use_36bit_addrs = true; ++ } else { ++ dev_err(dev, "40-bit DMA controller not found\n"); ++ return -EINVAL; ++ } ++ } ++ + /* 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); +@@ -496,13 +562,14 @@ static int vchiq_platform_init(struct pl + } + + WARN_ON(((unsigned long)slot_mem & (PAGE_SIZE - 1)) != 0); ++ channelbase = slot_phys; + + vchiq_slot_zero = vchiq_init_slots(slot_mem, slot_mem_size); + if (!vchiq_slot_zero) + return -ENOMEM; + + 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; + +@@ -536,7 +603,6 @@ static int vchiq_platform_init(struct pl + } + + /* 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) { +@@ -550,6 +616,8 @@ static int vchiq_platform_init(struct pl + return -ENXIO; + } + ++ g_dma_dev = dma_dev ?: dev; ++ + vchiq_log_info(vchiq_arm_log_level, "vchiq_init - done (slots %pK, phys %pad)", + vchiq_slot_zero, &slot_phys); + +@@ -1755,6 +1823,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,bcm2711-vchiq", .data = &bcm2711_drvdata }, + {}, + }; + MODULE_DEVICE_TABLE(of, vchiq_of_match); +@@ -1787,22 +1856,8 @@ vchiq_register_child(struct platform_dev + + child->dev.of_node = np; + +- /* +- * We want the dma-ranges etc to be copied from a device with the +- * correct dma-ranges for the VPU. +- * VCHIQ on Pi4 is now under scb which doesn't get those dma-ranges. +- * Take the "dma" node as going to be suitable as it sees the world +- * through the same eyes as the VPU. +- */ +- np = of_find_node_by_path("dma"); +- if (!np) +- np = pdev->dev.of_node; +- + of_dma_configure(&child->dev, np, true); + +- if (np != pdev->dev.of_node) +- of_node_put(np); +- + return child; + } + diff --git a/target/linux/bcm27xx/patches-6.6/950-1412-staging-vchiq_arm-children-inherit-DMA-config.patch b/target/linux/bcm27xx/patches-6.6/950-1412-staging-vchiq_arm-children-inherit-DMA-config.patch new file mode 100644 index 0000000000..df93f3a0c4 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1412-staging-vchiq_arm-children-inherit-DMA-config.patch @@ -0,0 +1,36 @@ +From 1129091b2d95273d930acf2926a569b90512a248 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 21 Jul 2020 17:34:09 +0100 +Subject: [PATCH] staging: vchiq_arm: children inherit DMA config + +Although it is no longer necessary for vchiq's children to have a +different DMA configuration to the parent, they do still need to +explicitly to have their DMA configuration set - to be that of the +parent. + +Signed-off-by: Phil Elwell +--- + .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c ++++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +@@ -1856,8 +1856,18 @@ vchiq_register_child(struct platform_dev + + child->dev.of_node = np; + ++ /* ++ * We want the dma-ranges etc to be copied from the parent VCHIQ device ++ * to be passed on to the children without a node of their own. ++ */ ++ if (!np) ++ np = pdev->dev.of_node; ++ + of_dma_configure(&child->dev, np, true); + ++ if (np != pdev->dev.of_node) ++ of_node_put(np); ++ + return child; + } + diff --git a/target/linux/bcm27xx/patches-6.6/950-1413-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch b/target/linux/bcm27xx/patches-6.6/950-1413-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch new file mode 100644 index 0000000000..7120123ecf --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1413-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch @@ -0,0 +1,118 @@ +From 2d26a598ceceaea8a6837146c741eb742bbd4baa Mon Sep 17 00:00:00 2001 +From: detule +Date: Tue, 2 Oct 2018 04:10:08 -0400 +Subject: [PATCH] staging: vchiq_arm: Usa a DMA pool for small bulks + +During a bulk transfer we request a DMA allocation to hold the +scatter-gather list. Most of the time, this allocation is small +(<< PAGE_SIZE), however it can be requested at a high enough frequency +to cause fragmentation and/or stress the CMA allocator (think time +spent in compaction here, or during allocations elsewhere). + +Implement a pool to serve up small DMA allocations, falling back +to a coherent allocation if the request is greater than +VCHIQ_DMA_POOL_SIZE. + +Signed-off-by: Oliver Gjoneski +--- + .../interface/vchiq_arm/vchiq_arm.c | 33 ++++++++++++++++--- + 1 file changed, 29 insertions(+), 4 deletions(-) + +--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c ++++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -51,6 +52,8 @@ + + #define ARM_DS_ACTIVE BIT(2) + ++#define VCHIQ_DMA_POOL_SIZE PAGE_SIZE ++ + /* Override the default prefix, which would be vchiq_arm (from the filename) */ + #undef MODULE_PARAM_PREFIX + #define MODULE_PARAM_PREFIX DEVICE_NAME "." +@@ -128,6 +131,7 @@ struct vchiq_pagelist_info { + struct pagelist *pagelist; + size_t pagelist_buffer_size; + dma_addr_t dma_addr; ++ bool is_from_pool; + enum dma_data_direction dma_dir; + unsigned int num_pages; + unsigned int pages_need_release; +@@ -148,6 +152,7 @@ static void __iomem *g_regs; + * of 32. + */ + 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; +@@ -190,8 +195,13 @@ cleanup_pagelistinfo(struct vchiq_instan + if (pagelistinfo->pages_need_release) + unpin_user_pages(pagelistinfo->pages, pagelistinfo->num_pages); + +- dma_free_coherent(instance->state->dev, pagelistinfo->pagelist_buffer_size, +- pagelistinfo->pagelist, pagelistinfo->dma_addr); ++ if (pagelistinfo->is_from_pool) { ++ dma_pool_free(g_dma_pool, pagelistinfo->pagelist, ++ pagelistinfo->dma_addr); ++ } else { ++ dma_free_coherent(instance->state->dev, pagelistinfo->pagelist_buffer_size, ++ pagelistinfo->pagelist, pagelistinfo->dma_addr); ++ } + } + + static inline bool +@@ -226,6 +236,7 @@ create_pagelist(struct vchiq_instance *i + u32 *addrs; + unsigned int num_pages, offset, i, k; + int actual_pages; ++ bool is_from_pool; + size_t pagelist_size; + struct scatterlist *scatterlist, *sg; + int dma_buffers; +@@ -255,8 +266,14 @@ create_pagelist(struct vchiq_instance *i + /* Allocate enough storage to hold the page pointers and the page + * list + */ +- pagelist = dma_alloc_coherent(instance->state->dev, pagelist_size, &dma_addr, +- GFP_KERNEL); ++ if (pagelist_size > VCHIQ_DMA_POOL_SIZE) { ++ pagelist = dma_alloc_coherent(instance->state->dev, pagelist_size, &dma_addr, ++ GFP_KERNEL); ++ is_from_pool = false; ++ } else { ++ pagelist = dma_pool_alloc(g_dma_pool, GFP_KERNEL, &dma_addr); ++ is_from_pool = true; ++ } + + vchiq_log_trace(vchiq_arm_log_level, "%s - %pK", __func__, pagelist); + +@@ -277,6 +294,7 @@ create_pagelist(struct vchiq_instance *i + pagelistinfo->pagelist = pagelist; + pagelistinfo->pagelist_buffer_size = pagelist_size; + pagelistinfo->dma_addr = dma_addr; ++ pagelistinfo->is_from_pool = is_from_pool; + pagelistinfo->dma_dir = (type == PAGELIST_WRITE) ? + DMA_TO_DEVICE : DMA_FROM_DEVICE; + pagelistinfo->num_pages = num_pages; +@@ -617,6 +635,13 @@ static int vchiq_platform_init(struct pl + } + + g_dma_dev = dma_dev ?: dev; ++ g_dma_pool = dmam_pool_create("vchiq_scatter_pool", dev, ++ VCHIQ_DMA_POOL_SIZE, g_cache_line_size, ++ 0); ++ if (!g_dma_pool) { ++ dev_err(dev, "failed to create dma pool"); ++ return -ENOMEM; ++ } + + vchiq_log_info(vchiq_arm_log_level, "vchiq_init - done (slots %pK, phys %pad)", + vchiq_slot_zero, &slot_phys); diff --git a/target/linux/bcm27xx/patches-6.6/950-1414-staging-vchiq_arm-Add-log_level-module-params.patch b/target/linux/bcm27xx/patches-6.6/950-1414-staging-vchiq_arm-Add-log_level-module-params.patch new file mode 100644 index 0000000000..d85765bc32 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1414-staging-vchiq_arm-Add-log_level-module-params.patch @@ -0,0 +1,27 @@ +From 5b29221e96d1ba60a78d5c804a20fa35a6d0517a Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 29 Apr 2022 09:19:10 +0100 +Subject: [PATCH] staging: vchiq_arm: Add log_level module params + +Add module parameters to control the logging levels for the various +vchiq logging categories. + +Signed-off-by: Phil Elwell +--- + .../staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c ++++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +@@ -64,6 +64,11 @@ + /* Run time control of log level, based on KERN_XXX level. */ + int vchiq_arm_log_level = VCHIQ_LOG_DEFAULT; + int vchiq_susp_log_level = VCHIQ_LOG_ERROR; ++module_param_named(arm_log_level, vchiq_arm_log_level, int, 0644); ++module_param_named(susp_log_level, vchiq_susp_log_level, int, 0644); ++module_param_named(core_log_level, vchiq_core_log_level, int, 0644); ++module_param_named(core_msg_log_level, vchiq_core_msg_log_level, int, 0644); ++module_param_named(sync_log_level, vchiq_sync_log_level, int, 0644); + + DEFINE_SPINLOCK(msg_queue_spinlock); + struct vchiq_state g_state; diff --git a/target/linux/bcm27xx/patches-6.6/950-1415-media-i2c-imx477-Fix-link-frequency-menu.patch b/target/linux/bcm27xx/patches-6.6/950-1415-media-i2c-imx477-Fix-link-frequency-menu.patch new file mode 100644 index 0000000000..9b5b0e47d6 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1415-media-i2c-imx477-Fix-link-frequency-menu.patch @@ -0,0 +1,25 @@ +From 8691544f688bd3ae9b6db0845a75ce230fc9e90f Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Thu, 21 Nov 2024 15:54:58 +0000 +Subject: [PATCH] media: i2c: imx477: Fix link frequency menu + +"media: i2c: imx477: Add options for slightly modifying the link freq" +created a link frequency menu with 2 items in instead of one. +Correct this. + +Signed-off-by: Dave Stevenson +--- + drivers/media/i2c/imx477.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/media/i2c/imx477.c ++++ b/drivers/media/i2c/imx477.c +@@ -2051,7 +2051,7 @@ static int imx477_init_controls(struct i + /* LINK_FREQ is also read only */ + imx477->link_freq = + v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx477_ctrl_ops, +- V4L2_CID_LINK_FREQ, 1, 0, ++ V4L2_CID_LINK_FREQ, 0, 0, + &link_freqs[imx477->link_freq_idx]); + if (imx477->link_freq) + imx477->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; diff --git a/target/linux/bcm27xx/patches-6.6/950-1416-misc-rp1-pio-Fix-copy-paste-error-in-pio_rp1.h.patch b/target/linux/bcm27xx/patches-6.6/950-1416-misc-rp1-pio-Fix-copy-paste-error-in-pio_rp1.h.patch new file mode 100644 index 0000000000..cf13be0a04 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1416-misc-rp1-pio-Fix-copy-paste-error-in-pio_rp1.h.patch @@ -0,0 +1,25 @@ +From 99a0201bb0abc946dc431214b638b2cc6b01dda5 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 25 Nov 2024 16:19:55 +0000 +Subject: [PATCH] misc/rp1-pio: Fix copy/paste error in pio_rp1.h + +As per the subject, there was a copy/paste error that caused +pio_sm_unclaim from a driver to result in a call to +pio_sm_claim. Fix it. + +Signed-off-by: Phil Elwell +--- + include/linux/pio_rp1.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/pio_rp1.h ++++ b/include/linux/pio_rp1.h +@@ -320,7 +320,7 @@ static inline int pio_sm_unclaim(struct + if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) + return -EINVAL; + +- return rp1_pio_sm_claim(client, &args); ++ return rp1_pio_sm_unclaim(client, &args); + } + + static inline int pio_claim_unused_sm(struct rp1_pio_client *client, bool required) diff --git a/target/linux/bcm27xx/patches-6.6/950-1417-misc-rp1-pio-Fix-parameter-checks-wihout-client.patch b/target/linux/bcm27xx/patches-6.6/950-1417-misc-rp1-pio-Fix-parameter-checks-wihout-client.patch new file mode 100644 index 0000000000..e68bd175d8 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1417-misc-rp1-pio-Fix-parameter-checks-wihout-client.patch @@ -0,0 +1,25 @@ +From 3687701e8d252864f440f91f1aedf8ffd58d6ee6 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 25 Nov 2024 21:51:13 +0000 +Subject: [PATCH] misc: rp1-pio: Fix parameter checks wihout client + +Passing bad parameters to an API call without a pio pointer will cause +a NULL pointer exception when the persistent error is set. Guard +against that. + +Signed-off-by: Phil Elwell +--- + include/linux/pio_rp1.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/pio_rp1.h ++++ b/include/linux/pio_rp1.h +@@ -20,7 +20,7 @@ + #endif + + #define bad_params_if(client, test) \ +- ({ bool f = (test); if (f) pio_set_error(client, -EINVAL); \ ++ ({ bool f = (test); if (f && client) pio_set_error(client, -EINVAL); \ + if (f && PARAM_WARNINGS_ENABLED) WARN_ON((test)); \ + f; }) + diff --git a/target/linux/bcm27xx/patches-6.6/950-1418-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch b/target/linux/bcm27xx/patches-6.6/950-1418-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch new file mode 100644 index 0000000000..5e50af70df --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1418-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch @@ -0,0 +1,147 @@ +From 008c93b47b9b965368eb5bbfbef60b816931e0ab Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 20 Nov 2024 13:58:08 +0000 +Subject: [PATCH] drm: vc4: dsi: Handle the different command FIFO widths + +DSI0 and DSI1 have different widths for the command FIFO (24bit +vs 32bit), but the driver was assuming the 32bit width of DSI1 +in all cases. +DSI0 also wants the data packed as 24bit big endian, so the +formatting code needs updating. + +Handle the difference via the variant structure. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/vc4/vc4_dsi.c | 64 ++++++++++++++++++++++++----------- + 1 file changed, 44 insertions(+), 20 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_dsi.c ++++ b/drivers/gpu/drm/vc4/vc4_dsi.c +@@ -44,7 +44,6 @@ + + #define DSI_CMD_FIFO_DEPTH 16 + #define DSI_PIX_FIFO_DEPTH 256 +-#define DSI_PIX_FIFO_WIDTH 4 + + #define DSI0_CTRL 0x00 + +@@ -170,11 +169,15 @@ + #define DSI1_DISP1_CTRL 0x2c + /* Format of the data written to TXPKT_PIX_FIFO. */ + # define DSI_DISP1_PFORMAT_MASK VC4_MASK(2, 1) +-# define DSI_DISP1_PFORMAT_SHIFT 1 +-# define DSI_DISP1_PFORMAT_16BIT 0 +-# define DSI_DISP1_PFORMAT_24BIT 1 +-# define DSI_DISP1_PFORMAT_32BIT_LE 2 +-# define DSI_DISP1_PFORMAT_32BIT_BE 3 ++# define DSI1_DISP1_PFORMAT_SHIFT 1 ++# define DSI0_DISP1_PFORMAT_16BIT 0 ++# define DSI0_DISP1_PFORMAT_16BIT_ADJ 1 ++# define DSI0_DISP1_PFORMAT_24BIT 2 ++# define DSI0_DISP1_PFORMAT_32BIT_LE 3 /* NB Invalid, but required for macros to work */ ++# define DSI1_DISP1_PFORMAT_16BIT 0 ++# define DSI1_DISP1_PFORMAT_24BIT 1 ++# define DSI1_DISP1_PFORMAT_32BIT_LE 2 ++# define DSI1_DISP1_PFORMAT_32BIT_BE 3 + + /* DISP1 is always command mode. */ + # define DSI_DISP1_ENABLE BIT(0) +@@ -553,6 +556,7 @@ struct vc4_dsi_variant { + unsigned int port; + + bool broken_axi_workaround; ++ unsigned int cmd_fifo_width; + + const char *debugfs_name; + const struct debugfs_reg32 *regs; +@@ -1151,10 +1155,16 @@ static void vc4_dsi_bridge_pre_enable(st + /* Set up DISP1 for transferring long command payloads through + * the pixfifo. + */ +- DSI_PORT_WRITE(DISP1_CTRL, +- VC4_SET_FIELD(DSI_DISP1_PFORMAT_32BIT_LE, +- DSI_DISP1_PFORMAT) | +- DSI_DISP1_ENABLE); ++ if (dsi->variant->cmd_fifo_width == 4) ++ DSI_PORT_WRITE(DISP1_CTRL, ++ VC4_SET_FIELD(DSI_PORT_BIT(DISP1_PFORMAT_32BIT_LE), ++ DSI_DISP1_PFORMAT) | ++ DSI_DISP1_ENABLE); ++ else ++ DSI_PORT_WRITE(DISP1_CTRL, ++ VC4_SET_FIELD(DSI_PORT_BIT(DISP1_PFORMAT_24BIT), ++ DSI_DISP1_PFORMAT) | ++ DSI_DISP1_ENABLE); + + /* Bring AFE out of reset. */ + DSI_PORT_WRITE(PHY_AFEC0, +@@ -1235,9 +1245,9 @@ static ssize_t vc4_dsi_transfer(struct v + pix_fifo_len = 0; + } else { + cmd_fifo_len = (packet.payload_length % +- DSI_PIX_FIFO_WIDTH); ++ dsi->variant->cmd_fifo_width); + pix_fifo_len = ((packet.payload_length - cmd_fifo_len) / +- DSI_PIX_FIFO_WIDTH); ++ dsi->variant->cmd_fifo_width); + } + + WARN_ON_ONCE(pix_fifo_len >= DSI_PIX_FIFO_DEPTH); +@@ -1255,14 +1265,25 @@ static ssize_t vc4_dsi_transfer(struct v + + for (i = 0; i < cmd_fifo_len; i++) + DSI_PORT_WRITE(TXPKT_CMD_FIFO, packet.payload[i]); +- for (i = 0; i < pix_fifo_len; i++) { +- const u8 *pix = packet.payload + cmd_fifo_len + i * 4; ++ if (dsi->variant->cmd_fifo_width == 4) { ++ for (i = 0; i < pix_fifo_len; i++) { ++ const u8 *pix = packet.payload + cmd_fifo_len + i * 4; ++ ++ DSI_PORT_WRITE(TXPKT_PIX_FIFO, ++ pix[0] | ++ pix[1] << 8 | ++ pix[2] << 16 | ++ pix[3] << 24); ++ } ++ } else { ++ for (i = 0; i < pix_fifo_len; i++) { ++ const u8 *pix = packet.payload + cmd_fifo_len + i * 3; + +- DSI_PORT_WRITE(TXPKT_PIX_FIFO, +- pix[0] | +- pix[1] << 8 | +- pix[2] << 16 | +- pix[3] << 24); ++ DSI_PORT_WRITE(TXPKT_PIX_FIFO, ++ pix[2] | ++ pix[1] << 8 | ++ pix[0] << 16); ++ } + } + + if (msg->flags & MIPI_DSI_MSG_USE_LPM) +@@ -1516,6 +1537,7 @@ static const struct drm_encoder_funcs vc + + static const struct vc4_dsi_variant bcm2711_dsi1_variant = { + .port = 1, ++ .cmd_fifo_width = 4, + .debugfs_name = "dsi1_regs", + .regs = dsi1_regs, + .nregs = ARRAY_SIZE(dsi1_regs), +@@ -1523,6 +1545,7 @@ static const struct vc4_dsi_variant bcm2 + + static const struct vc4_dsi_variant bcm2835_dsi0_variant = { + .port = 0, ++ .cmd_fifo_width = 3, + .debugfs_name = "dsi0_regs", + .regs = dsi0_regs, + .nregs = ARRAY_SIZE(dsi0_regs), +@@ -1530,6 +1553,7 @@ static const struct vc4_dsi_variant bcm2 + + static const struct vc4_dsi_variant bcm2835_dsi1_variant = { + .port = 1, ++ .cmd_fifo_width = 4, + .broken_axi_workaround = true, + .debugfs_name = "dsi1_regs", + .regs = dsi1_regs, diff --git a/target/linux/bcm27xx/patches-6.6/950-1419-dts-bcm2712-rpi-For-CM5IO-i2c_csi_dsi-needs-to-be-CA.patch b/target/linux/bcm27xx/patches-6.6/950-1419-dts-bcm2712-rpi-For-CM5IO-i2c_csi_dsi-needs-to-be-CA.patch new file mode 100644 index 0000000000..0aab1043b5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1419-dts-bcm2712-rpi-For-CM5IO-i2c_csi_dsi-needs-to-be-CA.patch @@ -0,0 +1,30 @@ +From eafaa6015fc0ed676f6115905e7c4145d23f5b7d Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 26 Nov 2024 15:53:24 +0000 +Subject: [PATCH] dts: bcm2712-rpi: For CM5IO, i2c_csi_dsi needs to be + CAM/DISP1 + +Noted setting up a display on CM5IO. Add +"dtoverlay=vc4-kms-dsi-ili7881-7inch" fails as it tries to +find the regulator/backlight/touch on i2c_csi_dsi, which pointed +at i2c_csi_dsi0 by default. + +Adding the dsi0 override updated to point at dsi0, and pointed +the i2c at i2c_csi_dsi0, which all works. + +The default with i2c_csi_dsi needs to be consistent in using +dsi1/csi1 and the corresponding i2c interface (i2c_csi_dsi1). + +Signed-off-by: Dave Stevenson +--- + arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi +@@ -11,4 +11,4 @@ i2c_csi_dsi0: &i2c6 { // Note: This is f + symlink = "i2c-6"; + }; + +-i2c_csi_dsi: &i2c_csi_dsi0 { }; // The connector that needs no jumper to enable ++i2c_csi_dsi: &i2c_csi_dsi1 { }; // The connector that needs no jumper to enable diff --git a/target/linux/bcm27xx/patches-6.6/950-1420-dts-bcm2712-rpi-cm5-Remove-inaccessible-USB_OC_N.patch b/target/linux/bcm27xx/patches-6.6/950-1420-dts-bcm2712-rpi-cm5-Remove-inaccessible-USB_OC_N.patch new file mode 100644 index 0000000000..e2a01216a3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1420-dts-bcm2712-rpi-cm5-Remove-inaccessible-USB_OC_N.patch @@ -0,0 +1,25 @@ +From d128c123754e9dd03ad72c16851a1652331d6da1 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 27 Nov 2024 10:24:47 +0000 +Subject: [PATCH] dts: bcm2712-rpi-cm5: Remove inaccessible USB_OC_N + +Although VBUS_EN on GPIO42 appears on the CM5's 100-way headers, +USB_OC_N on GPIO43 does not. Remove the signal name to avoid further +confusion and disappointment. + +Signed-off-by: Phil Elwell +--- + arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi +@@ -718,7 +718,7 @@ spi10_cs_pins: &spi10_cs_gpio1 {}; + "-", // GPIO40 + "-", // GPIO41 + "USB_VBUS_EN", // GPIO42 +- "USB_OC_N", // GPIO43 ++ "-", // GPIO43 + "RP1_STAT_LED", // GPIO44 + "FAN_PWM", // GPIO45 + "-", // GPIO46 diff --git a/target/linux/bcm27xx/patches-6.6/950-1421-overlays-qca7000-replace-URL-with-textual-hint.patch b/target/linux/bcm27xx/patches-6.6/950-1421-overlays-qca7000-replace-URL-with-textual-hint.patch new file mode 100644 index 0000000000..8cdb2504a1 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1421-overlays-qca7000-replace-URL-with-textual-hint.patch @@ -0,0 +1,33 @@ +From 77389e715039b1feac9c6261727600892cc12fdb Mon Sep 17 00:00:00 2001 +From: Michael Heimpold +Date: Fri, 29 Nov 2024 14:10:04 +0100 +Subject: [PATCH] overlays: qca7000: replace URL with textual hint + +The deep link into the website is not that stable, so let's +replace it with a textual description where to find the +product information. + +Signed-off-by: Michael Heimpold +--- + arch/arm/boot/dts/overlays/qca7000-overlay.dts | 2 +- + arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/arch/arm/boot/dts/overlays/qca7000-overlay.dts ++++ b/arch/arm/boot/dts/overlays/qca7000-overlay.dts +@@ -1,5 +1,5 @@ + // Overlay for the Qualcomm Atheros QCA7000 on PLC Stamp micro EVK +-// Visit: https://chargebyte.com/products/evaluation-tools/plc-stamp-micro-2-evaluation-board for details ++// Visit: https://chargebyte.com -> Controllers & Modules -> Evaluation Tools -> PLC Stamp Micro 2 Evaluation Board for details + + /dts-v1/; + /plugin/; +--- a/arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts ++++ b/arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts +@@ -1,5 +1,5 @@ + // Overlay for the Qualcomm Atheros QCA7000 on PLC Stamp micro EVK +-// Visit: https://in-tech-smartcharging.com/products/evaluation-tools/plc-stamp-micro-2-evaluation-board for details ++// Visit: https://chargebyte.com -> Controllers & Modules -> Evaluation Tools -> PLC Stamp Micro 2 Evaluation Board for details + + /dts-v1/; + /plugin/; diff --git a/target/linux/bcm27xx/patches-6.6/950-1422-dt-bindings-net-cdns-macb-Add-compatible-for-Raspber.patch b/target/linux/bcm27xx/patches-6.6/950-1422-dt-bindings-net-cdns-macb-Add-compatible-for-Raspber.patch new file mode 100644 index 0000000000..1364b4f713 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1422-dt-bindings-net-cdns-macb-Add-compatible-for-Raspber.patch @@ -0,0 +1,24 @@ +From 178f1c2747c3920723242f26ba290785d45bffae Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 11 Nov 2024 16:38:01 +0000 +Subject: [PATCH] dt-bindings: net: cdns,macb: Add compatible for Raspberry Pi + RP1 + +The Raspberry Pi RP1 chip has the Cadence GEM ethernet +controller, so add a compatible string for it. + +Signed-off-by: Dave Stevenson +--- + Documentation/devicetree/bindings/net/cdns,macb.yaml | 1 + + 1 file changed, 1 insertion(+) + +--- a/Documentation/devicetree/bindings/net/cdns,macb.yaml ++++ b/Documentation/devicetree/bindings/net/cdns,macb.yaml +@@ -54,6 +54,7 @@ properties: + - cdns,np4-macb # NP4 SoC devices + - microchip,sama7g5-emac # Microchip SAMA7G5 ethernet interface + - microchip,sama7g5-gem # Microchip SAMA7G5 gigabit ethernet interface ++ - raspberrypi,rp1-gem # Raspberry Pi RP1 gigabit ethernet interface + - sifive,fu540-c000-gem # SiFive FU540-C000 SoC + - cdns,emac # Generic + - cdns,gem # Generic diff --git a/target/linux/bcm27xx/patches-6.6/950-1423-net-macb-Add-support-for-Raspberry-Pi-RP1-ethernet-c.patch b/target/linux/bcm27xx/patches-6.6/950-1423-net-macb-Add-support-for-Raspberry-Pi-RP1-ethernet-c.patch index 3941d69edf..6a9b7b36bd 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1423-net-macb-Add-support-for-Raspberry-Pi-RP1-ethernet-c.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1423-net-macb-Add-support-for-Raspberry-Pi-RP1-ethernet-c.patch @@ -1,8 +1,8 @@ From f9f0024bd9bf04a58b64bae356be4c04022d23bc Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 11 Nov 2024 16:40:07 +0000 -Subject: [PATCH 1423/1482] net: macb: Add support for Raspberry Pi RP1 - ethernet controller +Subject: [PATCH] net: macb: Add support for Raspberry Pi RP1 ethernet + controller The RP1 chip has the Cadence GEM block, but wants the tx_clock to always run at 125MHz, in the same way as sama7g5. diff --git a/target/linux/bcm27xx/patches-6.6/950-1424-rp1-clk-Only-set-PLL_SEC_RST-in-rp1_pll_divider_off.patch b/target/linux/bcm27xx/patches-6.6/950-1424-rp1-clk-Only-set-PLL_SEC_RST-in-rp1_pll_divider_off.patch index d3f216d31d..91d836f71c 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1424-rp1-clk-Only-set-PLL_SEC_RST-in-rp1_pll_divider_off.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1424-rp1-clk-Only-set-PLL_SEC_RST-in-rp1_pll_divider_off.patch @@ -1,8 +1,7 @@ From 33c225f622d596034a9261316666089a92aa6834 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 25 Nov 2024 12:30:06 +0000 -Subject: [PATCH 1424/1482] rp1: clk: Only set PLL_SEC_RST in - rp1_pll_divider_off +Subject: [PATCH] rp1: clk: Only set PLL_SEC_RST in rp1_pll_divider_off Rather than clearing all the bits in rp1_pll_divider_off and setting PLL_SEC_RST, retain the status of all the other diff --git a/target/linux/bcm27xx/patches-6.6/950-1425-rp1-clk-Rationalise-the-use-of-the-CLK_IS_CRITICAL-f.patch b/target/linux/bcm27xx/patches-6.6/950-1425-rp1-clk-Rationalise-the-use-of-the-CLK_IS_CRITICAL-f.patch index 247ae455ac..3f4850c899 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1425-rp1-clk-Rationalise-the-use-of-the-CLK_IS_CRITICAL-f.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1425-rp1-clk-Rationalise-the-use-of-the-CLK_IS_CRITICAL-f.patch @@ -1,8 +1,7 @@ From eb836a6a299322a8e2b9627cccd23c7a76d068ba Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Fri, 8 Nov 2024 17:36:13 +0000 -Subject: [PATCH 1425/1482] rp1: clk: Rationalise the use of the - CLK_IS_CRITICAL flag +Subject: [PATCH] rp1: clk: Rationalise the use of the CLK_IS_CRITICAL flag The clock setup had been copied from clk-bcm2835 which had to cope with the firmware having configured clocks, so there were flags diff --git a/target/linux/bcm27xx/patches-6.6/950-1426-dt-arm64-Fixup-RP1-ethernet-DT-configuration.patch b/target/linux/bcm27xx/patches-6.6/950-1426-dt-arm64-Fixup-RP1-ethernet-DT-configuration.patch index ad6569e99c..f213d8def6 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1426-dt-arm64-Fixup-RP1-ethernet-DT-configuration.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1426-dt-arm64-Fixup-RP1-ethernet-DT-configuration.patch @@ -1,7 +1,7 @@ From 0b4af929b7125abd3a262577b380c7c81ee9b1c5 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 11 Nov 2024 15:18:14 +0000 -Subject: [PATCH 1426/1482] dt: arm64: Fixup RP1 ethernet DT configuration +Subject: [PATCH] dt: arm64: Fixup RP1 ethernet DT configuration Configure RP1's ethernet block to do the correct thing. clk_eth is intended to be fixed at 125MHz, so use a new compatible, @@ -14,7 +14,7 @@ Signed-off-by: Dave Stevenson --- a/arch/arm64/boot/dts/broadcom/rp1.dtsi +++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi -@@ -24,6 +24,7 @@ +@@ -32,6 +32,7 @@ // RP1_PLL_VIDEO_CORE and dividers are now managed by VEC,DPI drivers <&rp1_clocks RP1_PLL_SYS>, <&rp1_clocks RP1_PLL_SYS_SEC>, @@ -22,7 +22,7 @@ Signed-off-by: Dave Stevenson <&rp1_clocks RP1_PLL_AUDIO>, <&rp1_clocks RP1_PLL_AUDIO_SEC>, <&rp1_clocks RP1_CLK_SYS>, -@@ -38,6 +39,7 @@ +@@ -46,6 +47,7 @@ <1536000000>, // RP1_PLL_AUDIO_CORE <200000000>, // RP1_PLL_SYS <125000000>, // RP1_PLL_SYS_SEC @@ -30,7 +30,7 @@ Signed-off-by: Dave Stevenson <61440000>, // RP1_PLL_AUDIO <192000000>, // RP1_PLL_AUDIO_SEC <200000000>, // RP1_CLK_SYS -@@ -968,12 +970,14 @@ +@@ -976,12 +978,14 @@ rp1_eth: ethernet@100000 { reg = <0xc0 0x40100000 0x0 0x4000>; diff --git a/target/linux/bcm27xx/patches-6.6/950-1427-clk-rp1-Add-RP1_CLK_DMA.patch b/target/linux/bcm27xx/patches-6.6/950-1427-clk-rp1-Add-RP1_CLK_DMA.patch index 9026cebaf9..b13becd187 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1427-clk-rp1-Add-RP1_CLK_DMA.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1427-clk-rp1-Add-RP1_CLK_DMA.patch @@ -1,7 +1,7 @@ From d4e41ed9954fa86c4774f98d393aa401c81a68e7 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 13 Nov 2024 13:10:27 +0000 -Subject: [PATCH 1427/1482] clk: rp1: Add RP1_CLK_DMA. +Subject: [PATCH] clk: rp1: Add RP1_CLK_DMA. The DMA block has a clock, but wasn't defined in the driver. This resulted in the parent being disabled as unused, and then DMA diff --git a/target/linux/bcm27xx/patches-6.6/950-1428-rp1-clk-Remove-CLK_IGNORE_UNUSED-flags.patch b/target/linux/bcm27xx/patches-6.6/950-1428-rp1-clk-Remove-CLK_IGNORE_UNUSED-flags.patch index 8fa06045a5..662a2485dd 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1428-rp1-clk-Remove-CLK_IGNORE_UNUSED-flags.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1428-rp1-clk-Remove-CLK_IGNORE_UNUSED-flags.patch @@ -1,7 +1,7 @@ From 9049e4df2c54b5e620f855f66db3a18c9f2e181f Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Fri, 8 Nov 2024 17:37:08 +0000 -Subject: [PATCH 1428/1482] rp1: clk: Remove CLK_IGNORE_UNUSED flags +Subject: [PATCH] rp1: clk: Remove CLK_IGNORE_UNUSED flags There should be no issue in disabling the RP1 clocks as long as the kernel knows about all consumers. diff --git a/target/linux/bcm27xx/patches-6.6/950-1429-dt-rp1-Use-clk_sys-for-ethernet-hclk-and-pclk.patch b/target/linux/bcm27xx/patches-6.6/950-1429-dt-rp1-Use-clk_sys-for-ethernet-hclk-and-pclk.patch index 6d0eb13389..937b125717 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1429-dt-rp1-Use-clk_sys-for-ethernet-hclk-and-pclk.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1429-dt-rp1-Use-clk_sys-for-ethernet-hclk-and-pclk.patch @@ -1,7 +1,7 @@ From 542d0f7f2e9f90fc0f02f8cb141f7c3fbf46081b Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 11 Nov 2024 17:11:18 +0000 -Subject: [PATCH 1429/1482] dt: rp1: Use clk_sys for ethernet hclk and pclk +Subject: [PATCH] dt: rp1: Use clk_sys for ethernet hclk and pclk hclk and pclk of the MAC are connected to clk_sys, so define them as being connected accordingly, rather than having fake @@ -14,7 +14,7 @@ Signed-off-by: Dave Stevenson --- a/arch/arm64/boot/dts/broadcom/rp1.dtsi +++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi -@@ -974,7 +974,8 @@ +@@ -982,7 +982,8 @@ #address-cells = <1>; #size-cells = <0>; interrupts = ; @@ -24,7 +24,7 @@ Signed-off-by: Dave Stevenson &rp1_clocks RP1_CLK_ETH_TSU &rp1_clocks RP1_CLK_ETH>; clock-names = "pclk", "hclk", "tsu_clk", "tx_clk"; -@@ -1195,18 +1196,6 @@ +@@ -1230,18 +1231,6 @@ clock-output-names = "xosc"; clock-frequency = <50000000>; }; diff --git a/target/linux/bcm27xx/patches-6.6/950-1430-dt-rp1-Link-RP1-DMA-to-the-associated-clock.patch b/target/linux/bcm27xx/patches-6.6/950-1430-dt-rp1-Link-RP1-DMA-to-the-associated-clock.patch index 36d4f8b723..623fcc6cc3 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1430-dt-rp1-Link-RP1-DMA-to-the-associated-clock.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1430-dt-rp1-Link-RP1-DMA-to-the-associated-clock.patch @@ -1,7 +1,7 @@ From efecbda4014b490e042c7fd090942b32316f9345 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 13 Nov 2024 13:11:33 +0000 -Subject: [PATCH 1430/1482] dt: rp1: Link RP1 DMA to the associated clock +Subject: [PATCH] dt: rp1: Link RP1 DMA to the associated clock This makes the kernel representation of the clock structure match reality. @@ -13,7 +13,7 @@ Signed-off-by: Dave Stevenson --- a/arch/arm64/boot/dts/broadcom/rp1.dtsi +++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi -@@ -1061,7 +1061,7 @@ +@@ -1081,7 +1081,7 @@ reg = <0xc0 0x40188000 0x0 0x1000>; compatible = "snps,axi-dma-1.01a"; interrupts = ; diff --git a/target/linux/bcm27xx/patches-6.6/950-1431-raspberrypi-firmware-Add-the-RPI-firmware-UART-APIs.patch b/target/linux/bcm27xx/patches-6.6/950-1431-raspberrypi-firmware-Add-the-RPI-firmware-UART-APIs.patch new file mode 100644 index 0000000000..7e518bff1c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1431-raspberrypi-firmware-Add-the-RPI-firmware-UART-APIs.patch @@ -0,0 +1,23 @@ +From eb035f3ad7da1324d310ef83b42398f47d5bafe7 Mon Sep 17 00:00:00 2001 +From: Tim Gover +Date: Fri, 1 Nov 2024 19:42:17 +0000 +Subject: [PATCH] raspberrypi-firmware: Add the RPI firmware UART APIs + +Add VideoCore mailbox definitions for the new RPi firmware UART. + +Signed-off-by: Tim Gover +--- + include/soc/bcm2835/raspberrypi-firmware.h | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/include/soc/bcm2835/raspberrypi-firmware.h ++++ b/include/soc/bcm2835/raspberrypi-firmware.h +@@ -98,6 +98,8 @@ enum rpi_firmware_property_tag { + RPI_FIRMWARE_GET_REBOOT_FLAGS = 0x00030064, + RPI_FIRMWARE_SET_REBOOT_FLAGS = 0x00038064, + RPI_FIRMWARE_NOTIFY_DISPLAY_DONE = 0x00030066, ++ RPI_FIRMWARE_GET_SW_UART = 0x0003008a, ++ RPI_FIRMWARE_SET_SW_UART = 0x0003808a, + + /* Dispmanx TAGS */ + RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001, diff --git a/target/linux/bcm27xx/patches-6.6/950-1432-serial-core-Add-the-Raspberry-Pi-firmware-UART-id.patch b/target/linux/bcm27xx/patches-6.6/950-1432-serial-core-Add-the-Raspberry-Pi-firmware-UART-id.patch new file mode 100644 index 0000000000..5767f407fe --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1432-serial-core-Add-the-Raspberry-Pi-firmware-UART-id.patch @@ -0,0 +1,22 @@ +From b8a0e563fd181205565a0edaaebc82b1abf0c5be Mon Sep 17 00:00:00 2001 +From: Tim Gover +Date: Fri, 1 Nov 2024 19:43:21 +0000 +Subject: [PATCH] serial: core: Add the Raspberry Pi firmware UART id + +Assign a new serial core number for the RPi firmware UART. + +Signed-off-by: Tim Gover +--- + include/uapi/linux/serial_core.h | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/include/uapi/linux/serial_core.h ++++ b/include/uapi/linux/serial_core.h +@@ -245,4 +245,7 @@ + /* Sunplus UART */ + #define PORT_SUNPLUS 123 + ++/* RPi firmware UART */ ++#define PORT_RPI_FW 124 ++ + #endif /* _UAPILINUX_SERIAL_CORE_H */ diff --git a/target/linux/bcm27xx/patches-6.6/950-1433-serial-tty-Add-a-driver-for-the-RPi-firmware-UART.patch b/target/linux/bcm27xx/patches-6.6/950-1433-serial-tty-Add-a-driver-for-the-RPi-firmware-UART.patch new file mode 100644 index 0000000000..f26c1bee86 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1433-serial-tty-Add-a-driver-for-the-RPi-firmware-UART.patch @@ -0,0 +1,630 @@ +From 2548d954d78bca44c5cf430f8ea6de7c771312d7 Mon Sep 17 00:00:00 2001 +From: Tim Gover +Date: Wed, 28 Aug 2024 09:46:50 +0100 +Subject: [PATCH] serial: tty: Add a driver for the RPi firmware UART + +On Raspberry Pi 4 and earlier models the firmware provides +a low speed (up to 115200 baud) bit-bashed UART on arbitrary +GPIOs using the second VPU core. + +The firmware driver is designed to support 19200 baud. Higher +rates up to 115200 seem to work but there may be more jitter. + +This can be useful for debug or managing additional low +speed peripherals if the hardware PL011 and 8250 hardware +UARTs are already used for console / bluetooth. + +The firmware driver requires a fixed core clock frequency +and also requires the VPU PWM audio driver to be disabled +(dtparam=audio=off) + +Runtime configuration is handled via the vc-mailbox APIs +with the FIFO buffers being allocated in uncached VPU +addressable memory. The FIFO pointers are stored in spare +VideoCore multi-core sync registers in order to reduce the number +of uncached SDRAM accesses thereby reducing jitter. + +Signed-off-by: Tim Gover +--- + drivers/tty/serial/Kconfig | 11 + + drivers/tty/serial/Makefile | 1 + + drivers/tty/serial/rpi-fw-uart.c | 563 +++++++++++++++++++++++++++++++ + 3 files changed, 575 insertions(+) + create mode 100644 drivers/tty/serial/rpi-fw-uart.c + +--- a/drivers/tty/serial/Kconfig ++++ b/drivers/tty/serial/Kconfig +@@ -1578,6 +1578,17 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE + but you can alter that using a kernel command line option such as + "console=ttyNVTx". + ++config SERIAL_RPI_FW ++ tristate "Raspberry Pi Firmware software UART support" ++ depends on ARM_AMBA || COMPILE_TEST ++ select SERIAL_CORE ++ help ++ This selects the Raspberry Pi firmware UART. This is a bit-bashed ++ implementation running on the Raspbery Pi VPU core. ++ This is not supported on Raspberry Pi 5 or newer platforms. ++ ++ If unsure, say N. ++ + endmenu + + config SERIAL_MCTRL_GPIO +--- a/drivers/tty/serial/Makefile ++++ b/drivers/tty/serial/Makefile +@@ -88,6 +88,7 @@ obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += mi + obj-$(CONFIG_SERIAL_SIFIVE) += sifive.o + obj-$(CONFIG_SERIAL_LITEUART) += liteuart.o + obj-$(CONFIG_SERIAL_SUNPLUS) += sunplus-uart.o ++obj-$(CONFIG_SERIAL_RPI_FW) += rpi-fw-uart.o + + # GPIOLIB helpers for modem control lines + obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o +--- /dev/null ++++ b/drivers/tty/serial/rpi-fw-uart.c +@@ -0,0 +1,563 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2024, Raspberry Pi Ltd. All rights reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define RPI_FW_UART_RX_FIFO_RD 0xb0 ++#define RPI_FW_UART_RX_FIFO_WR 0xb4 ++#define RPI_FW_UART_TX_FIFO_RD 0xb8 ++#define RPI_FW_UART_TX_FIFO_WR 0xbc ++ ++#define RPI_FW_UART_FIFO_SIZE 32 ++#define RPI_FW_UART_FIFO_SIZE_MASK (RPI_FW_UART_FIFO_SIZE - 1) ++ ++#define RPI_FW_UART_MIN_VERSION 3 ++ ++struct rpi_fw_uart_params { ++ u32 start; ++ u32 baud; ++ u32 data_bits; ++ u32 stop_bits; ++ u32 gpio_rx; ++ u32 gpio_tx; ++ u32 flags; ++ u32 fifosize; ++ u32 rx_buffer; ++ u32 tx_buffer; ++ u32 version; ++ u32 fifo_reg_base; ++}; ++ ++struct rpi_fw_uart { ++ struct uart_driver driver; ++ struct uart_port port; ++ struct rpi_firmware *firmware; ++ struct gpio_desc *rx_gpiod; ++ struct gpio_desc *tx_gpiod; ++ unsigned int rx_gpio; ++ unsigned int tx_gpio; ++ unsigned int baud; ++ unsigned int data_bits; ++ unsigned int stop_bits; ++ unsigned char __iomem *base; ++ size_t dma_buffer_size; ++ ++ struct hrtimer trigger_start_rx; ++ ktime_t rx_poll_delay; ++ void *rx_buffer; ++ dma_addr_t rx_buffer_dma_addr; ++ int rx_stop; ++ ++ void *tx_buffer; ++ dma_addr_t tx_buffer_dma_addr; ++}; ++ ++static unsigned int rpi_fw_uart_tx_is_full(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ u32 rd, wr; ++ ++ rd = readl(rfu->base + RPI_FW_UART_TX_FIFO_RD); ++ wr = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR); ++ return ((wr + 1) & RPI_FW_UART_FIFO_SIZE_MASK) == rd; ++} ++ ++static unsigned int rpi_fw_uart_tx_is_empty(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ u32 rd, wr; ++ ++ if (!rfu->tx_buffer) ++ return 1; ++ ++ rd = readl(rfu->base + RPI_FW_UART_TX_FIFO_RD); ++ wr = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR); ++ ++ return rd == wr; ++} ++ ++unsigned int rpi_fw_uart_rx_is_empty(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ u32 rd, wr; ++ ++ if (!rfu->rx_buffer) ++ return 1; ++ ++ rd = readl(rfu->base + RPI_FW_UART_RX_FIFO_RD); ++ wr = readl(rfu->base + RPI_FW_UART_RX_FIFO_WR); ++ ++ return rd == wr; ++} ++ ++static unsigned int rpi_fw_uart_tx_empty(struct uart_port *port) ++{ ++ return rpi_fw_uart_tx_is_empty(port) ? TIOCSER_TEMT : 0; ++} ++ ++static void rpi_fw_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) ++{ ++ /* ++ * No hardware flow control, firmware automatically configures ++ * TX to output high and RX to input low. ++ */ ++ dev_dbg(port->dev, "%s mctrl %u\n", __func__, mctrl); ++} ++ ++static unsigned int rpi_fw_uart_get_mctrl(struct uart_port *port) ++{ ++ /* No hardware flow control */ ++ return TIOCM_CTS; ++} ++ ++static void rpi_fw_uart_stop(struct uart_port *port) ++{ ++ struct rpi_fw_uart_params msg = {.start = 0}; ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ ++ hrtimer_cancel(&rfu->trigger_start_rx); ++ ++ if (rpi_firmware_property(rfu->firmware, ++ RPI_FIRMWARE_SET_SW_UART, ++ &msg, sizeof(msg))) ++ dev_warn(port->dev, ++ "Failed to shutdown rpi-fw uart. Firmware not configured?"); ++} ++ ++static void rpi_fw_uart_stop_tx(struct uart_port *port) ++{ ++ /* No supported by the current firmware APIs. */ ++} ++ ++static void rpi_fw_uart_stop_rx(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ ++ rfu->rx_stop = 1; ++} ++ ++static unsigned int rpi_fw_write(struct uart_port *port, const char *s, ++ unsigned int count) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ u8 *out = rfu->tx_buffer; ++ unsigned int consumed = 0; ++ ++ while (consumed < count && !rpi_fw_uart_tx_is_full(port)) { ++ u32 wp = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR) ++ & RPI_FW_UART_FIFO_SIZE_MASK; ++ out[wp] = s[consumed++]; ++ wp = (wp + 1) & RPI_FW_UART_FIFO_SIZE_MASK; ++ writel(wp, rfu->base + RPI_FW_UART_TX_FIFO_WR); ++ } ++ return consumed; ++} ++ ++/* Called with port.lock taken */ ++static void rpi_fw_uart_start_tx(struct uart_port *port) ++{ ++ struct circ_buf *xmit; ++ ++ xmit = &port->state->xmit; ++ for (;;) { ++ unsigned int consumed; ++ unsigned long count = CIRC_CNT_TO_END(xmit->head, xmit->tail, ++ UART_XMIT_SIZE); ++ if (!count) ++ break; ++ ++ consumed = rpi_fw_write(port, &xmit->buf[xmit->tail], count); ++ uart_xmit_advance(port, consumed); ++ } ++ uart_write_wakeup(port); ++} ++ ++/* Called with port.lock taken */ ++static void rpi_fw_uart_start_rx(struct uart_port *port) ++{ ++ struct tty_port *tty_port = &port->state->port; ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ int count = 0; ++ ++ /* ++ * RX is polled, read up to a full buffer of data before trying again ++ * so that this can be interrupted if the firmware is filling the ++ * buffer too fast ++ */ ++ while (!rpi_fw_uart_rx_is_empty(port) && count < port->fifosize) { ++ const u8 *in = rfu->rx_buffer; ++ u32 rp = readl(rfu->base + RPI_FW_UART_RX_FIFO_RD) ++ & RPI_FW_UART_FIFO_SIZE_MASK; ++ ++ tty_insert_flip_char(tty_port, in[rp], TTY_NORMAL); ++ rp = (rp + 1) & RPI_FW_UART_FIFO_SIZE_MASK; ++ writel(rp, rfu->base + RPI_FW_UART_RX_FIFO_RD); ++ count++; ++ } ++ if (count) ++ tty_flip_buffer_push(tty_port); ++} ++ ++static enum hrtimer_restart rpi_fw_uart_trigger_rx(struct hrtimer *t) ++{ ++ unsigned long flags; ++ struct rpi_fw_uart *rfu = container_of(t, struct rpi_fw_uart, ++ trigger_start_rx); ++ ++ spin_lock_irqsave(&rfu->port.lock, flags); ++ if (rfu->rx_stop) { ++ spin_unlock_irqrestore(&rfu->port.lock, flags); ++ return HRTIMER_NORESTART; ++ } ++ ++ rpi_fw_uart_start_rx(&rfu->port); ++ spin_unlock_irqrestore(&rfu->port.lock, flags); ++ hrtimer_forward_now(t, rfu->rx_poll_delay); ++ return HRTIMER_RESTART; ++} ++ ++static void rpi_fw_uart_break_ctl(struct uart_port *port, int ctl) ++{ ++ dev_dbg(port->dev, "%s ctl %d\n", __func__, ctl); ++} ++ ++static int rpi_fw_uart_configure(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ struct rpi_fw_uart_params msg; ++ unsigned long flags; ++ int rc; ++ ++ rpi_fw_uart_stop(port); ++ ++ memset(&msg, 0, sizeof(msg)); ++ msg.start = 1; ++ msg.gpio_rx = rfu->rx_gpio; ++ msg.gpio_tx = rfu->tx_gpio; ++ msg.data_bits = rfu->data_bits; ++ msg.stop_bits = rfu->stop_bits; ++ msg.baud = rfu->baud; ++ msg.fifosize = RPI_FW_UART_FIFO_SIZE; ++ msg.rx_buffer = (u32) rfu->rx_buffer_dma_addr; ++ msg.tx_buffer = (u32) rfu->tx_buffer_dma_addr; ++ ++ rfu->rx_poll_delay = ms_to_ktime(50); ++ ++ /* ++ * Reconfigures the firmware UART with the new settings. On the first ++ * call retrieve the addresses of the FIFO buffers. The buffers are ++ * allocated at startup and are not de-allocated. ++ * NB rpi_firmware_property can block ++ */ ++ rc = rpi_firmware_property(rfu->firmware, ++ RPI_FIRMWARE_SET_SW_UART, ++ &msg, sizeof(msg)); ++ if (rc) ++ goto fail; ++ ++ rc = rpi_firmware_property(rfu->firmware, ++ RPI_FIRMWARE_GET_SW_UART, ++ &msg, sizeof(msg)); ++ if (rc) ++ goto fail; ++ ++ dev_dbg(port->dev, "version %08x, reg addr %x\n", msg.version, ++ msg.fifo_reg_base); ++ ++ dev_info(port->dev, "started %d baud %u data %u stop %u rx %u tx %u flags %u fifosize %u\n", ++ msg.start, msg.baud, msg.data_bits, msg.stop_bits, ++ msg.gpio_rx, msg.gpio_tx, msg.flags, msg.fifosize); ++ ++ if (msg.fifosize != port->fifosize) { ++ dev_err(port->dev, "Expected fifo size %u actual %u", ++ port->fifosize, msg.fifosize); ++ rc = -EINVAL; ++ goto fail; ++ } ++ ++ if (!msg.start) { ++ dev_err(port->dev, "Firmware service not running\n"); ++ rc = -EINVAL; ++ } ++ ++ spin_lock_irqsave(&rfu->port.lock, flags); ++ rfu->rx_stop = 0; ++ hrtimer_start(&rfu->trigger_start_rx, ++ rfu->rx_poll_delay, HRTIMER_MODE_REL); ++ spin_unlock_irqrestore(&rfu->port.lock, flags); ++ return 0; ++fail: ++ dev_err(port->dev, "Failed to configure rpi-fw uart. Firmware not configured?"); ++ return rc; ++} ++ ++static void rpi_fw_uart_free_buffers(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ ++ if (rfu->rx_buffer) ++ dma_free_coherent(port->dev, rfu->dma_buffer_size, ++ rfu->rx_buffer, GFP_ATOMIC); ++ ++ if (rfu->tx_buffer) ++ dma_free_coherent(port->dev, rfu->dma_buffer_size, ++ rfu->tx_buffer, GFP_ATOMIC); ++ ++ rfu->rx_buffer = NULL; ++ rfu->tx_buffer = NULL; ++ rfu->rx_buffer_dma_addr = 0; ++ rfu->tx_buffer_dma_addr = 0; ++} ++ ++static int rpi_fw_uart_alloc_buffers(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ ++ if (rfu->tx_buffer) ++ return 0; ++ ++ rfu->dma_buffer_size = PAGE_ALIGN(RPI_FW_UART_FIFO_SIZE); ++ ++ rfu->rx_buffer = dma_alloc_coherent(port->dev, rfu->dma_buffer_size, ++ &rfu->rx_buffer_dma_addr, GFP_ATOMIC); ++ ++ if (!rfu->rx_buffer) ++ goto alloc_fail; ++ ++ rfu->tx_buffer = dma_alloc_coherent(port->dev, rfu->dma_buffer_size, ++ &rfu->tx_buffer_dma_addr, GFP_ATOMIC); ++ ++ if (!rfu->tx_buffer) ++ goto alloc_fail; ++ ++ dev_dbg(port->dev, "alloc-buffers %p %x %p %x\n", ++ rfu->rx_buffer, (u32) rfu->rx_buffer_dma_addr, ++ rfu->tx_buffer, (u32) rfu->tx_buffer_dma_addr); ++ return 0; ++ ++alloc_fail: ++ dev_err(port->dev, "%s uart buffer allocation failed\n", __func__); ++ rpi_fw_uart_free_buffers(port); ++ return -ENOMEM; ++} ++ ++static int rpi_fw_uart_startup(struct uart_port *port) ++{ ++ int rc; ++ ++ rc = rpi_fw_uart_alloc_buffers(port); ++ if (rc) ++ dev_err(port->dev, "Failed to start\n"); ++ return rc; ++} ++ ++static void rpi_fw_uart_shutdown(struct uart_port *port) ++{ ++ rpi_fw_uart_stop(port); ++ rpi_fw_uart_free_buffers(port); ++} ++ ++static void rpi_fw_uart_set_termios(struct uart_port *port, ++ struct ktermios *new, ++ const struct ktermios *old) ++{ ++ struct rpi_fw_uart *rfu = ++ container_of(port, struct rpi_fw_uart, port); ++ rfu->baud = uart_get_baud_rate(port, new, old, 50, 115200); ++ rfu->stop_bits = (new->c_cflag & CSTOPB) ? 2 : 1; ++ ++ rpi_fw_uart_configure(port); ++} ++ ++static const struct uart_ops rpi_fw_uart_ops = { ++ .tx_empty = rpi_fw_uart_tx_empty, ++ .set_mctrl = rpi_fw_uart_set_mctrl, ++ .get_mctrl = rpi_fw_uart_get_mctrl, ++ .stop_rx = rpi_fw_uart_stop_rx, ++ .stop_tx = rpi_fw_uart_stop_tx, ++ .start_tx = rpi_fw_uart_start_tx, ++ .break_ctl = rpi_fw_uart_break_ctl, ++ .startup = rpi_fw_uart_startup, ++ .shutdown = rpi_fw_uart_shutdown, ++ .set_termios = rpi_fw_uart_set_termios, ++}; ++ ++static int rpi_fw_uart_get_gpio_offset(struct device *dev, const char *name) ++{ ++ struct of_phandle_args of_args = { 0 }; ++ bool is_bcm28xx; ++ ++ /* This really shouldn't fail, given that we have a gpiod */ ++ if (of_parse_phandle_with_args(dev->of_node, name, "#gpio-cells", 0, &of_args)) ++ return dev_err_probe(dev, -EINVAL, "can't find gpio declaration\n"); ++ ++ is_bcm28xx = of_device_is_compatible(of_args.np, "brcm,bcm2835-gpio") || ++ of_device_is_compatible(of_args.np, "brcm,bcm2711-gpio"); ++ of_node_put(of_args.np); ++ if (!is_bcm28xx || of_args.args_count != 2) ++ return dev_err_probe(dev, -EINVAL, "not a BCM28xx gpio\n"); ++ ++ return of_args.args[0]; ++} ++ ++static int rpi_fw_uart_probe(struct platform_device *pdev) ++{ ++ struct device_node *firmware_node; ++ struct device *dev = &pdev->dev; ++ struct rpi_firmware *firmware; ++ struct uart_port *port; ++ struct rpi_fw_uart *rfu; ++ struct rpi_fw_uart_params msg; ++ int version_major; ++ int err; ++ ++ dev_dbg(dev, "%s of_node %p\n", __func__, dev->of_node); ++ ++ /* ++ * We can be probed either through the an old-fashioned ++ * platform device registration or through a DT node that is a ++ * child of the firmware node. Handle both cases. ++ */ ++ if (dev->of_node) ++ firmware_node = of_parse_phandle(dev->of_node, "firmware", 0); ++ else ++ firmware_node = of_find_compatible_node(NULL, NULL, ++ "raspberrypi,bcm2835-firmware"); ++ if (!firmware_node) { ++ dev_err(dev, "Missing firmware node\n"); ++ return -ENOENT; ++ } ++ ++ firmware = devm_rpi_firmware_get(dev, firmware_node); ++ of_node_put(firmware_node); ++ if (!firmware) ++ return -EPROBE_DEFER; ++ ++ rfu = devm_kzalloc(dev, sizeof(*rfu), GFP_KERNEL); ++ if (!rfu) ++ return -ENOMEM; ++ ++ rfu->firmware = firmware; ++ ++ err = rpi_firmware_property(rfu->firmware, RPI_FIRMWARE_GET_SW_UART, ++ &msg, sizeof(msg)); ++ if (err) { ++ dev_err(dev, "VC firmware does not support rpi-fw-uart\n"); ++ return err; ++ } ++ ++ version_major = msg.version >> 16; ++ if (msg.version < RPI_FW_UART_MIN_VERSION) { ++ dev_err(dev, "rpi-fw-uart fw version %d is too old min version %d\n", ++ version_major, RPI_FW_UART_MIN_VERSION); ++ return -EINVAL; ++ } ++ ++ rfu->rx_gpiod = devm_gpiod_get(dev, "rx", GPIOD_IN); ++ if (IS_ERR(rfu->rx_gpiod)) ++ return PTR_ERR(rfu->rx_gpiod); ++ ++ rfu->tx_gpiod = devm_gpiod_get(dev, "tx", GPIOD_OUT_HIGH); ++ if (IS_ERR(rfu->tx_gpiod)) ++ return PTR_ERR(rfu->tx_gpiod); ++ ++ rfu->rx_gpio = rpi_fw_uart_get_gpio_offset(dev, "rx-gpios"); ++ if (rfu->rx_gpio < 0) ++ return rfu->rx_gpio; ++ rfu->tx_gpio = rpi_fw_uart_get_gpio_offset(dev, "tx-gpios"); ++ if (rfu->tx_gpio < 0) ++ return rfu->tx_gpio; ++ ++ rfu->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(rfu->base)) ++ return PTR_ERR(rfu->base); ++ ++ /* setup the driver */ ++ rfu->driver.owner = THIS_MODULE; ++ rfu->driver.driver_name = "ttyRFU"; ++ rfu->driver.dev_name = "ttyRFU"; ++ rfu->driver.nr = 1; ++ rfu->data_bits = 8; ++ ++ /* RX is polled */ ++ hrtimer_init(&rfu->trigger_start_rx, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ rfu->trigger_start_rx.function = rpi_fw_uart_trigger_rx; ++ ++ err = uart_register_driver(&rfu->driver); ++ if (err) { ++ dev_err(dev, "failed to register UART driver: %d\n", ++ err); ++ return err; ++ } ++ ++ /* setup the port */ ++ port = &rfu->port; ++ spin_lock_init(&port->lock); ++ port->dev = &pdev->dev; ++ port->type = PORT_RPI_FW; ++ port->ops = &rpi_fw_uart_ops; ++ port->fifosize = RPI_FW_UART_FIFO_SIZE; ++ port->iotype = UPIO_MEM; ++ port->flags = UPF_BOOT_AUTOCONF; ++ port->private_data = rfu; ++ ++ err = uart_add_one_port(&rfu->driver, port); ++ if (err) { ++ dev_err(dev, "failed to add UART port: %d\n", err); ++ goto unregister_uart; ++ } ++ platform_set_drvdata(pdev, rfu); ++ ++ dev_info(dev, "version %d.%d gpios tx %u rx %u\n", ++ msg.version >> 16, msg.version & 0xffff, ++ rfu->tx_gpio, rfu->rx_gpio); ++ return 0; ++ ++unregister_uart: ++ uart_unregister_driver(&rfu->driver); ++ ++ return err; ++} ++ ++static int rpi_fw_uart_remove(struct platform_device *pdev) ++{ ++ struct rpi_fw_uart *rfu = platform_get_drvdata(pdev); ++ ++ uart_remove_one_port(&rfu->driver, &rfu->port); ++ uart_unregister_driver(&rfu->driver); ++ ++ return 0; ++} ++ ++static const struct of_device_id rpi_fw_match[] = { ++ { .compatible = "raspberrypi,firmware-uart" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, rpi_fw_match); ++ ++static struct platform_driver rpi_fw_driver = { ++ .driver = { ++ .name = "rpi_fw-uart", ++ .of_match_table = rpi_fw_match, ++ }, ++ .probe = rpi_fw_uart_probe, ++ .remove = rpi_fw_uart_remove, ++}; ++module_platform_driver(rpi_fw_driver); ++ ++MODULE_AUTHOR("Tim Gover "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Raspberry Pi Firmware Software UART driver"); diff --git a/target/linux/bcm27xx/patches-6.6/950-1435-dtoverlay-Add-an-overlay-for-the-Raspberry-Pi-firmwa.patch b/target/linux/bcm27xx/patches-6.6/950-1435-dtoverlay-Add-an-overlay-for-the-Raspberry-Pi-firmwa.patch new file mode 100644 index 0000000000..0e4b3e3747 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1435-dtoverlay-Add-an-overlay-for-the-Raspberry-Pi-firmwa.patch @@ -0,0 +1,95 @@ +From b6b126861062020fb50859c5af71d8846ce43d7c Mon Sep 17 00:00:00 2001 +From: Tim Gover +Date: Mon, 4 Nov 2024 13:44:10 +0000 +Subject: [PATCH] dtoverlay: Add an overlay for the Raspberry Pi firmware UART + +Add a device-tree overlay to configure the GPIOs for the +Raspberry Pi firmware UART. + +Example config.txt +dtoverlay=rpi-fw-uart,txd0_pin=20,rxd0_pin=21 + +Signed-off-by: Phil Elwell +Signed-off-by: Tim Gover +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 12 ++++++ + .../boot/dts/overlays/rpi-fw-uart-overlay.dts | 41 +++++++++++++++++++ + 3 files changed, 54 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/rpi-fw-uart-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -233,6 +233,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + rpi-dacpro.dtbo \ + rpi-digiampplus.dtbo \ + rpi-ft5406.dtbo \ ++ rpi-fw-uart.dtbo \ + rpi-poe.dtbo \ + rpi-poe-plus.dtbo \ + rpi-sense.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -4141,6 +4141,18 @@ Params: touchscreen-size-x Touchscr + touchscreen-swapped-x-y Swap X and Y cordinates (default 0); + + ++Name: rpi-fw-uart ++Info: Configures the firmware software UART driver. ++ This driver requires exclusive usage of the second VPU core. The ++ following config.txt entries should be set when this driver is used. ++ dtparam=audio=off ++ isp_use_vpu0=1 ++Load: dtoverlay=rpi-fw-uart,[=] ++Params: txd0_pin GPIO pin for TXD0 (any free - default 20) ++ ++ rxd0_pin GPIO pin for RXD0 (any free - default 21) ++ ++ + Name: rpi-poe + Info: Raspberry Pi PoE HAT fan + Load: dtoverlay=rpi-poe,[=] +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/rpi-fw-uart-overlay.dts +@@ -0,0 +1,41 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Overlay for the Raspberry Pi Firmware UART driver ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ rpi_fw_uart_pins: rpi_fw_uart_pins@4 { ++ brcm,pins = <20 21>; ++ brcm,function = <1 0>; /* output input */ ++ brcm,pull = <0 2>; /* none pull-up */ ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&soc>; ++ __overlay__ { ++ rpi_fw_uart: rpi_fw_uart@7e000000 { ++ compatible = "raspberrypi,firmware-uart"; ++ reg = <0x7e000000 0x100>; /* VideoCore MS sync regs */ ++ firmware = <&firmware>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&rpi_fw_uart_pins>; ++ tx-gpios = <&gpio 20 0>; ++ rx-gpios = <&gpio 21 0>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ txd0_pin = <&rpi_fw_uart>,"tx-gpios:4", ++ <&rpi_fw_uart_pins>, "brcm,pins:0"; ++ rxd0_pin = <&rpi_fw_uart>,"rx-gpios:4", ++ <&rpi_fw_uart_pins>, "brcm,pins:4"; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-6.6/950-1436-ARM-dts-Remove-duplicate-tags.patch b/target/linux/bcm27xx/patches-6.6/950-1436-ARM-dts-Remove-duplicate-tags.patch new file mode 100644 index 0000000000..7829a4b753 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1436-ARM-dts-Remove-duplicate-tags.patch @@ -0,0 +1,142 @@ +From 1993b453dc4a62378e90d91e9e0006a6c085f38a Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 18 Sep 2024 10:23:41 +0100 +Subject: [PATCH] ARM: dts: Remove duplicate tags + +A dts file should have exactly one /dts-v1/ tag, and overlays should +also have one /plugin/ tag. Through careless inclusion of other files, +some Device Trees and overlays end up with duplicated tags - this +commit removes them. + +The change is largely cosmetic, unless using an old version of dtc. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts | 1 - + arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi | 3 --- + arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi | 2 -- + arch/arm/boot/dts/overlays/pisound-pi5-overlay.dts | 3 --- + arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts | 3 --- + arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts | 3 --- + arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts | 3 --- + arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts | 3 --- + arch/arm/boot/dts/overlays/w1-gpio-pi5-overlay.dts | 3 --- + arch/arm/boot/dts/overlays/w1-gpio-pullup-pi5-overlay.dts | 3 --- + arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi | 1 - + 11 files changed, 28 deletions(-) + +--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts ++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts +@@ -1,5 +1,4 @@ + // SPDX-License-Identifier: GPL-2.0 +-/dts-v1/; + #include "bcm2711-rpi-4-b.dts" + + / { +--- a/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi ++++ b/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi +@@ -1,7 +1,4 @@ + // Definitions for I2C based sensors using the Industrial IO or HWMON interface. +-/dts-v1/; +-/plugin/; +- + #include + + / { +--- a/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi ++++ b/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi +@@ -1,8 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0-only + // Partial definitions for IMX290 or IMX327 camera module on VC I2C bus + // The compatible string should be set in an overlay that then includes this one +-/dts-v1/; +-/plugin/; + + #include + +--- a/arch/arm/boot/dts/overlays/pisound-pi5-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pisound-pi5-overlay.dts +@@ -17,9 +17,6 @@ + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +-/dts-v1/; +-/plugin/; +- + #include "pisound-overlay.dts" + + &pisound_spi { +--- a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts +@@ -2,9 +2,6 @@ + * vc4-fkms-v3d-overlay.dts + */ + +-/dts-v1/; +-/plugin/; +- + #include "cma-overlay.dts" + + / { +--- a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts +@@ -2,9 +2,6 @@ + * vc4-fkms-v3d-overlay.dts + */ + +-/dts-v1/; +-/plugin/; +- + #include "cma-overlay.dts" + + &frag0 { +--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts +@@ -2,9 +2,6 @@ + * vc4-kms-v3d-overlay.dts + */ + +-/dts-v1/; +-/plugin/; +- + #include + + #include "cma-overlay.dts" +--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts +@@ -2,9 +2,6 @@ + * vc4-kms-v3d-pi4-overlay.dts + */ + +-/dts-v1/; +-/plugin/; +- + #include + + #include "cma-overlay.dts" +--- a/arch/arm/boot/dts/overlays/w1-gpio-pi5-overlay.dts ++++ b/arch/arm/boot/dts/overlays/w1-gpio-pi5-overlay.dts +@@ -1,6 +1,3 @@ +-/dts-v1/; +-/plugin/; +- + #include "w1-gpio-overlay.dts" + + / { +--- a/arch/arm/boot/dts/overlays/w1-gpio-pullup-pi5-overlay.dts ++++ b/arch/arm/boot/dts/overlays/w1-gpio-pullup-pi5-overlay.dts +@@ -1,6 +1,3 @@ +-/dts-v1/; +-/plugin/; +- + #include "w1-gpio-pullup-overlay.dts" + + / { +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi +@@ -1,5 +1,4 @@ + // SPDX-License-Identifier: GPL-2.0 +-/dts-v1/; + + #include "bcm2712-rpi-cm5.dtsi" + diff --git a/target/linux/bcm27xx/patches-6.6/950-1437-Allow-setting-I-C-clock-frequency-via-i2c_arm_baudra.patch b/target/linux/bcm27xx/patches-6.6/950-1437-Allow-setting-I-C-clock-frequency-via-i2c_arm_baudra.patch new file mode 100644 index 0000000000..1d14a0bfec --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1437-Allow-setting-I-C-clock-frequency-via-i2c_arm_baudra.patch @@ -0,0 +1,25 @@ +From e33702e5e5fe9fef6ec967961e2e5e1c2285ba36 Mon Sep 17 00:00:00 2001 +From: gtrainavicius +Date: Wed, 4 Dec 2024 11:18:14 +0200 +Subject: [PATCH] =?UTF-8?q?Allow=20setting=20I=C2=B2C=20clock=20frequency?= + =?UTF-8?q?=20via=20i2c=5Farm=5Fbaudrate=20dtparam=20when=20using=20pimidi?= + =?UTF-8?q?=20overlay.?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This change removes the forced 1MHz clock frequency, so it can be overridden using `i2c_arm_baudrate`. +--- + arch/arm/boot/dts/overlays/pimidi-overlay.dts | 1 - + 1 file changed, 1 deletion(-) + +--- a/arch/arm/boot/dts/overlays/pimidi-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pimidi-overlay.dts +@@ -26,7 +26,6 @@ + target = <&i2c_arm>; + __overlay__ { + status = "okay"; +- clock-frequency=<1000000>; + + pimidi_ctrl: pimidi_ctrl@20 { + compatible = "blokaslabs,pimidi"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1438-nvme-pci-Disable-Host-Memory-Buffer-usage.patch b/target/linux/bcm27xx/patches-6.6/950-1438-nvme-pci-Disable-Host-Memory-Buffer-usage.patch new file mode 100644 index 0000000000..4f1ea9249c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1438-nvme-pci-Disable-Host-Memory-Buffer-usage.patch @@ -0,0 +1,48 @@ +From fda47c026dee7acd975ee2c0f7a440d4038cfaa3 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 3 Dec 2024 15:57:01 +0000 +Subject: [PATCH] nvme-pci: Disable Host Memory Buffer usage + +Some NVME drives seem to request significant amounts of DMA coherent +memory - enough to exhaust our standard 64MB CMA allocation. + +Try disabling the feature to see what effect it has - drives should +continue to function without it. + +Link: https://github.com/raspberrypi/linux/issues/6504 + +Signed-off-by: Phil Elwell +--- + drivers/nvme/host/pci.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -1932,6 +1932,7 @@ static void nvme_free_host_mem(struct nv + dev->nr_host_mem_descs = 0; + } + ++#if 0 + static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred, + u32 chunk_size) + { +@@ -2000,9 +2001,11 @@ out: + dev->host_mem_descs = NULL; + return -ENOMEM; + } ++#endif + + static int nvme_alloc_host_mem(struct nvme_dev *dev, u64 min, u64 preferred) + { ++#if 0 + u64 min_chunk = min_t(u64, preferred, PAGE_SIZE * MAX_ORDER_NR_PAGES); + u64 hmminds = max_t(u32, dev->ctrl.hmminds * 4096, PAGE_SIZE * 2); + u64 chunk_size; +@@ -2015,6 +2018,7 @@ static int nvme_alloc_host_mem(struct nv + nvme_free_host_mem(dev); + } + } ++#endif + + return -ENOMEM; + } diff --git a/target/linux/bcm27xx/patches-6.6/950-1439-fixup-serial-tty-Add-a-driver-for-the-RPi-firmware-U.patch b/target/linux/bcm27xx/patches-6.6/950-1439-fixup-serial-tty-Add-a-driver-for-the-RPi-firmware-U.patch new file mode 100644 index 0000000000..0f18dc4a8d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1439-fixup-serial-tty-Add-a-driver-for-the-RPi-firmware-U.patch @@ -0,0 +1,23 @@ +From 0313a0961b685973f7833017479a277e3a4c05a4 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 4 Dec 2024 14:40:59 +0000 +Subject: [PATCH] fixup! serial: tty: Add a driver for the RPi firmware UART + +Make SERIAL_RPI_FW depend on RASPBERRYPI_FIRMWARE. + +Signed-off-by: Phil Elwell +--- + drivers/tty/serial/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/tty/serial/Kconfig ++++ b/drivers/tty/serial/Kconfig +@@ -1580,7 +1580,7 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE + + config SERIAL_RPI_FW + tristate "Raspberry Pi Firmware software UART support" +- depends on ARM_AMBA || COMPILE_TEST ++ depends on RASPBERRYPI_FIRMWARE || COMPILE_TEST + select SERIAL_CORE + help + This selects the Raspberry Pi firmware UART. This is a bit-bashed diff --git a/target/linux/bcm27xx/patches-6.6/950-1440-serial-rpi-fw-uart-Demote-debug-log-messages.patch b/target/linux/bcm27xx/patches-6.6/950-1440-serial-rpi-fw-uart-Demote-debug-log-messages.patch new file mode 100644 index 0000000000..271f743286 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1440-serial-rpi-fw-uart-Demote-debug-log-messages.patch @@ -0,0 +1,28 @@ +From 0a5be0fe6ba3a981508421131def7eab55d6d75c Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 5 Dec 2024 12:08:23 +0000 +Subject: [PATCH] serial: rpi-fw-uart: Demote debug log messages + +A dev_info call in rpi_fw_uart_configure causes kernel log output every +time one opens the UART. Demote it to dev_dbg. + +Signed-off-by: Phil Elwell +--- + drivers/tty/serial/rpi-fw-uart.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/tty/serial/rpi-fw-uart.c ++++ b/drivers/tty/serial/rpi-fw-uart.c +@@ -277,9 +277,9 @@ static int rpi_fw_uart_configure(struct + dev_dbg(port->dev, "version %08x, reg addr %x\n", msg.version, + msg.fifo_reg_base); + +- dev_info(port->dev, "started %d baud %u data %u stop %u rx %u tx %u flags %u fifosize %u\n", +- msg.start, msg.baud, msg.data_bits, msg.stop_bits, +- msg.gpio_rx, msg.gpio_tx, msg.flags, msg.fifosize); ++ dev_dbg(port->dev, "started %d baud %u data %u stop %u rx %u tx %u flags %u fifosize %u\n", ++ msg.start, msg.baud, msg.data_bits, msg.stop_bits, ++ msg.gpio_rx, msg.gpio_tx, msg.flags, msg.fifosize); + + if (msg.fifosize != port->fifosize) { + dev_err(port->dev, "Expected fifo size %u actual %u", diff --git a/target/linux/bcm27xx/patches-6.6/950-1441-dtoverlays-Add-Arducam-override-for-ov9281.patch b/target/linux/bcm27xx/patches-6.6/950-1441-dtoverlays-Add-Arducam-override-for-ov9281.patch new file mode 100644 index 0000000000..354ff961fe --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1441-dtoverlays-Add-Arducam-override-for-ov9281.patch @@ -0,0 +1,55 @@ +From 02dee262a9c7295ea514e9db7b9aa4b239922cb3 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 2 Dec 2024 15:41:21 +0000 +Subject: [PATCH] dtoverlays: Add Arducam override for ov9281 + +The Arducam module is slow starting up, so add an override +to slow the regulator down. +https://forums.raspberrypi.com/viewtopic.php?t=380236 + +Signed-off-by: Dave Stevenson +--- + arch/arm/boot/dts/overlays/README | 2 ++ + arch/arm/boot/dts/overlays/ov9281-overlay.dts | 13 ++++++++++++- + 2 files changed, 14 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -3538,6 +3538,8 @@ Params: rotation Mounting + configuring the sensor (default on) + cam0 Adopt the default configuration for CAM0 on a + Compute Module (CSI0, i2c_vc, and cam0_reg). ++ arducam Slow down the regulator for slow Arducam ++ modules. + + + Name: papirus +--- a/arch/arm/boot/dts/overlays/ov9281-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ov9281-overlay.dts +@@ -57,6 +57,14 @@ + }; + }; + ++ reg_frag: fragment@5 { ++ target = <&cam1_reg>; ++ __dormant__ { ++ startup-delay-us = <20000>; ++ off-on-delay-us = <30000>; ++ }; ++ }; ++ + __overrides__ { + rotation = <&cam_node>,"rotation:0"; + orientation = <&cam_node>,"orientation:0"; +@@ -65,7 +73,10 @@ + <&csi_frag>, "target:0=",<&csi0>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <&cam_node>, "clocks:0=",<&cam0_clk>, +- <&cam_node>, "avdd-supply:0=",<&cam0_reg>; ++ <&cam_node>, "avdd-supply:0=",<&cam0_reg>, ++ <®_frag>, "target:0=",<&cam0_reg>; ++ arducam = <0>, "+5"; ++ + }; + }; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1442-drivers-input-touchscreen-Add-support-for-no-irq-to-.patch b/target/linux/bcm27xx/patches-6.6/950-1442-drivers-input-touchscreen-Add-support-for-no-irq-to-.patch new file mode 100644 index 0000000000..d13982e9ba --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1442-drivers-input-touchscreen-Add-support-for-no-irq-to-.patch @@ -0,0 +1,118 @@ +From 97638920f1a40e2e0cab363d1e03837ff50c5478 Mon Sep 17 00:00:00 2001 +From: eng33 +Date: Thu, 5 Dec 2024 17:19:23 +0800 +Subject: [PATCH] drivers:input:touchscreen: Add support for no irq to ili210x + driver + +Signed-off-by: eng33 +--- + drivers/input/touchscreen/ili210x.c | 63 ++++++++++++++++++++++++----- + 1 file changed, 52 insertions(+), 11 deletions(-) + +--- a/drivers/input/touchscreen/ili210x.c ++++ b/drivers/input/touchscreen/ili210x.c +@@ -67,6 +67,8 @@ struct ili210x { + u8 version_proto[2]; + u8 ic_mode[2]; + bool stop; ++ struct timer_list poll_timer; ++ struct work_struct poll_work; + }; + + static int ili210x_read_reg(struct i2c_client *client, +@@ -360,6 +362,34 @@ static irqreturn_t ili210x_irq(int irq, + return IRQ_HANDLED; + } + ++static void ili210x_poll_work(struct work_struct *work) ++{ ++ struct ili210x *priv = container_of(work, struct ili210x, poll_work); ++ struct i2c_client *client = priv->client; ++ const struct ili2xxx_chip *chip = priv->chip; ++ u8 touchdata[ILI210X_DATA_SIZE] = { 0 }; ++ bool touch; ++ int error; ++ ++ error = chip->get_touch_data(client, touchdata); ++ if (error) { ++ dev_err(&client->dev, "Unable to get touch data: %d\n", error); ++ return; ++ } ++ ++ touch = ili210x_report_events(priv, touchdata); ++} ++ ++static void ili210x_poll_timer_callback(struct timer_list *t) ++{ ++ struct ili210x *priv = from_timer(priv, t, poll_timer); ++ ++ schedule_work(&priv->poll_work); ++ ++ if (!priv->stop) ++ mod_timer(&priv->poll_timer, jiffies + msecs_to_jiffies(ILI2XXX_POLL_PERIOD)); ++} ++ + static int ili251x_firmware_update_resolution(struct device *dev) + { + struct i2c_client *client = to_i2c_client(dev); +@@ -945,11 +975,6 @@ static int ili210x_i2c_probe(struct i2c_ + return -ENODEV; + } + +- if (client->irq <= 0) { +- dev_err(dev, "No IRQ!\n"); +- return -EINVAL; +- } +- + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpio)) + return PTR_ERR(reset_gpio); +@@ -1001,12 +1026,17 @@ static int ili210x_i2c_probe(struct i2c_ + return error; + } + +- error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq, +- IRQF_ONESHOT, client->name, priv); +- if (error) { +- dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", +- error); +- return error; ++ if (client->irq) { ++ error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq, ++ IRQF_ONESHOT, client->name, priv); ++ if (error) { ++ dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", error); ++ return error; ++ } ++ } else { ++ timer_setup(&priv->poll_timer, ili210x_poll_timer_callback, 0); ++ mod_timer(&priv->poll_timer, jiffies + msecs_to_jiffies(ILI2XXX_POLL_PERIOD)); ++ INIT_WORK(&priv->poll_work, ili210x_poll_work); + } + + error = devm_add_action_or_reset(dev, ili210x_stop, priv); +@@ -1029,6 +1059,16 @@ static int ili210x_i2c_probe(struct i2c_ + return 0; + } + ++static void ili210x_i2c_remove(struct i2c_client *client) ++{ ++ struct ili210x *tsdata = i2c_get_clientdata(client); ++ ++ if (!client->irq) { ++ del_timer(&tsdata->poll_timer); ++ cancel_work_sync(&tsdata->poll_work); ++ } ++} ++ + static const struct i2c_device_id ili210x_i2c_id[] = { + { "ili210x", (long)&ili210x_chip }, + { "ili2117", (long)&ili211x_chip }, +@@ -1054,6 +1094,7 @@ static struct i2c_driver ili210x_ts_driv + }, + .id_table = ili210x_i2c_id, + .probe = ili210x_i2c_probe, ++ .remove = ili210x_i2c_remove, + }; + + module_i2c_driver(ili210x_ts_driver); diff --git a/target/linux/bcm27xx/patches-6.6/950-1443-drivers-gpu-drm-panel-Added-waveshare-13.3inch-panel.patch b/target/linux/bcm27xx/patches-6.6/950-1443-drivers-gpu-drm-panel-Added-waveshare-13.3inch-panel.patch new file mode 100644 index 0000000000..c6861c6a54 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1443-drivers-gpu-drm-panel-Added-waveshare-13.3inch-panel.patch @@ -0,0 +1,309 @@ +From 4a89fda8f73df89e009a6188ef07ab97b1d03c7f Mon Sep 17 00:00:00 2001 +From: eng33 +Date: Thu, 5 Dec 2024 17:20:22 +0800 +Subject: [PATCH] drivers:gpu:drm:panel: Added waveshare 13.3inch panel(support + 2/4lane) + +Signed-off-by: eng33 +--- + drivers/gpu/drm/panel/panel-waveshare-dsi.c | 155 +++++++++++++++++--- + 1 file changed, 138 insertions(+), 17 deletions(-) + +--- a/drivers/gpu/drm/panel/panel-waveshare-dsi.c ++++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c +@@ -32,6 +32,12 @@ struct ws_panel { + enum drm_panel_orientation orientation; + }; + ++struct ws_panel_data { ++ const struct drm_display_mode *mode; ++ int lanes; ++ unsigned long mode_flags; ++}; ++ + /* 2.8inch 480x640 + * https://www.waveshare.com/product/raspberry-pi/displays/2.8inch-dsi-lcd.htm + */ +@@ -47,6 +53,12 @@ static const struct drm_display_mode ws_ + .vtotal = 640 + 150 + 50 + 150, + }; + ++static const struct ws_panel_data ws_panel_2_8_data = { ++ .mode = &ws_panel_2_8_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 3.4inch 800x800 Round + * https://www.waveshare.com/product/displays/lcd-oled/3.4inch-dsi-lcd-c.htm + */ +@@ -62,6 +74,12 @@ static const struct drm_display_mode ws_ + .vtotal = 800 + 8 + 4 + 16, + }; + ++static const struct ws_panel_data ws_panel_3_4_data = { ++ .mode = &ws_panel_3_4_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 4.0inch 480x800 + * https://www.waveshare.com/product/raspberry-pi/displays/4inch-dsi-lcd.htm + */ +@@ -77,6 +95,12 @@ static const struct drm_display_mode ws_ + .vtotal = 800 + 20 + 100 + 20, + }; + ++static const struct ws_panel_data ws_panel_4_0_data = { ++ .mode = &ws_panel_4_0_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 7.0inch C 1024x600 + * https://www.waveshare.com/product/raspberry-pi/displays/lcd-oled/7inch-dsi-lcd-c-with-case-a.htm + */ +@@ -92,6 +116,12 @@ static const struct drm_display_mode ws_ + .vtotal = 600 + 10 + 10 + 10, + }; + ++static const struct ws_panel_data ws_panel_7_0_c_data = { ++ .mode = &ws_panel_7_0_c_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 7.9inch 400x1280 + * https://www.waveshare.com/product/raspberry-pi/displays/7.9inch-dsi-lcd.htm + */ +@@ -107,6 +137,12 @@ static const struct drm_display_mode ws_ + .vtotal = 1280 + 20 + 10 + 20, + }; + ++static const struct ws_panel_data ws_panel_7_9_data = { ++ .mode = &ws_panel_7_9_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 8.0inch or 10.1inch 1280x800 + * https://www.waveshare.com/product/raspberry-pi/displays/8inch-dsi-lcd-c.htm + * https://www.waveshare.com/product/raspberry-pi/displays/10.1inch-dsi-lcd-c.htm +@@ -123,6 +159,12 @@ static const struct drm_display_mode ws_ + .vtotal = 800 + 40 + 48 + 40, + }; + ++static const struct ws_panel_data ws_panel_10_1_data = { ++ .mode = &ws_panel_10_1_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 11.9inch 320x1480 + * https://www.waveshare.com/product/raspberry-pi/displays/11.9inch-dsi-lcd.htm + */ +@@ -138,6 +180,12 @@ static const struct drm_display_mode ws_ + .vtotal = 1480 + 60 + 60 + 60, + }; + ++static const struct ws_panel_data ws_panel_11_9_data = { ++ .mode = &ws_panel_11_9_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + static const struct drm_display_mode ws_panel_4_mode = { + .clock = 50000, + .hdisplay = 720, +@@ -150,6 +198,12 @@ static const struct drm_display_mode ws_ + .vtotal = 720 + 8 + 4 + 16, + }; + ++static const struct ws_panel_data ws_panel_4_data = { ++ .mode = &ws_panel_4_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 5.0inch 720x1280 + * https://www.waveshare.com/5inch-dsi-lcd-d.htm + */ +@@ -165,6 +219,12 @@ static const struct drm_display_mode ws_ + .vtotal = 1280 + 20 + 20 + 20, + }; + ++static const struct ws_panel_data ws_panel_5_0_data = { ++ .mode = &ws_panel_5_0_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 6.25inch 720x1560 + * https://www.waveshare.com/6.25inch-dsi-lcd.htm + */ +@@ -180,6 +240,12 @@ static const struct drm_display_mode ws_ + .vtotal = 1560 + 20 + 20 + 20, + }; + ++static const struct ws_panel_data ws_panel_6_25_data = { ++ .mode = &ws_panel_6_25_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 8.8inch 480x1920 + * https://www.waveshare.com/8.8inch-dsi-lcd.htm + */ +@@ -195,6 +261,48 @@ static const struct drm_display_mode ws_ + .vtotal = 1920 + 20 + 20 + 20, + }; + ++static const struct ws_panel_data ws_panel_8_8_data = { ++ .mode = &ws_panel_8_8_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ ++static const struct drm_display_mode ws_panel_13_3_4lane_mode = { ++ .clock = 148500, ++ .hdisplay = 1920, ++ .hsync_start = 1920 + 88, ++ .hsync_end = 1920 + 88 + 44, ++ .htotal = 1920 + 88 + 44 + 148, ++ .vdisplay = 1080, ++ .vsync_start = 1080 + 4, ++ .vsync_end = 1080 + 4 + 5, ++ .vtotal = 1080 + 4 + 5 + 36, ++}; ++ ++static const struct ws_panel_data ws_panel_13_3_4lane_data = { ++ .mode = &ws_panel_13_3_4lane_mode, ++ .lanes = 4, ++ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM, ++}; ++ ++static const struct drm_display_mode ws_panel_13_3_2lane_mode = { ++ .clock = 83333, ++ .hdisplay = 1920, ++ .hsync_start = 1920 + 88, ++ .hsync_end = 1920 + 88 + 44, ++ .htotal = 1920 + 88 + 44 + 148, ++ .vdisplay = 1080, ++ .vsync_start = 1080 + 4, ++ .vsync_end = 1080 + 4 + 5, ++ .vtotal = 1080 + 4 + 5 + 36, ++}; ++ ++static const struct ws_panel_data ws_panel_13_3_2lane_data = { ++ .mode = &ws_panel_13_3_2lane_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM, ++}; ++ + static struct ws_panel *panel_to_ts(struct drm_panel *panel) + { + return container_of(panel, struct ws_panel, base); +@@ -232,7 +340,10 @@ static int ws_panel_enable(struct drm_pa + { + struct ws_panel *ts = panel_to_ts(panel); + +- ws_panel_i2c_write(ts, 0xad, 0x01); ++ if (ts->mode == &ws_panel_13_3_2lane_mode) ++ ws_panel_i2c_write(ts, 0xad, 0x02); ++ else ++ ws_panel_i2c_write(ts, 0xad, 0x01); + + return 0; + } +@@ -328,13 +439,18 @@ static int ws_panel_probe(struct i2c_cli + .channel = 0, + .node = NULL, + }; ++ const struct ws_panel_data *_ws_panel_data; + int ret; + + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + +- ts->mode = of_device_get_match_data(dev); ++ _ws_panel_data = of_device_get_match_data(dev); ++ if (!_ws_panel_data) ++ return -EINVAL; ++ ++ ts->mode = _ws_panel_data->mode; + if (!ts->mode) + return -EINVAL; + +@@ -396,10 +512,9 @@ static int ws_panel_probe(struct i2c_cli + */ + drm_panel_add(&ts->base); + +- ts->dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | +- MIPI_DSI_CLOCK_NON_CONTINUOUS; ++ ts->dsi->mode_flags = _ws_panel_data->mode_flags; + ts->dsi->format = MIPI_DSI_FMT_RGB888; +- ts->dsi->lanes = 2; ++ ts->dsi->lanes = _ws_panel_data->lanes; + + ret = devm_mipi_dsi_attach(dev, ts->dsi); + +@@ -432,40 +547,46 @@ static void ws_panel_shutdown(struct i2c + static const struct of_device_id ws_panel_of_ids[] = { + { + .compatible = "waveshare,2.8inch-panel", +- .data = &ws_panel_2_8_mode, ++ .data = &ws_panel_2_8_data, + }, { + .compatible = "waveshare,3.4inch-panel", +- .data = &ws_panel_3_4_mode, ++ .data = &ws_panel_3_4_data, + }, { + .compatible = "waveshare,4.0inch-panel", +- .data = &ws_panel_4_0_mode, ++ .data = &ws_panel_4_0_data, + }, { + .compatible = "waveshare,7.0inch-c-panel", +- .data = &ws_panel_7_0_c_mode, ++ .data = &ws_panel_7_0_c_data, + }, { + .compatible = "waveshare,7.9inch-panel", +- .data = &ws_panel_7_9_mode, ++ .data = &ws_panel_7_9_data, + }, { + .compatible = "waveshare,8.0inch-panel", +- .data = &ws_panel_10_1_mode, ++ .data = &ws_panel_10_1_data, + }, { + .compatible = "waveshare,10.1inch-panel", +- .data = &ws_panel_10_1_mode, ++ .data = &ws_panel_10_1_data, + }, { + .compatible = "waveshare,11.9inch-panel", +- .data = &ws_panel_11_9_mode, ++ .data = &ws_panel_11_9_data, + }, { + .compatible = "waveshare,4inch-panel", +- .data = &ws_panel_4_mode, ++ .data = &ws_panel_4_data, + }, { + .compatible = "waveshare,5.0inch-panel", +- .data = &ws_panel_5_0_mode, ++ .data = &ws_panel_5_0_data, + }, { + .compatible = "waveshare,6.25inch-panel", +- .data = &ws_panel_6_25_mode, ++ .data = &ws_panel_6_25_data, + }, { + .compatible = "waveshare,8.8inch-panel", +- .data = &ws_panel_8_8_mode, ++ .data = &ws_panel_8_8_data, ++ }, { ++ .compatible = "waveshare,13.3inch-4lane-panel", ++ .data = &ws_panel_13_3_4lane_data, ++ }, { ++ .compatible = "waveshare,13.3inch-2lane-panel", ++ .data = &ws_panel_13_3_2lane_data, + }, { + /* sentinel */ + } diff --git a/target/linux/bcm27xx/patches-6.6/950-1444-arch-arm-boot-dts-overlays-Added-waveshare-13.3inch-.patch b/target/linux/bcm27xx/patches-6.6/950-1444-arch-arm-boot-dts-overlays-Added-waveshare-13.3inch-.patch new file mode 100644 index 0000000000..1d144bf2ca --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1444-arch-arm-boot-dts-overlays-Added-waveshare-13.3inch-.patch @@ -0,0 +1,46 @@ +From e442e5c1ab6bff5b5460b4fc949beb72aaf77970 Mon Sep 17 00:00:00 2001 +From: eng33 +Date: Thu, 5 Dec 2024 18:11:26 +0800 +Subject: [PATCH] arch:arm:boot:dts:overlays: Added waveshare 13.3inch panel + support + +Signed-off-by: eng33 +--- + arch/arm/boot/dts/overlays/README | 2 ++ + .../dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts | 7 +++++++ + 2 files changed, 9 insertions(+) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -5338,6 +5338,8 @@ Params: 2_8_inch 2.8" 480 + 8_0_inch 8.0" 1280x800 + 10_1_inch 10.1" 1280x800 + 11_9_inch 11.9" 320x1480 ++ 13_3_inch_4lane 13.3" 1920x1080 4lane ++ 13_3_inch_2lane 13.3" 1920x1080 2lane + i2c1 Use i2c-1 with jumper wires from GPIOs 2&3 + disable_touch Disable the touch controller + rotation Set the panel orientation property +--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts +@@ -51,6 +51,11 @@ + reg = <0x14>; + compatible = "goodix,gt911"; + }; ++ ++ touch2: ilitek@41 { ++ compatible = "ilitek,ili251x"; ++ reg = <0x41>; ++ }; + }; + }; + +@@ -120,6 +125,8 @@ + <&touch>, "touchscreen-inverted-x?", + <&touch>, "touchscreen-inverted-y?"; + 8_8_inch = <&panel>, "compatible=waveshare,8.8inch-panel"; ++ 13_3_inch_4lane = <&panel>, "compatible=waveshare,13.3inch-4lane-panel"; ++ 13_3_inch_2lane = <&panel>, "compatible=waveshare,13.3inch-2lane-panel"; + i2c1 = <&i2c_frag>, "target:0=",<&i2c1>, + <0>, "-3-4+5"; + disable_touch = <&touch>, "status=disabled"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1445-fixup-cgroup-Use-kernel-command-line-to-disable-memo.patch b/target/linux/bcm27xx/patches-6.6/950-1445-fixup-cgroup-Use-kernel-command-line-to-disable-memo.patch new file mode 100644 index 0000000000..8c4a1c3a3e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1445-fixup-cgroup-Use-kernel-command-line-to-disable-memo.patch @@ -0,0 +1,37 @@ +From 166dfc4399643681f2e4277bf7b7407e926861e5 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 9 Dec 2024 14:58:16 +0000 +Subject: [PATCH] fixup! cgroup: Use kernel command line to disable memory + cgroup + +cgroup features are distinct from cgroup subsystems - handle them +correctly. + +Signed-off-by: Phil Elwell +--- + kernel/cgroup/cgroup.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +--- a/kernel/cgroup/cgroup.c ++++ b/kernel/cgroup/cgroup.c +@@ -6769,11 +6769,19 @@ static int __init cgroup_enable(char *st + strcmp(token, ss->legacy_name)) + continue; + +- cgroup_feature_disable_mask &= ~(1 << i); + static_branch_enable(cgroup_subsys_enabled_key[i]); + pr_info("Enabling %s control group subsystem\n", + ss->name); + } ++ ++ for (i = 0; i < OPT_FEATURE_COUNT; i++) { ++ if (strcmp(token, cgroup_opt_feature_names[i])) ++ continue; ++ cgroup_feature_disable_mask &= ~(1 << i); ++ pr_info("Enabling %s control group feature\n", ++ cgroup_opt_feature_names[i]); ++ break; ++ } + } + return 1; + } diff --git a/target/linux/bcm27xx/patches-6.6/950-1446-media-i2c-ov9282-Correct-the-exposure-offset.patch b/target/linux/bcm27xx/patches-6.6/950-1446-media-i2c-ov9282-Correct-the-exposure-offset.patch new file mode 100644 index 0000000000..f673f547dd --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1446-media-i2c-ov9282-Correct-the-exposure-offset.patch @@ -0,0 +1,31 @@ +From e23afbf2c7aae9264322eee8e5c72ca1887606df Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 9 Dec 2024 10:43:18 +0000 +Subject: [PATCH] media: i2c: ov9282: Correct the exposure offset + +The datasheet lists that "Maximum exposure time is frame +length -25 row periods, where frame length is set by +registers {0x380E, 0x380F}". +However this driver had OV9282_EXPOSURE_OFFSET set to 12 +which allowed that restriction to be violated, and would +result in very under-exposed images. + +Correct the offset. + +Fixes: 14ea315bbeb7 ("media: i2c: Add ov9282 camera sensor driver") +Signed-off-by: Dave Stevenson +--- + drivers/media/i2c/ov9282.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/media/i2c/ov9282.c ++++ b/drivers/media/i2c/ov9282.c +@@ -40,7 +40,7 @@ + /* Exposure control */ + #define OV9282_REG_EXPOSURE 0x3500 + #define OV9282_EXPOSURE_MIN 1 +-#define OV9282_EXPOSURE_OFFSET 12 ++#define OV9282_EXPOSURE_OFFSET 25 + #define OV9282_EXPOSURE_STEP 1 + #define OV9282_EXPOSURE_DEFAULT 0x0282 + diff --git a/target/linux/bcm27xx/patches-6.6/950-1451-Revert-drm-vc4-hvs-Don-t-write-gamma-luts-on-2711.patch b/target/linux/bcm27xx/patches-6.6/950-1451-Revert-drm-vc4-hvs-Don-t-write-gamma-luts-on-2711.patch new file mode 100644 index 0000000000..e222ea886a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1451-Revert-drm-vc4-hvs-Don-t-write-gamma-luts-on-2711.patch @@ -0,0 +1,22 @@ +From 448a2db3990534810b45d3e4202df96ab2dc5815 Mon Sep 17 00:00:00 2001 +From: Dom Cobley +Date: Tue, 10 Dec 2024 15:28:28 +0000 +Subject: [PATCH] Revert "drm/vc4: hvs: Don't write gamma luts on 2711" + +This reverts commit 40c77e93cfdda320f47fc1a00a76ce466d20e976. +--- + drivers/gpu/drm/vc4/vc4_hvs.c | 3 --- + 1 file changed, 3 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hvs.c ++++ b/drivers/gpu/drm/vc4/vc4_hvs.c +@@ -521,9 +521,6 @@ static void vc4_hvs_lut_load(struct vc4_ + if (!drm_dev_enter(drm, &idx)) + return; + +- if (hvs->vc4->gen == VC4_GEN_5) +- return; +- + /* The LUT memory is laid out with each HVS channel in order, + * each of which takes 256 writes for R, 256 for G, then 256 + * for B. diff --git a/target/linux/bcm27xx/patches-6.6/950-1452-Revert-PCI-Warn-if-no-host-bridge-NUMA-node-info.patch b/target/linux/bcm27xx/patches-6.6/950-1452-Revert-PCI-Warn-if-no-host-bridge-NUMA-node-info.patch new file mode 100644 index 0000000000..5aa88661c3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1452-Revert-PCI-Warn-if-no-host-bridge-NUMA-node-info.patch @@ -0,0 +1,29 @@ +From 746662562995125ef7fb2c294300b0bd061b1251 Mon Sep 17 00:00:00 2001 +From: Dom Cobley +Date: Tue, 10 Dec 2024 16:39:31 +0000 +Subject: [PATCH] Revert "PCI: Warn if no host bridge NUMA node info" + +This warning doesn't mean anyting on our platform and +the warning causes confusion. + +See: https://forums.raspberrypi.com/viewtopic.php?p=2276125#p2276125 + +This reverts commit ad5086108b9f0361929aa9a79cf959ab5681d249. + +Signed-off-by: Dom Cobley +--- + drivers/pci/probe.c | 3 --- + 1 file changed, 3 deletions(-) + +--- a/drivers/pci/probe.c ++++ b/drivers/pci/probe.c +@@ -968,9 +968,6 @@ static int pci_register_host_bridge(stru + else + pr_info("PCI host bridge to bus %s\n", name); + +- if (nr_node_ids > 1 && pcibus_to_node(bus) == NUMA_NO_NODE) +- dev_warn(&bus->dev, "Unknown NUMA node; performance will be reduced\n"); +- + /* Coalesce contiguous windows */ + resource_list_for_each_entry_safe(window, n, &resources) { + if (list_is_last(&window->node, &resources)) diff --git a/target/linux/bcm27xx/patches-6.6/950-1454-drm-bridge-panel-Connector-to-allow-interlaced-modes.patch b/target/linux/bcm27xx/patches-6.6/950-1454-drm-bridge-panel-Connector-to-allow-interlaced-modes.patch new file mode 100644 index 0000000000..9e4ba2a75a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1454-drm-bridge-panel-Connector-to-allow-interlaced-modes.patch @@ -0,0 +1,25 @@ +From 7d294fbff4863e53a64685335b30aed9604cae49 Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +Date: Tue, 19 Nov 2024 16:11:32 +0000 +Subject: [PATCH] drm: bridge: panel: Connector to allow interlaced modes + +When initialized from panel_bridge_attach(), connector should +allow interlaced modes rather than invariably rejecting them, +so that other components can validate them. + +Signed-off-by: Nick Hollinghurst +--- + drivers/gpu/drm/bridge/panel.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/gpu/drm/bridge/panel.c ++++ b/drivers/gpu/drm/bridge/panel.c +@@ -82,6 +82,8 @@ static int panel_bridge_attach(struct dr + return ret; + } + ++ connector->interlace_allowed = true; ++ + drm_panel_bridge_set_orientation(connector, bridge); + + drm_connector_attach_encoder(&panel_bridge->connector, diff --git a/target/linux/bcm27xx/patches-6.6/950-1455-dts-overlays-vc4-kms-dpi-generic-overlay-Add-interla.patch b/target/linux/bcm27xx/patches-6.6/950-1455-dts-overlays-vc4-kms-dpi-generic-overlay-Add-interla.patch new file mode 100644 index 0000000000..1d9792305c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1455-dts-overlays-vc4-kms-dpi-generic-overlay-Add-interla.patch @@ -0,0 +1,34 @@ +From 2b0acbe8fd008e09a904b7a3c796a2dc79bf10ea Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +Date: Tue, 19 Nov 2024 16:17:40 +0000 +Subject: [PATCH] dts: overlays: vc4-kms-dpi-generic-overlay: Add "interlaced" + property + +Almost no DPI hardware supports it, but it's useful for RP1 video out. + +Signed-off-by: Nick Hollinghurst +--- + arch/arm/boot/dts/overlays/README | 1 + + arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts | 1 + + 2 files changed, 2 insertions(+) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -5099,6 +5099,7 @@ Params: clock-frequency Display + vsync-invert Vertical sync active low + de-invert Data Enable active low + pixclk-invert Negative edge pixel clock ++ interlaced Use an interlaced mode (where supported) + width-mm Define the screen width in mm + height-mm Define the screen height in mm + rgb565 Change to RGB565 output on GPIOs 0-19 +--- a/arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts +@@ -59,6 +59,7 @@ + vsync-invert = <&timing>, "vsync-active:0=0"; + de-invert = <&timing>, "de-active:0=0"; + pixclk-invert = <&timing>, "pixelclk-active:0=0"; ++ interlaced = <&timing>, "interlaced?"; + + width-mm = <&panel_generic>, "width-mm:0"; + height-mm = <&panel_generic>, "height-mm:0"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1456-drm-rp1-rp1-dpi-Add-interlaced-modes-and-PIO-program.patch b/target/linux/bcm27xx/patches-6.6/950-1456-drm-rp1-rp1-dpi-Add-interlaced-modes-and-PIO-program.patch index bbaee270c5..e78ee378a4 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1456-drm-rp1-rp1-dpi-Add-interlaced-modes-and-PIO-program.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1456-drm-rp1-rp1-dpi-Add-interlaced-modes-and-PIO-program.patch @@ -1,8 +1,8 @@ From 7735dd0736322cff23aff95490bae1d69937a9bf Mon Sep 17 00:00:00 2001 From: Nick Hollinghurst Date: Tue, 10 Dec 2024 13:23:09 +0000 -Subject: [PATCH 1456/1482] drm: rp1: rp1-dpi: Add interlaced modes and PIO - program to fix VSYNC +Subject: [PATCH] drm: rp1: rp1-dpi: Add interlaced modes and PIO program to + fix VSYNC Implement interlaced modes by wobbling the base pointer and VFP width for every field. This results in correct pixels but incorrect VSYNC. diff --git a/target/linux/bcm27xx/patches-6.6/950-1457-pwm-Improve-PWM_PIO_RP1-dependencies.patch b/target/linux/bcm27xx/patches-6.6/950-1457-pwm-Improve-PWM_PIO_RP1-dependencies.patch deleted file mode 100644 index 357cd05dc8..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-1457-pwm-Improve-PWM_PIO_RP1-dependencies.patch +++ /dev/null @@ -1,22 +0,0 @@ -From f85f3509692f966ec32e4db499f7e64dc6b6b952 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Thu, 12 Dec 2024 10:09:13 +0000 -Subject: [PATCH 1457/1482] pwm: Improve PWM_PIO_RP1 dependencies - -PWM_PIO_RP1 should select RP1_PIO, as it is useless without it. - -Signed-off-by: Phil Elwell ---- - drivers/pwm/Kconfig | 1 + - 1 file changed, 1 insertion(+) - ---- a/drivers/pwm/Kconfig -+++ b/drivers/pwm/Kconfig -@@ -457,6 +457,7 @@ config PWM_PCA9685 - config PWM_PIO_RP1 - tristate "RP1 PIO PWM support" - depends on FIRMWARE_RP1 || COMPILE_TEST -+ select RP1_PIO - help - This is a PWM framework driver for Raspberry Pi 5, using the PIO - hardware of RP1 to provide PWM functionality. Supports up to 4 diff --git a/target/linux/bcm27xx/patches-6.6/950-1458-Revert-pwm-Improve-PWM_PIO_RP1-dependencies.patch b/target/linux/bcm27xx/patches-6.6/950-1458-Revert-pwm-Improve-PWM_PIO_RP1-dependencies.patch deleted file mode 100644 index eabf3887d1..0000000000 --- a/target/linux/bcm27xx/patches-6.6/950-1458-Revert-pwm-Improve-PWM_PIO_RP1-dependencies.patch +++ /dev/null @@ -1,20 +0,0 @@ -From 73fb1e979a210094935f4af4c3d6e700fba30c5f Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Thu, 12 Dec 2024 10:28:54 +0000 -Subject: [PATCH 1458/1482] Revert "pwm: Improve PWM_PIO_RP1 dependencies" - -This reverts commit f85f3509692f966ec32e4db499f7e64dc6b6b952. ---- - drivers/pwm/Kconfig | 1 - - 1 file changed, 1 deletion(-) - ---- a/drivers/pwm/Kconfig -+++ b/drivers/pwm/Kconfig -@@ -457,7 +457,6 @@ config PWM_PCA9685 - config PWM_PIO_RP1 - tristate "RP1 PIO PWM support" - depends on FIRMWARE_RP1 || COMPILE_TEST -- select RP1_PIO - help - This is a PWM framework driver for Raspberry Pi 5, using the PIO - hardware of RP1 to provide PWM functionality. Supports up to 4 diff --git a/target/linux/bcm27xx/patches-6.6/950-1459-ASoC-allo-piano-dac-plus-Fix-volume-limit-locking.patch b/target/linux/bcm27xx/patches-6.6/950-1459-ASoC-allo-piano-dac-plus-Fix-volume-limit-locking.patch new file mode 100644 index 0000000000..8e279df688 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1459-ASoC-allo-piano-dac-plus-Fix-volume-limit-locking.patch @@ -0,0 +1,79 @@ +From ac0cd73932aa1e371ffaf0b974855ed3cd22937f Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 11 Dec 2024 13:47:30 +0000 +Subject: [PATCH] ASoC: allo-piano-dac-plus: Fix volume limit locking + +Calling snd_soc_limit_volume from within a kcontrol put handler seems +to cause a deadlock as it attempts to claim a write lock that is already +held. Call snd_soc_limit_volume from the main initialisation code +instead, to avoid the recursive locking. + +See: https://github.com/raspberrypi/linux/issues/6527 + +Signed-off-by: Phil Elwell +--- + sound/soc/bcm/allo-piano-dac-plus.c | 32 +++++++++++++---------------- + 1 file changed, 14 insertions(+), 18 deletions(-) + +--- a/sound/soc/bcm/allo-piano-dac-plus.c ++++ b/sound/soc/bcm/allo-piano-dac-plus.c +@@ -452,14 +452,6 @@ static int pcm512x_set_reg_sub(struct sn + + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); + +- if (digital_gain_0db_limit) { +- ret = snd_soc_limit_volume(card, "Subwoofer Playback Volume", +- 207); +- if (ret < 0) +- dev_warn(card->dev, "Failed to set volume limit: %d\n", +- ret); +- } +- + // When in Dual Mono, Sub vol control should not set anything. + if (glb_ptr->dual_mode != 1) { //Not in Dual Mono mode + +@@ -562,14 +554,6 @@ static int pcm512x_set_reg_master(struct + + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); + +- if (digital_gain_0db_limit) { +- ret = snd_soc_limit_volume(card, "Master Playback Volume", +- 207); +- if (ret < 0) +- dev_warn(card->dev, "Failed to set volume limit: %d\n", +- ret); +- } +- + if (glb_ptr->dual_mode == 1) { //in Dual Mono Mode + + ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component, +@@ -750,6 +734,18 @@ static int snd_allo_piano_dac_init(struc + if (digital_gain_0db_limit) { + int ret; + ++ ret = snd_soc_limit_volume(card, "Master Playback Volume", ++ 207); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to set master volume limit: %d\n", ++ ret); ++ ++ ret = snd_soc_limit_volume(card, "Subwoofer Playback Volume", ++ 207); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to set subwoofer volume limit: %d\n", ++ ret); ++ + //Set volume limit on both dacs + for (i = 0; i < ARRAY_SIZE(codec_ctl_pfx); i++) { + char cname[256]; +@@ -757,8 +753,8 @@ static int snd_allo_piano_dac_init(struc + sprintf(cname, "%s %s", codec_ctl_pfx[i], codec_ctl_name[0]); + ret = snd_soc_limit_volume(card, cname, 207); + if (ret < 0) +- dev_warn(card->dev, "Failed to set volume limit: %d\n", +- ret); ++ dev_warn(card->dev, "Failed to set %s volume limit: %d\n", ++ cname, ret); + } + } + diff --git a/target/linux/bcm27xx/patches-6.6/950-1460-drm-vc4-txp-Do-not-allow-24bpp-formats-when-transpos.patch b/target/linux/bcm27xx/patches-6.6/950-1460-drm-vc4-txp-Do-not-allow-24bpp-formats-when-transpos.patch new file mode 100644 index 0000000000..93de0f99a4 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1460-drm-vc4-txp-Do-not-allow-24bpp-formats-when-transpos.patch @@ -0,0 +1,30 @@ +From af4ab4fb77dfc697c8ae068b18f27de1ee5d609f Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 11 Dec 2024 16:30:43 +0000 +Subject: [PATCH] drm: vc4: txp: Do not allow 24bpp formats when transposing + +The hardware doesn't support transposing to 24bpp (RGB888/BGR888) +formats. There's no way to advertise this through DRM, so block +it from atomic_check instead. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/vc4/vc4_txp.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/gpu/drm/vc4/vc4_txp.c ++++ b/drivers/gpu/drm/vc4/vc4_txp.c +@@ -272,6 +272,13 @@ static int vc4_txp_connector_atomic_chec + return -EINVAL; + } + ++ if (conn_state->rotation & DRM_MODE_TRANSPOSE && ++ (fb->format->format == DRM_FORMAT_RGB888 || ++ fb->format->format == DRM_FORMAT_BGR888)) { ++ DRM_DEBUG_KMS("24bpp formats not supported when transposing\n"); ++ return -EINVAL; ++ } ++ + for (i = 0; i < ARRAY_SIZE(drm_fmts); i++) { + if (fb->format->format == drm_fmts[i]) + break; diff --git a/target/linux/bcm27xx/patches-6.6/950-1461-drm-Validate-connector-rotation-has-one-bit-set-in-t.patch b/target/linux/bcm27xx/patches-6.6/950-1461-drm-Validate-connector-rotation-has-one-bit-set-in-t.patch new file mode 100644 index 0000000000..2a6115981a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1461-drm-Validate-connector-rotation-has-one-bit-set-in-t.patch @@ -0,0 +1,29 @@ +From 0b216b3988e5b7035cd5ed8a9910eacbb3420ce0 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Thu, 12 Dec 2024 11:59:52 +0000 +Subject: [PATCH] drm: Validate connector rotation has one bit set in the + rotation property + +Copy the same validation logic as from the plane rotation property. + +Fixes: 8fec3ff87049 ("drm: Add a rotation parameter to connectors.") +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/drm_atomic_uapi.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/gpu/drm/drm_atomic_uapi.c ++++ b/drivers/gpu/drm/drm_atomic_uapi.c +@@ -812,6 +812,12 @@ static int drm_atomic_connector_set_prop + } else if (property == connector->privacy_screen_sw_state_property) { + state->privacy_screen_sw_state = val; + } else if (property == connector->rotation_property) { ++ if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK)) { ++ drm_dbg_atomic(connector->dev, ++ "[CONNECTOR:%d:%s] bad rotation bitmask: 0x%llx\n", ++ connector->base.id, connector->name, val); ++ return -EINVAL; ++ } + state->rotation = val; + } else if (connector->funcs->atomic_set_property) { + return connector->funcs->atomic_set_property(connector, diff --git a/target/linux/bcm27xx/patches-6.6/950-1462-ASoC-allo-piano-dac-plus-Suppress-517-errors.patch b/target/linux/bcm27xx/patches-6.6/950-1462-ASoC-allo-piano-dac-plus-Suppress-517-errors.patch new file mode 100644 index 0000000000..67bce02817 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1462-ASoC-allo-piano-dac-plus-Suppress-517-errors.patch @@ -0,0 +1,73 @@ +From 61494a7aa2ea887fa1cd1399a8db1317c87f661b Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 12 Dec 2024 13:05:41 +0000 +Subject: [PATCH] ASoC: allo-piano-dac-plus: Suppress -517 errors + +Use dev_err_probe to simplify the code and suppress EPROBE_DEFER errors. + +Signed-off-by: Phil Elwell +--- + sound/soc/bcm/allo-piano-dac-plus.c | 37 ++++++++--------------------- + 1 file changed, 10 insertions(+), 27 deletions(-) + +--- a/sound/soc/bcm/allo-piano-dac-plus.c ++++ b/sound/soc/bcm/allo-piano-dac-plus.c +@@ -974,48 +974,31 @@ static int snd_allo_piano_dac_probe(stru + + allo_piano_2_1_codecs[0].of_node = + of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); +- if (!allo_piano_2_1_codecs[0].of_node) { +- dev_err(&pdev->dev, +- "Property 'audio-codec' missing or invalid\n"); +- return -EINVAL; +- } +- + allo_piano_2_1_codecs[1].of_node = + of_parse_phandle(pdev->dev.of_node, "audio-codec", 1); +- if (!allo_piano_2_1_codecs[1].of_node) { +- dev_err(&pdev->dev, ++ if (!allo_piano_2_1_codecs[0].of_node || !allo_piano_2_1_codecs[1].of_node) ++ return dev_err_probe(&pdev->dev, -EINVAL, + "Property 'audio-codec' missing or invalid\n"); +- return -EINVAL; +- } + + mute_gpio[0] = devm_gpiod_get_optional(&pdev->dev, "mute1", + GPIOD_OUT_LOW); +- if (IS_ERR(mute_gpio[0])) { +- ret = PTR_ERR(mute_gpio[0]); +- dev_err(&pdev->dev, +- "failed to get mute1 gpio6: %d\n", ret); +- return ret; +- } ++ if (IS_ERR(mute_gpio[0])) ++ return dev_err_probe(&pdev->dev, PTR_ERR(mute_gpio[0]), ++ "failed to get mute1 gpio\n"); + + mute_gpio[1] = devm_gpiod_get_optional(&pdev->dev, "mute2", + GPIOD_OUT_LOW); +- if (IS_ERR(mute_gpio[1])) { +- ret = PTR_ERR(mute_gpio[1]); +- dev_err(&pdev->dev, +- "failed to get mute2 gpio25: %d\n", ret); +- return ret; +- } ++ if (IS_ERR(mute_gpio[1])) ++ return dev_err_probe(&pdev->dev, PTR_ERR(mute_gpio[1]), ++ "failed to get mute2 gpio\n"); + + if (mute_gpio[0] && mute_gpio[1]) + snd_allo_piano_dac.set_bias_level = + snd_allo_piano_set_bias_level; + + ret = snd_soc_register_card(&snd_allo_piano_dac); +- if (ret < 0) { +- dev_err(&pdev->dev, +- "snd_soc_register_card() failed: %d\n", ret); +- return ret; +- } ++ if (ret < 0) ++ return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); + + if ((mute_gpio[0]) && (mute_gpio[1])) + snd_allo_piano_gpio_mute(&snd_allo_piano_dac); diff --git a/target/linux/bcm27xx/patches-6.6/950-1463-drm-rp1-rp1-dpi-Fix-optional-dependency-on-RP1_PIO.patch b/target/linux/bcm27xx/patches-6.6/950-1463-drm-rp1-rp1-dpi-Fix-optional-dependency-on-RP1_PIO.patch index 52e87daa38..13c735eb4e 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1463-drm-rp1-rp1-dpi-Fix-optional-dependency-on-RP1_PIO.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1463-drm-rp1-rp1-dpi-Fix-optional-dependency-on-RP1_PIO.patch @@ -1,8 +1,7 @@ From 80533a952218696c0ef1b346bab50dc401e6b74c Mon Sep 17 00:00:00 2001 From: Nick Hollinghurst Date: Thu, 12 Dec 2024 11:58:12 +0000 -Subject: [PATCH 1463/1482] drm: rp1: rp1-dpi: Fix optional dependency on - RP1_PIO +Subject: [PATCH] drm: rp1: rp1-dpi: Fix optional dependency on RP1_PIO Add optional dependency to Kconfig, and conditionally compile PIO-dependent code. Add a mode validation function to reject diff --git a/target/linux/bcm27xx/patches-6.6/950-1464-serial-sc16is7xx-announce-support-for-SER_RS485_RTS_.patch b/target/linux/bcm27xx/patches-6.6/950-1464-serial-sc16is7xx-announce-support-for-SER_RS485_RTS_.patch new file mode 100644 index 0000000000..f78402d0c5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1464-serial-sc16is7xx-announce-support-for-SER_RS485_RTS_.patch @@ -0,0 +1,42 @@ +From 694247173f2e136196d7cb3a392c84cda65674d2 Mon Sep 17 00:00:00 2001 +From: Hugo Villeneuve +Date: Mon, 7 Oct 2024 12:27:15 -0400 +Subject: [PATCH] serial: sc16is7xx: announce support for SER_RS485_RTS_ON_SEND + +commit 068d35a7be65fa3bca4bba21c269bfe0b39158a6 upstream. + +When specifying flag SER_RS485_RTS_ON_SEND in RS485 configuration, +we get the following warning after commit 4afeced55baa ("serial: core: +fix sanitizing check for RTS settings"): + + invalid RTS setting, using RTS_AFTER_SEND instead + +This results in SER_RS485_RTS_AFTER_SEND being set and the +driver always write to the register field SC16IS7XX_EFCR_RTS_INVERT_BIT, +which breaks some hardware using these chips. + +The hardware supports both RTS_ON_SEND and RTS_AFTER_SEND modes, so fix +this by announcing support for RTS_ON_SEND. + +Signed-off-by: Hugo Villeneuve +Suggested-by: Konstantin Pugin +Link: https://lore.kernel.org/lkml/20240422133219.2710061-2-ria.freelander@gmail.com +Reviewed-by: Andy Shevchenko +Tested-by: Hugo Villeneuve +Link: https://lore.kernel.org/r/20241007162716.3122912-1-hugo@hugovil.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/tty/serial/sc16is7xx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/tty/serial/sc16is7xx.c ++++ b/drivers/tty/serial/sc16is7xx.c +@@ -1457,7 +1457,7 @@ static int sc16is7xx_setup_mctrl_ports(s + } + + static const struct serial_rs485 sc16is7xx_rs485_supported = { +- .flags = SER_RS485_ENABLED | SER_RS485_RTS_AFTER_SEND, ++ .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, /* Not supported but keep returning -EINVAL */ + }; diff --git a/target/linux/bcm27xx/patches-6.6/950-1467-dtoverlays-Add-override-for-target-path-on-I2C-overl.patch b/target/linux/bcm27xx/patches-6.6/950-1467-dtoverlays-Add-override-for-target-path-on-I2C-overl.patch new file mode 100644 index 0000000000..d5f72d1c92 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1467-dtoverlays-Add-override-for-target-path-on-I2C-overl.patch @@ -0,0 +1,550 @@ +From b75fd2a9385e1358fa82218184e73513f9a5e57f Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 16 Dec 2024 15:11:08 +0000 +Subject: [PATCH] dtoverlays: Add override for target-path on I2C overlays + +To allow for attaching any of the standard overlays to a +bitbashed i2c-gpio bus, allow specifying the target path for +the overlay. + +Suggested by: +https://forums.raspberrypi.com/viewtopic.php?t=381059 + +Example: +dtoverlay=i2c-gpio,i2c_gpio_sda=10,i2c_gpio_scl=11 +dtoverlay=mcp23017,i2c-path=/i2c@0 +dtoverlay=i2c-gpio,i2c_gpio_sda=12,i2c_gpio_scl=13,bus=3 +dtoverlay=mcp23017,i2c-path=/i2c@3 + +Signed-off-by: Dave Stevenson +--- + arch/arm/boot/dts/overlays/README | 59 +++++++++++++++++++ + .../arm/boot/dts/overlays/ads1115-overlay.dts | 2 + + .../boot/dts/overlays/edt-ft5406-overlay.dts | 3 + + arch/arm/boot/dts/overlays/goodix-overlay.dts | 4 +- + .../dts/overlays/hd44780-i2c-lcd-overlay.dts | 4 +- + .../arm/boot/dts/overlays/i2c-fan-overlay.dts | 2 + + .../arm/boot/dts/overlays/i2c-mux-overlay.dts | 2 + + .../dts/overlays/i2c-pwm-pca9685a-overlay.dts | 2 + + .../arm/boot/dts/overlays/i2c-rtc-overlay.dts | 2 + + .../boot/dts/overlays/i2c-sensor-overlay.dts | 2 + + .../boot/dts/overlays/ilitek251x-overlay.dts | 4 +- + .../boot/dts/overlays/mcp23017-overlay.dts | 2 + + .../arm/boot/dts/overlays/pca953x-overlay.dts | 30 +++++++++- + .../arm/boot/dts/overlays/pcf857x-overlay.dts | 30 +++++++++- + .../dts/overlays/sc16is750-i2c-overlay.dts | 30 +++++++++- + .../dts/overlays/sc16is752-i2c-overlay.dts | 30 +++++++++- + 16 files changed, 201 insertions(+), 7 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -555,6 +555,7 @@ Params: addr I2C bus + overlay - BCM2711 only) + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + Channel parameters can be set for each enabled channel. + A maximum of 4 channels can be enabled (letters a thru d). +@@ -1238,6 +1239,7 @@ Params: sizex Touchscr + addr Sets the address for the touch controller. Note + that the device must be configured to use the + specified address. ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: enc28j60 +@@ -1439,6 +1441,7 @@ Info: Enables I2C connected Goodix gt9 + Load: dtoverlay=goodix,= + Params: interrupt GPIO used for interrupt (default 4) + reset GPIO used for reset (default 17) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: googlevoicehat-soundcard +@@ -1730,6 +1733,7 @@ Params: addr I2C addr + display_height Height of the display in characters (default 2) + + display_width Width of the display in characters (default 16) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: hd44780-lcd +@@ -2095,6 +2099,8 @@ Params: addr Sets the + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) + ++ i2c-path Override I2C path to allow for i2c-gpio buses ++ + minpwm PWM setting for the fan when the SoC is below + mintemp (range 0-255. default 0) + maxpwm PWM setting for the fan when the SoC is above +@@ -2165,6 +2171,8 @@ Params: pca9542 Select t + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) + ++ i2c-path Override I2C path to allow for i2c-gpio buses ++ + disconnect_on_idle Force the mux to disconnect all child buses + after every transaction. + +@@ -2186,6 +2194,7 @@ Params: addr I2C addr + overlay - BCM2711 only) + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: i2c-rtc +@@ -2255,6 +2264,8 @@ Params: abx80x Select o + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) + ++ i2c-path Override I2C path to allow for i2c-gpio buses ++ + addr Sets the address for the RTC. Note that the + device must be configured to use the specified + address. +@@ -2519,6 +2530,8 @@ Params: addr Set the + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) + ++ i2c-path Override I2C path to allow for i2c-gpio buses ++ + + Name: i2c0 + Info: Change i2c0 pin usage. Not all pin combinations are usable on all +@@ -2661,6 +2674,7 @@ Params: interrupt GPIO use + touchscreen (in pixels) + sizey Touchscreen size y, vertical resolution of + touchscreen (in pixels) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: imx219 +@@ -3138,6 +3152,7 @@ Params: gpiopin Gpio pin + overlay - BCM2711 only) + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: mcp23s17 +@@ -3587,6 +3602,17 @@ Params: addr I2C addr + cat9554 Select the Onnn CAT9554 (8 bit) + pca9654 Select the Onnn PCA9654 (8 bit) + xra1202 Select the Exar XRA1202 (8 bit) ++ i2c0 Choose the I2C0 bus on GPIOs 0&1 ++ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c3 Choose the I2C3 bus (configure with the i2c3 ++ overlay - BCM2711 only) ++ i2c4 Choose the I2C3 bus (configure with the i2c3 ++ overlay - BCM2711 only) ++ i2c5 Choose the I2C5 bus (configure with the i2c4 ++ overlay - BCM2711 only) ++ i2c6 Choose the I2C6 bus (configure with the i2c6 ++ overlay - BCM2711 only) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: pcf857x +@@ -3598,6 +3624,17 @@ Params: addr I2C addr + pcf8574a Select the NXP PCF8574A (8 bit) + pcf8575 Select the NXP PCF8575 (16 bit) + pca8574 Select the NXP PCA8574 (8 bit) ++ i2c0 Choose the I2C0 bus on GPIOs 0&1 ++ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c3 Choose the I2C3 bus (configure with the i2c3 ++ overlay - BCM2711 only) ++ i2c4 Choose the I2C3 bus (configure with the i2c3 ++ overlay - BCM2711 only) ++ i2c5 Choose the I2C5 bus (configure with the i2c4 ++ overlay - BCM2711 only) ++ i2c6 Choose the I2C6 bus (configure with the i2c6 ++ overlay - BCM2711 only) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: pcie-32bit-dma +@@ -4257,6 +4294,17 @@ Load: dtoverlay=sc16is750-i2c,= + Params: int_pin GPIO used for IRQ (default 24) + addr Address (default 0x48) + xtal On-board crystal frequency (default 14745600) ++ i2c0 Choose the I2C0 bus on GPIOs 0&1 ++ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c3 Choose the I2C3 bus (configure with the i2c3 ++ overlay - BCM2711 only) ++ i2c4 Choose the I2C4 bus (configure with the i2c4 ++ overlay - BCM2711 only) ++ i2c5 Choose the I2C5 bus (configure with the i2c5 ++ overlay - BCM2711 only) ++ i2c6 Choose the I2C6 bus (configure with the i2c6 ++ overlay - BCM2711 only) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: sc16is750-spi0 +@@ -4275,6 +4323,17 @@ Load: dtoverlay=sc16is752-i2c,= + Params: int_pin GPIO used for IRQ (default 24) + addr Address (default 0x48) + xtal On-board crystal frequency (default 14745600) ++ i2c0 Choose the I2C0 bus on GPIOs 0&1 ++ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c3 Choose the I2C3 bus (configure with the i2c3 ++ overlay - BCM2711 only) ++ i2c4 Choose the I2C4 bus (configure with the i2c4 ++ overlay - BCM2711 only) ++ i2c5 Choose the I2C5 bus (configure with the i2c5 ++ overlay - BCM2711 only) ++ i2c6 Choose the I2C6 bus (configure with the i2c6 ++ overlay - BCM2711 only) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: sc16is752-spi0 +--- a/arch/arm/boot/dts/overlays/ads1115-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ads1115-overlay.dts +@@ -131,5 +131,7 @@ + <&frag100>, "target-path=i2c5"; + i2c6 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c6"; ++ i2c-path = <&frag100>, "target?=0", ++ <&frag100>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts ++++ b/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts +@@ -41,6 +41,9 @@ + i2c6 = <&ts_i2c_frag>, "target?=0", + <&ts_i2c_frag>, "target-path=i2c6", + <0>,"-0-1"; ++ i2c-path = <&ts_i2c_frag>, "target?=0", ++ <&ts_i2c_frag>, "target-path", ++ <0>,"-0-1"; + addr = <&ft5406>,"reg:0"; + }; + }; +--- a/arch/arm/boot/dts/overlays/goodix-overlay.dts ++++ b/arch/arm/boot/dts/overlays/goodix-overlay.dts +@@ -16,7 +16,7 @@ + }; + }; + +- fragment@1 { ++ i2c_frag: fragment@1 { + target = <&i2c1>; + __overlay__ { + #address-cells = <1>; +@@ -42,5 +42,7 @@ + <>9271>,"irq-gpios:4"; + reset = <&goodix_pins>,"brcm,pins:4", + <>9271>,"reset-gpios:4"; ++ i2c-path = <&i2c_frag>, "target?=0", ++ <&i2c_frag>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/hd44780-i2c-lcd-overlay.dts ++++ b/arch/arm/boot/dts/overlays/hd44780-i2c-lcd-overlay.dts +@@ -4,7 +4,7 @@ + / { + compatible = "brcm,bcm2835"; + +- fragment@0 { ++ i2c_frag: fragment@0 { + target = <&i2c_arm>; + __overlay__ { + status = "okay"; +@@ -52,6 +52,8 @@ + display_height = <&lcd_screen>,"display-height-chars:0"; + display_width = <&lcd_screen>,"display-width-chars:0"; + addr = <&pcf857x>,"reg:0"; ++ i2c-path = <&i2c_frag>, "target?=0", ++ <&i2c_frag>, "target-path"; + }; + + }; +--- a/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts +@@ -93,6 +93,8 @@ + <&frag100>, "target-path=i2c5"; + i2c6 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c6"; ++ i2c-path = <&frag100>, "target?=0", ++ <&frag100>, "target-path"; + addr = <&emc2301>,"reg:0"; + minpwm = <&emc2301>,"emc2305,pwm-min.0"; + maxpwm = <&emc2301>,"emc2305,pwm-max.0"; +--- a/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts +@@ -175,6 +175,8 @@ + <&frag100>, "target-path=i2c5"; + i2c6 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c6"; ++ i2c-path = <&frag100>, "target?=0", ++ <&frag100>, "target-path"; + disconnect_on_idle = + <&pca9542>,"idle-state:0=", , + <&pca9545>,"idle-state:0=", , +--- a/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts +@@ -57,5 +57,7 @@ + <&frag100>, "target-path=i2c5"; + i2c6 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c6"; ++ i2c-path = <&frag100>, "target?=0", ++ <&frag100>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts +@@ -38,5 +38,7 @@ + <&frag100>, "target-path=i2c5"; + i2c6 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c6"; ++ i2c-path = <&frag100>, "target?=0", ++ <&frag100>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts +@@ -38,5 +38,7 @@ + <&frag100>, "target-path=i2c5"; + i2c6 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c6"; ++ i2c-path = <&frag100>, "target?=0", ++ <&frag100>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/ilitek251x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ilitek251x-overlay.dts +@@ -16,7 +16,7 @@ + }; + }; + +- fragment@1 { ++ frag1: fragment@1 { + target = <&i2c1>; + __overlay__ { + #address-cells = <1>; +@@ -41,5 +41,7 @@ + <&ili251x>,"interrupts:0"; + sizex = <&ili251x>,"touchscreen-size-x:0"; + sizey = <&ili251x>,"touchscreen-size-y:0"; ++ i2c-path = <&frag1>, "target?=0", ++ <&frag1>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/mcp23017-overlay.dts ++++ b/arch/arm/boot/dts/overlays/mcp23017-overlay.dts +@@ -98,6 +98,8 @@ + <&frag100>, "target-path=i2c5"; + i2c6 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c6"; ++ i2c-path = <&frag100>, "target?=0", ++ <&frag100>, "target-path"; + }; + }; + +--- a/arch/arm/boot/dts/overlays/pca953x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pca953x-overlay.dts +@@ -5,7 +5,7 @@ + /{ + compatible = "brcm,bcm2835"; + +- fragment@0 { ++ frag0: fragment@0 { + target = <&i2c_arm>; + __overlay__ { + #address-cells = <1>; +@@ -204,6 +204,20 @@ + }; + }; + ++ fragment@100 { ++ target = <&i2c0if>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@101 { ++ target = <&i2c0mux>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ + __overrides__ { + addr = <&pca>,"reg:0"; + pca6416 = <0>, "+1"; +@@ -236,5 +250,19 @@ + cat9554 = <0>, "+28"; + pca9654 = <0>, "+29"; + xra1202 = <0>, "+30"; ++ i2c0 = <&frag0>, "target:0=",<&i2c0>, ++ <0>,"+100+101"; ++ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, ++ <0>,"+100+101"; ++ i2c3 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c3"; ++ i2c4 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c4"; ++ i2c5 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c5"; ++ i2c6 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c6"; ++ i2c-path = <&frag0>, "target?=0", ++ <&frag0>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/pcf857x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pcf857x-overlay.dts +@@ -6,7 +6,7 @@ + / { + compatible = "brcm,bcm2835"; + +- fragment@0 { ++ frag0: fragment@0 { + target = <&i2c_arm>; + __overlay__ { + #address-cells = <1>; +@@ -22,11 +22,39 @@ + }; + }; + ++ fragment@100 { ++ target = <&i2c0if>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@101 { ++ target = <&i2c0mux>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ + __overrides__ { + pcf8574 = <&pcf857x>,"compatible=nxp,pcf8574", <&pcf857x>,"reg:0=0x20"; + pcf8574a = <&pcf857x>,"compatible=nxp,pcf8574a", <&pcf857x>,"reg:0=0x38"; + pcf8575 = <&pcf857x>,"compatible=nxp,pcf8575", <&pcf857x>,"reg:0=0x20"; + pca8574 = <&pcf857x>,"compatible=nxp,pca8574", <&pcf857x>,"reg:0=0x20"; + addr = <&pcf857x>,"reg:0"; ++ i2c0 = <&frag0>, "target:0=",<&i2c0>, ++ <0>,"+100+101"; ++ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, ++ <0>,"+100+101"; ++ i2c3 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c3"; ++ i2c4 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c4"; ++ i2c5 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c5"; ++ i2c6 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c6"; ++ i2c-path = <&frag0>, "target?=0", ++ <&frag0>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts +@@ -4,7 +4,7 @@ + / { + compatible = "brcm,bcm2835"; + +- fragment@0 { ++ frag0: fragment@0 { + target = <&i2c_arm>; + __overlay__ { + #address-cells = <1>; +@@ -48,10 +48,38 @@ + }; + }; + ++ fragment@100 { ++ target = <&i2c0if>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@101 { ++ target = <&i2c0mux>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ + __overrides__ { + int_pin = <&sc16is750>,"interrupts:0", <&int_pins>,"brcm,pins:0", + <&int_pins>,"reg:0"; + addr = <&sc16is750>,"reg:0", <&sc16is750_clk>,"name"; + xtal = <&sc16is750_clk>,"clock-frequency:0"; ++ i2c0 = <&frag0>, "target:0=",<&i2c0>, ++ <0>,"+100+101"; ++ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, ++ <0>,"+100+101"; ++ i2c3 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c3"; ++ i2c4 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c4"; ++ i2c5 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c5"; ++ i2c6 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c6"; ++ i2c-path = <&frag0>, "target?=0", ++ <&frag0>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts +@@ -4,7 +4,7 @@ + / { + compatible = "brcm,bcm2835"; + +- fragment@0 { ++ frag0: fragment@0 { + target = <&i2c_arm>; + __overlay__ { + #address-cells = <1>; +@@ -48,10 +48,38 @@ + }; + }; + ++ fragment@100 { ++ target = <&i2c0if>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@101 { ++ target = <&i2c0mux>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ + __overrides__ { + int_pin = <&sc16is752>,"interrupts:0", <&int_pins>,"brcm,pins:0", + <&int_pins>,"reg:0"; + addr = <&sc16is752>,"reg:0",<&sc16is752_clk>,"name"; + xtal = <&sc16is752_clk>,"clock-frequency:0"; ++ i2c0 = <&frag0>, "target:0=",<&i2c0>, ++ <0>,"+100+101"; ++ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, ++ <0>,"+100+101"; ++ i2c3 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c3"; ++ i2c4 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c4"; ++ i2c5 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c5"; ++ i2c6 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c6"; ++ i2c-path = <&frag0>, "target?=0", ++ <&frag0>, "target-path"; + }; + }; diff --git a/target/linux/bcm27xx/patches-6.6/950-1468-misc-rp1-pio-Support-larger-data-transfers.patch b/target/linux/bcm27xx/patches-6.6/950-1468-misc-rp1-pio-Support-larger-data-transfers.patch index 828efdb79b..3183fb580e 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1468-misc-rp1-pio-Support-larger-data-transfers.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1468-misc-rp1-pio-Support-larger-data-transfers.patch @@ -1,7 +1,7 @@ From 4b0ca96738bb937529655a0062d60775f47b0f5e Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Mon, 16 Dec 2024 23:01:41 +0000 -Subject: [PATCH 1468/1482] misc: rp1-pio: Support larger data transfers +Subject: [PATCH] misc: rp1-pio: Support larger data transfers Add a separate IOCTL for larger transfer with a 32-bit data_bytes field. diff --git a/target/linux/bcm27xx/patches-6.6/950-1469-dtoverlays-Use-continuous-clock-mode-for-ov9281.patch b/target/linux/bcm27xx/patches-6.6/950-1469-dtoverlays-Use-continuous-clock-mode-for-ov9281.patch new file mode 100644 index 0000000000..c59e72eb13 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1469-dtoverlays-Use-continuous-clock-mode-for-ov9281.patch @@ -0,0 +1,34 @@ +From a4a4d7f9183bae11d81616346038e9efaba2fce1 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 16 Dec 2024 19:15:52 +0000 +Subject: [PATCH] dtoverlays: Use continuous clock mode for ov9281 + +This increases the maximum frame rate from 247 to 260fps in +10-bit mode. + +Signed-off-by: Dave Stevenson +--- + arch/arm/boot/dts/overlays/ov9281-overlay.dts | 1 - + arch/arm/boot/dts/overlays/ov9281.dtsi | 1 - + 2 files changed, 2 deletions(-) + +--- a/arch/arm/boot/dts/overlays/ov9281-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ov9281-overlay.dts +@@ -29,7 +29,6 @@ + csi_ep: endpoint { + remote-endpoint = <&cam_endpoint>; + data-lanes = <1 2>; +- clock-noncontinuous; + }; + }; + }; +--- a/arch/arm/boot/dts/overlays/ov9281.dtsi ++++ b/arch/arm/boot/dts/overlays/ov9281.dtsi +@@ -19,7 +19,6 @@ cam_node: ov9281@60 { + cam_endpoint: endpoint { + clock-lanes = <0>; + data-lanes = <1 2>; +- clock-noncontinuous; + link-frequencies = + /bits/ 64 <400000000>; + }; diff --git a/target/linux/bcm27xx/patches-6.6/950-1470-overlays-goodix-Allow-override-i2c-address.patch b/target/linux/bcm27xx/patches-6.6/950-1470-overlays-goodix-Allow-override-i2c-address.patch new file mode 100644 index 0000000000..508896b3bd --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1470-overlays-goodix-Allow-override-i2c-address.patch @@ -0,0 +1,36 @@ +From 62085522016ee2dadbe8668a6a97919770020817 Mon Sep 17 00:00:00 2001 +From: Renjaya Raga Zenta +Date: Wed, 18 Dec 2024 16:44:32 +0700 +Subject: [PATCH] overlays: goodix: Allow override i2c address + +Some Goodix devices e.g. gt911 use address 0x5d instead of 0x14. +So, make the address overridable. + +Signed-off-by: Renjaya Raga Zenta +--- + arch/arm/boot/dts/overlays/README | 3 ++- + arch/arm/boot/dts/overlays/goodix-overlay.dts | 1 + + 2 files changed, 3 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -1439,7 +1439,8 @@ Name: goodix + Info: Enables I2C connected Goodix gt9271 multiple touch controller using + GPIOs 4 and 17 (pins 7 and 11 on GPIO header) for interrupt and reset. + Load: dtoverlay=goodix,= +-Params: interrupt GPIO used for interrupt (default 4) ++Params: addr I2C address (default 0x14) ++ interrupt GPIO used for interrupt (default 4) + reset GPIO used for reset (default 17) + i2c-path Override I2C path to allow for i2c-gpio buses + +--- a/arch/arm/boot/dts/overlays/goodix-overlay.dts ++++ b/arch/arm/boot/dts/overlays/goodix-overlay.dts +@@ -37,6 +37,7 @@ + }; + + __overrides__ { ++ addr = <>9271>,"reg:0"; + interrupt = <&goodix_pins>,"brcm,pins:0", + <>9271>,"interrupts:0", + <>9271>,"irq-gpios:4"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1471-fixup-misc-Add-RP1-PIO-driver.patch b/target/linux/bcm27xx/patches-6.6/950-1471-fixup-misc-Add-RP1-PIO-driver.patch index 696c0d9186..5adc2a69c7 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1471-fixup-misc-Add-RP1-PIO-driver.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1471-fixup-misc-Add-RP1-PIO-driver.patch @@ -1,7 +1,7 @@ From cd26850713088942ca4f9a248a8bed1f0504a58f Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 19 Dec 2024 15:11:40 +0000 -Subject: [PATCH 1471/1482] fixup! misc: Add RP1 PIO driver +Subject: [PATCH] fixup! misc: Add RP1 PIO driver Change the Kconfig dependencies so that RP1_PIO depends on FIRMWARE_RP1, rather than selecting it. diff --git a/target/linux/bcm27xx/patches-6.6/950-1473-misc-rp1-pio-More-logical-probe-sequence.patch b/target/linux/bcm27xx/patches-6.6/950-1473-misc-rp1-pio-More-logical-probe-sequence.patch index 3204997b6e..b67b029010 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1473-misc-rp1-pio-More-logical-probe-sequence.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1473-misc-rp1-pio-More-logical-probe-sequence.patch @@ -1,7 +1,7 @@ From 468b525d45a726e4ba704b33c4eba53de47ac684 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 5 Dec 2024 16:03:39 +0000 -Subject: [PATCH 1473/1482] misc: rp1-pio: More logical probe sequence +Subject: [PATCH] misc: rp1-pio: More logical probe sequence Sort the probe function initialisation into a more logical order. diff --git a/target/linux/bcm27xx/patches-6.6/950-1474-misc-rp1-pio-Convert-floats-to-24.8-fixed-point.patch b/target/linux/bcm27xx/patches-6.6/950-1474-misc-rp1-pio-Convert-floats-to-24.8-fixed-point.patch index ba997136bd..d843f1f913 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1474-misc-rp1-pio-Convert-floats-to-24.8-fixed-point.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1474-misc-rp1-pio-Convert-floats-to-24.8-fixed-point.patch @@ -1,7 +1,7 @@ From 5c07ba20630a629399eaa6583457aca93ff74606 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Mon, 9 Dec 2024 09:58:29 +0000 -Subject: [PATCH 1474/1482] misc: rp1-pio: Convert floats to 24.8 fixed point +Subject: [PATCH] misc: rp1-pio: Convert floats to 24.8 fixed point Floating point arithmetic is not supported in the kernel, so use fixed point instead. diff --git a/target/linux/bcm27xx/patches-6.6/950-1475-misc-rp1-pio-Minor-cosmetic-tweaks.patch b/target/linux/bcm27xx/patches-6.6/950-1475-misc-rp1-pio-Minor-cosmetic-tweaks.patch index 73cbacb328..439698d3ef 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1475-misc-rp1-pio-Minor-cosmetic-tweaks.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1475-misc-rp1-pio-Minor-cosmetic-tweaks.patch @@ -1,7 +1,7 @@ From 75203c6641cfe47dfb817b095430021b0981ff47 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 10 Dec 2024 12:06:14 +0000 -Subject: [PATCH 1475/1482] misc: rp1-pio: Minor cosmetic tweaks +Subject: [PATCH] misc: rp1-pio: Minor cosmetic tweaks No functional change. diff --git a/target/linux/bcm27xx/patches-6.6/950-1476-misc-rp1-pio-Add-in-kernel-DMA-support.patch b/target/linux/bcm27xx/patches-6.6/950-1476-misc-rp1-pio-Add-in-kernel-DMA-support.patch index 05c6de2b01..facfb23c95 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1476-misc-rp1-pio-Add-in-kernel-DMA-support.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1476-misc-rp1-pio-Add-in-kernel-DMA-support.patch @@ -1,7 +1,7 @@ From fddd3e9318dbf01fb763b6880021abc558fce8e6 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 12 Dec 2024 17:09:27 +0000 -Subject: [PATCH 1476/1482] misc: rp1-pio: Add in-kernel DMA support +Subject: [PATCH] misc: rp1-pio: Add in-kernel DMA support Add kernel-facing implementations of pio_sm_config_xfer and pio_xm_xfer_data. diff --git a/target/linux/bcm27xx/patches-6.6/950-1477-misc-Add-ws2812-pio-rp1-driver.patch b/target/linux/bcm27xx/patches-6.6/950-1477-misc-Add-ws2812-pio-rp1-driver.patch index 469a8a9b91..b50bdea197 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1477-misc-Add-ws2812-pio-rp1-driver.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1477-misc-Add-ws2812-pio-rp1-driver.patch @@ -1,7 +1,7 @@ From d6d83ad3d9a3a594909a1ad1c82b735ab711cd12 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 3 Dec 2024 16:09:30 +0000 -Subject: [PATCH 1477/1482] misc: Add ws2812-pio-rp1 driver +Subject: [PATCH] misc: Add ws2812-pio-rp1 driver ws2812-pio-rp1 is a PIO-based driver for WS2812 LEDS. It creates a character device in /dev, the default name of which is /dev/leds, diff --git a/target/linux/bcm27xx/patches-6.6/950-1478-overlays-Add-ws2812-pio-overlay.patch b/target/linux/bcm27xx/patches-6.6/950-1478-overlays-Add-ws2812-pio-overlay.patch index 8e5051f385..ab98fa139f 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1478-overlays-Add-ws2812-pio-overlay.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1478-overlays-Add-ws2812-pio-overlay.patch @@ -1,7 +1,7 @@ From 4a8f2b39157825fefc505fe4b94f3a9ce101e170 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 12 Dec 2024 23:23:39 +0000 -Subject: [PATCH 1478/1482] overlays: Add ws2812-pio overlay +Subject: [PATCH] overlays: Add ws2812-pio overlay Add an overlay to enable a WS2812 LED driver on a given GPIO. @@ -15,7 +15,7 @@ Signed-off-by: Phil Elwell --- a/arch/arm/boot/dts/overlays/Makefile +++ b/arch/arm/boot/dts/overlays/Makefile -@@ -337,7 +337,8 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ +@@ -342,7 +342,8 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ waveshare-can-fd-hat-mode-a.dtbo \ waveshare-can-fd-hat-mode-b.dtbo \ wittypi.dtbo \ @@ -27,7 +27,7 @@ Signed-off-by: Phil Elwell targets += $(dtbo-y) --- a/arch/arm/boot/dts/overlays/README +++ b/arch/arm/boot/dts/overlays/README -@@ -5487,6 +5487,28 @@ Params: alsaname Changes +@@ -5599,6 +5599,28 @@ Params: alsaname Changes compatible Changes the codec compatibility diff --git a/target/linux/bcm27xx/patches-6.6/950-1480-overlays-Add-and-document-i2c_csi_dsi0-parameters.patch b/target/linux/bcm27xx/patches-6.6/950-1480-overlays-Add-and-document-i2c_csi_dsi0-parameters.patch new file mode 100644 index 0000000000..ab0f051136 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1480-overlays-Add-and-document-i2c_csi_dsi0-parameters.patch @@ -0,0 +1,342 @@ +From 489570796a5789f849683fc3fb034c55cb13e4c6 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 19 Dec 2024 17:13:17 +0000 +Subject: [PATCH] overlays: Add and document i2c_csi_dsi0 parameters + +Add "i2c_csi_dsi0" parameters to overlays that already have an +"i2c_csi_dsi" parameter. + +The I2C bus and GPIO mapping of i2c_csi_dsi and i2c_csi_dsi0 varies +between platforms. Document the associations against the dtparams +"i2c_csi_dsi" and "i2c_csi_dsi0" - run "dtparam -h i2c_csi_dsi" +and "dtparam -h i2c_csi_dsi0" to read it. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/overlays/README | 103 ++++++++++++++++-- + .../arm/boot/dts/overlays/ads1115-overlay.dts | 2 + + .../arm/boot/dts/overlays/i2c-fan-overlay.dts | 2 + + .../arm/boot/dts/overlays/i2c-mux-overlay.dts | 2 + + .../dts/overlays/i2c-pwm-pca9685a-overlay.dts | 2 + + .../arm/boot/dts/overlays/i2c-rtc-overlay.dts | 2 + + .../boot/dts/overlays/i2c-sensor-overlay.dts | 2 + + .../boot/dts/overlays/mcp23017-overlay.dts | 2 + + .../arm/boot/dts/overlays/pca953x-overlay.dts | 2 + + .../arm/boot/dts/overlays/pcf857x-overlay.dts | 2 + + .../dts/overlays/sc16is750-i2c-overlay.dts | 2 + + .../dts/overlays/sc16is752-i2c-overlay.dts | 2 + + 12 files changed, 113 insertions(+), 12 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -301,10 +301,31 @@ Params: + i2c_baudrate An alias for i2c_arm_baudrate + + i2c_csi_dsi Set to "on" to enable the i2c_csi_dsi interface ++ The I2C bus and GPIOs are platform specific: ++ B rev 1: ++ i2c-1 on 2 & 3 ++ B rev 2, B+, CM, Zero, Zero W, 2B, CM2, CM3, ++ CM4S: ++ i2c-0 on 28 & 29 ++ 3B, 3B+, Zero 2W, 4B, 400, CM4: ++ i2c-0 on 44 & 45 ++ 5, 500: ++ i2c-11/i2c-4 on 40 & 41 ++ CM5 on CM5IO: ++ i2c-0 on 0 & 1 ++ CM5 on CM4IO: ++ i2c-10/i2c-6 on 38 & 39 + + i2c_csi_dsi0 Set to "on" to enable the i2c_csi_dsi0 interface ++ The I2C bus and GPIOs are platform specific: ++ B rev 1 & 2, B+, CM, Zero, Zero W, 2B, CM2, ++ CM3, CM4S, 3B, 3B+, Zero 2W, 4B, 400, CM4, ++ CM5 on CM4IO: ++ i2c-0 on 0 & 1 ++ 5, 500, CM5 on CM5IO: ++ i2c-10/i2c-6 on 38 & 39 + +- i2c_csi_dsi1 Set to "on" to enable the i2c_csi_dsi1 interface ++ i2c_csi_dsi1 A Pi 5 family-specific alias for i2c_csi_dsi. + + i2c_vc Set to "on" to enable the i2c interface + usually reserved for the VideoCore processor +@@ -546,7 +567,12 @@ Params: addr I2C bus + Amplifier for this channel. (Default 1 sets the + full scale of the channel to 4.096 Volts) + i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) + i2c4 Choose the I2C4 bus (configure with the i2c4 +@@ -2086,7 +2112,13 @@ Params: addr Sets the + + i2c0 Choose the I2C0 bus on GPIOs 0&1 + +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) +@@ -2158,7 +2190,13 @@ Params: pca9542 Select t + + i2c0 Choose the I2C0 bus on GPIOs 0&1 + +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) +@@ -2186,7 +2224,12 @@ Info: Adds support for an NXP PCA9685A + Load: dtoverlay=i2c-pwm-pca9685a,= + Params: addr I2C address of PCA9685A (default 0x40) + i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) + i2c4 Choose the I2C3 bus (configure with the i2c3 +@@ -2251,7 +2294,13 @@ Params: abx80x Select o + + i2c0 Choose the I2C0 bus on GPIOs 0&1 + +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) +@@ -2517,7 +2566,12 @@ Params: addr Set the + + i2c0 Choose the I2C0 bus on GPIOs 0&1 + +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) +@@ -3144,7 +3198,12 @@ Params: gpiopin Gpio pin + mcp23008 Configure an MCP23008 instead. + noints Disable the interrupt GPIO line. + i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) + i2c4 Choose the I2C4 bus (configure with the i2c4 +@@ -3604,7 +3663,12 @@ Params: addr I2C addr + pca9654 Select the Onnn PCA9654 (8 bit) + xra1202 Select the Exar XRA1202 (8 bit) + i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) + i2c4 Choose the I2C3 bus (configure with the i2c3 +@@ -3626,7 +3690,12 @@ Params: addr I2C addr + pcf8575 Select the NXP PCF8575 (16 bit) + pca8574 Select the NXP PCA8574 (8 bit) + i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) + i2c4 Choose the I2C3 bus (configure with the i2c3 +@@ -4296,7 +4365,12 @@ Params: int_pin GPIO use + addr Address (default 0x48) + xtal On-board crystal frequency (default 14745600) + i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) + i2c4 Choose the I2C4 bus (configure with the i2c4 +@@ -4325,7 +4399,12 @@ Params: int_pin GPIO use + addr Address (default 0x48) + xtal On-board crystal frequency (default 14745600) + i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) + i2c4 Choose the I2C4 bus (configure with the i2c4 +--- a/arch/arm/boot/dts/overlays/ads1115-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ads1115-overlay.dts +@@ -123,6 +123,8 @@ + i2c0 = <&frag100>, "target:0=",<&i2c0>; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; ++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+101+102"; + i2c3 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c3"; + i2c4 = <&frag100>, "target?=0", +--- a/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts +@@ -85,6 +85,8 @@ + i2c0 = <&frag100>,"target:0=",<&i2c0>; + i2c_csi_dsi = <&frag100>,"target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; ++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+101+102"; + i2c3 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c3"; + i2c4 = <&frag100>, "target?=0", +--- a/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts +@@ -167,6 +167,8 @@ + <0>,"+101+102"; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; ++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+101+102"; + i2c3 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c3"; + i2c4 = <&frag100>, "target?=0", +--- a/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts +@@ -49,6 +49,8 @@ + <0>,"+101+102"; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; ++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+101+102"; + i2c3 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c3"; + i2c4 = <&frag100>, "target?=0", +--- a/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts +@@ -30,6 +30,8 @@ + i2c0 = <&frag100>, "target:0=",<&i2c0>; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; ++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+101+102"; + i2c3 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c3"; + i2c4 = <&frag100>, "target?=0", +--- a/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts +@@ -30,6 +30,8 @@ + i2c0 = <&frag100>, "target:0=",<&i2c0>; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; ++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+101+102"; + i2c3 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c3"; + i2c4 = <&frag100>, "target?=0", +--- a/arch/arm/boot/dts/overlays/mcp23017-overlay.dts ++++ b/arch/arm/boot/dts/overlays/mcp23017-overlay.dts +@@ -90,6 +90,8 @@ + i2c0 = <&frag100>, "target:0=",<&i2c0>; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; ++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+101+102"; + i2c3 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c3"; + i2c4 = <&frag100>, "target?=0", +--- a/arch/arm/boot/dts/overlays/pca953x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pca953x-overlay.dts +@@ -254,6 +254,8 @@ + <0>,"+100+101"; + i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, + <0>,"+100+101"; ++ i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+100+101"; + i2c3 = <&frag0>, "target?=0", + <&frag0>, "target-path=i2c3"; + i2c4 = <&frag0>, "target?=0", +--- a/arch/arm/boot/dts/overlays/pcf857x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pcf857x-overlay.dts +@@ -46,6 +46,8 @@ + <0>,"+100+101"; + i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, + <0>,"+100+101"; ++ i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+100+101"; + i2c3 = <&frag0>, "target?=0", + <&frag0>, "target-path=i2c3"; + i2c4 = <&frag0>, "target?=0", +--- a/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts +@@ -71,6 +71,8 @@ + <0>,"+100+101"; + i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, + <0>,"+100+101"; ++ i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+100+101"; + i2c3 = <&frag0>, "target?=0", + <&frag0>, "target-path=i2c3"; + i2c4 = <&frag0>, "target?=0", +--- a/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts +@@ -71,6 +71,8 @@ + <0>,"+100+101"; + i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, + <0>,"+100+101"; ++ i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+100+101"; + i2c3 = <&frag0>, "target?=0", + <&frag0>, "target-path=i2c3"; + i2c4 = <&frag0>, "target?=0", diff --git a/target/linux/bcm27xx/patches-6.6/950-1481-dts-Add-noanthogs-parameter-to-CM4-and-CM5.patch b/target/linux/bcm27xx/patches-6.6/950-1481-dts-Add-noanthogs-parameter-to-CM4-and-CM5.patch new file mode 100644 index 0000000000..483031057c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1481-dts-Add-noanthogs-parameter-to-CM4-and-CM5.patch @@ -0,0 +1,56 @@ +From 147ddfdaf626fe5484596235bba8bdc6dcfde501 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 20 Dec 2024 15:08:52 +0000 +Subject: [PATCH] dts: Add noanthogs parameter to CM4 and CM5 + +By default, the antenna selection on CM4 and CM5 is fixed at boot time, +with the dtparams ant1, ant2 and noant selecting which should be +enabled. Add a new dtparam - noanthogs - which leaves the GPIOs free +to be controlled at runtime by the OS. + +N.B. Using this parameter without suitable OS support will leave both +antennae disabled, resulting in attenuated WiFi and Bluetooth signals. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts | 2 ++ + arch/arm/boot/dts/overlays/README | 6 ++++++ + arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 2 ++ + 3 files changed, 10 insertions(+) + +--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts ++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts +@@ -493,6 +493,8 @@ i2c_csi_dsi0: &i2c0 { + <&ant1>, "output-low?=on", + <&ant2>, "output-high?=off", + <&ant2>, "output-low?=on"; ++ noanthogs = <&ant1>,"status=disabled", ++ <&ant2>, "status=disabled"; + + pcie_tperst_clk_ms = <&pcie0>,"brcm,tperst-clk-ms:0"; + }; +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -153,6 +153,12 @@ Params: + + noant Disable both antennas. CM4/5 only. + ++ noanthogs Disable the GPIO hogs on the antenna controls ++ so they can be controlled at runtime. Note that ++ using this parameter without suitable OS ++ support will result in attenuated WiFi and ++ Bluetooth signals. CM4/5 only. ++ + audio Set to "on" to enable the onboard ALSA audio + interface (default "off") + +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi +@@ -750,5 +750,7 @@ spi10_cs_pins: &spi10_cs_gpio1 {}; + <&ant1>, "output-low?=on", + <&ant2>, "output-high?=off", + <&ant2>, "output-low?=on"; ++ noanthogs = <&ant1>,"status=disabled", ++ <&ant2>, "status=disabled"; + }; + };