bcm27xx: pull 6.6 patches from RPi repo
authorÁlvaro Fernández Rojas <noltari@gmail.com>
Fri, 27 Dec 2024 16:14:40 +0000 (17:14 +0100)
committerÁlvaro Fernández Rojas <noltari@gmail.com>
Sat, 28 Dec 2024 08:06:30 +0000 (09:06 +0100)
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 <noltari@gmail.com>
130 files changed:
target/linux/bcm27xx/bcm2708/config-6.6
target/linux/bcm27xx/bcm2709/config-6.6
target/linux/bcm27xx/bcm2710/config-6.6
target/linux/bcm27xx/bcm2711/config-6.6
target/linux/bcm27xx/bcm2712/config-6.6
target/linux/bcm27xx/config-6.6
target/linux/bcm27xx/patches-6.6/950-0297-staging-vchiq_arm-Add-36-bit-address-support.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-0298-staging-vchiq_arm-children-inherit-DMA-config.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-0299-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-0365-staging-vchiq_arm-Add-log_level-module-params.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-0998-i2c-designware-Add-support-for-bus-clear-feature.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-1267-dts-rp1-Disable-DMA-usage-for-UART0.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-1351-drivers-media-bcm2835_isp-Cache-LS-table-dmabuf.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1351-mailbox-Add-RP1-mailbox-support.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-1352-firmware-Add-an-RP1-firmware-interface.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-1353-misc-Add-RP1-PIO-driver.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-1353-pwm-Add-GPIO-PWM-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1354-fixup-musc-add-RP1-PIO-driver.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-1355-dtoverlay-Add-a-dtoverlay-for-pwm-gpio.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1355-misc-rp1-pio-Add-an-in-kernel.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-1356-dts-2712-Drop-some-numa-options-from-bootargs.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1356-pwm-Add-pwm-pio-rp1-driver.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-1357-misc-rp1-pio-Fix-copy-paste-error-in-pio_rp1.h.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-1357-mmc-quirks-add-more-broken-Kingston-Canvas-Go-SD-car.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1357-rpi-pio-add-missing.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-1358-dt-bindings-usb-snps-dwc3-add-FS-HS-periodic-NAK-pol.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1358-misc-rpi-pio-back-port-some.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-1359-misc-rp1-pio-Add-FIFO.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-1359-usb-dwc3-core-add-support-for-setting-NAK-enhancemen.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1360-DTS-rp1-set-enhanced-FS-NAK-quirk-for-usb3-controlle.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1360-misc-rp1-pio-Fix-parameter-checks-wihout-client.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-1361-drivers-usb-xhci-prevent-a-theoretical-race-on-non-c.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1362-iio-humidity-dht11-Allow-non-zero-decimals.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1363-drm-vc4-Correct-condition-for-ignoring-a-plane-to-sr.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1364-drm-vc4-Use-the-TPZ-scaling-filter-for-1x1-source-im.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1365-drm-Set-non-desktop-property-to-true-for-writeback-a.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1366-drm-Increase-plane_mask-to-64bit.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1367-drm-vc4-Increase-number-of-overlay-planes-from-16-to.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1368-drm-vc4-Assign-32-overlay-planes-to-writeback-only.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1369-drm-Add-a-DRM_MODE_TRANSPOSE-option-to-the-DRM-rotat.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1370-drm-Add-a-rotation-parameter-to-connectors.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1371-drm-vc4-txp-Add-a-rotation-property-to-the-writeback.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1372-dmaengine-dw-axi-dmac-Allow-client-chosen-width.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1373-spi-dw-Let-the-DMAC-set-the-transfer-widths.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1374-serial-pl011-Request-a-memory-width-of-1-byte.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1377-drivers-usb-xhci-set-HID-bit-in-streaming-endpoint-c.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1379-media-i2c-imx477-Add-options-for-slightly-modifying-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1380-dtoverlays-Add-link-frequency-override-to-imx477-378.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1381-dmaengine-dw-axi-dmac-Only-start-idle-channels.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1382-mailbox-Add-RP1-mailbox-support.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1383-firmware-Add-an-RP1-firmware-interface-over-mbox.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1384-misc-Add-RP1-PIO-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1386-dts-bcm2712-rpi-Add-RP1-firmware-and-mailboxes.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1387-dts-bcm2712-rpi-Add-the-RP1-PIO-device.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1388-misc-rp1-pio-Add-an-in-kernel-API.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1389-pwm-Add-pwm-pio-rp1-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1391-overlays-Add-pwm-pio-overlay.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1392-fixup-misc-Add-RP1-PIO-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1394-misc-rp1-pio-Add-compat_ioctl-method.patch
target/linux/bcm27xx/patches-6.6/950-1396-i2c-designware-Add-support-for-bus-clear-feature.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1397-drivers-media-pci-Update-Hailo-accelerator-device-dr.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1398-dtoverlays-enable-SPI-CS-active-high.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1399-drm-vc4-hvs-Defer-updating-the-enable_bg_fill-until-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1400-misc-rp1-pio-Add-FIFO-related-methods.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1401-overlays-Enable-Raspberry-Touch-2-rotation-with-over.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1402-rp1-pio-Add-missing-static-inline-s.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1403-misc-rp1-pio-Back-port-some-6.11-build-fixes.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1404-Adding-Pimidi-kernel-module.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1406-Adding-pimidi-overlay.dts.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1411-staging-vchiq_arm-Add-36-bit-address-support.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1412-staging-vchiq_arm-children-inherit-DMA-config.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1413-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1414-staging-vchiq_arm-Add-log_level-module-params.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1415-media-i2c-imx477-Fix-link-frequency-menu.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1416-misc-rp1-pio-Fix-copy-paste-error-in-pio_rp1.h.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1417-misc-rp1-pio-Fix-parameter-checks-wihout-client.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1418-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1419-dts-bcm2712-rpi-For-CM5IO-i2c_csi_dsi-needs-to-be-CA.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1420-dts-bcm2712-rpi-cm5-Remove-inaccessible-USB_OC_N.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1421-overlays-qca7000-replace-URL-with-textual-hint.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1422-dt-bindings-net-cdns-macb-Add-compatible-for-Raspber.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1423-net-macb-Add-support-for-Raspberry-Pi-RP1-ethernet-c.patch
target/linux/bcm27xx/patches-6.6/950-1424-rp1-clk-Only-set-PLL_SEC_RST-in-rp1_pll_divider_off.patch
target/linux/bcm27xx/patches-6.6/950-1425-rp1-clk-Rationalise-the-use-of-the-CLK_IS_CRITICAL-f.patch
target/linux/bcm27xx/patches-6.6/950-1426-dt-arm64-Fixup-RP1-ethernet-DT-configuration.patch
target/linux/bcm27xx/patches-6.6/950-1427-clk-rp1-Add-RP1_CLK_DMA.patch
target/linux/bcm27xx/patches-6.6/950-1428-rp1-clk-Remove-CLK_IGNORE_UNUSED-flags.patch
target/linux/bcm27xx/patches-6.6/950-1429-dt-rp1-Use-clk_sys-for-ethernet-hclk-and-pclk.patch
target/linux/bcm27xx/patches-6.6/950-1430-dt-rp1-Link-RP1-DMA-to-the-associated-clock.patch
target/linux/bcm27xx/patches-6.6/950-1431-raspberrypi-firmware-Add-the-RPI-firmware-UART-APIs.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1432-serial-core-Add-the-Raspberry-Pi-firmware-UART-id.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1433-serial-tty-Add-a-driver-for-the-RPi-firmware-UART.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1435-dtoverlay-Add-an-overlay-for-the-Raspberry-Pi-firmwa.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1436-ARM-dts-Remove-duplicate-tags.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1437-Allow-setting-I-C-clock-frequency-via-i2c_arm_baudra.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1438-nvme-pci-Disable-Host-Memory-Buffer-usage.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1439-fixup-serial-tty-Add-a-driver-for-the-RPi-firmware-U.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1440-serial-rpi-fw-uart-Demote-debug-log-messages.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1441-dtoverlays-Add-Arducam-override-for-ov9281.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1442-drivers-input-touchscreen-Add-support-for-no-irq-to-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1443-drivers-gpu-drm-panel-Added-waveshare-13.3inch-panel.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1444-arch-arm-boot-dts-overlays-Added-waveshare-13.3inch-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1445-fixup-cgroup-Use-kernel-command-line-to-disable-memo.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1446-media-i2c-ov9282-Correct-the-exposure-offset.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1451-Revert-drm-vc4-hvs-Don-t-write-gamma-luts-on-2711.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1452-Revert-PCI-Warn-if-no-host-bridge-NUMA-node-info.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1454-drm-bridge-panel-Connector-to-allow-interlaced-modes.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1455-dts-overlays-vc4-kms-dpi-generic-overlay-Add-interla.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1456-drm-rp1-rp1-dpi-Add-interlaced-modes-and-PIO-program.patch
target/linux/bcm27xx/patches-6.6/950-1457-pwm-Improve-PWM_PIO_RP1-dependencies.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-1458-Revert-pwm-Improve-PWM_PIO_RP1-dependencies.patch [deleted file]
target/linux/bcm27xx/patches-6.6/950-1459-ASoC-allo-piano-dac-plus-Fix-volume-limit-locking.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1460-drm-vc4-txp-Do-not-allow-24bpp-formats-when-transpos.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1461-drm-Validate-connector-rotation-has-one-bit-set-in-t.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1462-ASoC-allo-piano-dac-plus-Suppress-517-errors.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1463-drm-rp1-rp1-dpi-Fix-optional-dependency-on-RP1_PIO.patch
target/linux/bcm27xx/patches-6.6/950-1464-serial-sc16is7xx-announce-support-for-SER_RS485_RTS_.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1467-dtoverlays-Add-override-for-target-path-on-I2C-overl.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1468-misc-rp1-pio-Support-larger-data-transfers.patch
target/linux/bcm27xx/patches-6.6/950-1469-dtoverlays-Use-continuous-clock-mode-for-ov9281.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1470-overlays-goodix-Allow-override-i2c-address.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1471-fixup-misc-Add-RP1-PIO-driver.patch
target/linux/bcm27xx/patches-6.6/950-1473-misc-rp1-pio-More-logical-probe-sequence.patch
target/linux/bcm27xx/patches-6.6/950-1474-misc-rp1-pio-Convert-floats-to-24.8-fixed-point.patch
target/linux/bcm27xx/patches-6.6/950-1475-misc-rp1-pio-Minor-cosmetic-tweaks.patch
target/linux/bcm27xx/patches-6.6/950-1476-misc-rp1-pio-Add-in-kernel-DMA-support.patch
target/linux/bcm27xx/patches-6.6/950-1477-misc-Add-ws2812-pio-rp1-driver.patch
target/linux/bcm27xx/patches-6.6/950-1478-overlays-Add-ws2812-pio-overlay.patch
target/linux/bcm27xx/patches-6.6/950-1480-overlays-Add-and-document-i2c_csi_dsi0-parameters.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.6/950-1481-dts-Add-noanthogs-parameter-to-CM4-and-CM5.patch [new file with mode: 0644]

index b587b5d7198b2c8b5ede0a97e99d981de1f700ed..a8a3cc9af7c8069b96d09509c8195ad24d682501 100644 (file)
@@ -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
index 4a295d52c860304bc2a6d657a83edaac16475380..edcc8f58cc2569d1a3698188b9d48c2455be51a8 100644 (file)
@@ -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
index 97f2f110b06500d3e209ed9bda2246b5ce94ce97..18a9c26b01d494c4399fdf4fbe358f378cf3ea66 100644 (file)
@@ -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
index f9b7469607304203a72abb4baef14f0c49968751..a48771b8e7086392777949092c5ff50a61e069bd 100644 (file)
@@ -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
index 5ed564f5ab9f42ee6ce3b78585c4d8e09ee71684..9dac0688df3d119c803da6f2fac5526819462e70 100644 (file)
@@ -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
index 50ca86f58508d2cf283fa0d58f43f509e45d9e77..e69dbf5d74584d71359b5563e18e55772462c77d 100644 (file)
@@ -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 (file)
index 11147d9..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-From fc5ed9d9bf0411523220bab60304da6d23257a64 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- .../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 (file)
index 87bea65..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-From d4712f611e6d60dd9cf09df581f5df6fad6a2207 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- .../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 (file)
index 162d333..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-From 9f328c347fc9a5495b8383aa2bae1d3bc242a2ab Mon Sep 17 00:00:00 2001
-From: detule <ogjoneski@gmail.com>
-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 <ogjoneski@gmail.com>
----
- .../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 <linux/platform_device.h>
- #include <linux/compat.h>
- #include <linux/dma-mapping.h>
-+#include <linux/dmapool.h>
- #include <linux/rcupdate.h>
- #include <linux/delay.h>
- #include <linux/slab.h>
-@@ -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 (file)
index 51815f2..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-From 79f24f7454a416fed9106c75ea9b3be480465dda Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- .../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 (file)
index 691484d..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-From 24cb07b0c0724a22e474d12e7c2d5b834bf3b076 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- 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 (file)
index c809c4f..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-From cc63d552b9aab92fb581dfb08267d5af697f477b Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- 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 = <RP1_INT_UART0 IRQ_TYPE_LEVEL_HIGH>;
-                       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 (file)
index 0000000..1f5ed75
--- /dev/null
@@ -0,0 +1,141 @@
+From 25e6acfe00f589a5989ebd2c8d21a130fb3bf106 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+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 <naush@raspberrypi.com>
+---
+ .../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 (file)
index 23bdb6a..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-From 0d58d8cfb6f989f290d983552fcaa116e582e84a Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- 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 <linux/compat.h>
-+#include <linux/device.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/err.h>
-+#include <linux/interrupt.h>
-+#include <linux/irq.h>
-+#include <linux/kernel.h>
-+#include <linux/mailbox_controller.h>
-+#include <linux/miscdevice.h>
-+#include <linux/module.h>
-+#include <linux/of_address.h>
-+#include <linux/of_irq.h>
-+#include <linux/platform_device.h>
-+
-+/*
-+ * 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 <phil@raspberrypi.com>");
-+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 (file)
index 3a9eb03..0000000
+++ /dev/null
@@ -1,421 +0,0 @@
-From 67daeadcaa7cee1f4b9df7aa108d199e73f35451 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- 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 <eric@anholt.net>
-+ *    Copyright (C) 2015 Broadcom
-+ */
-+
-+#include <linux/dma-mapping.h>
-+#include <linux/kref.h>
-+#include <linux/mailbox_client.h>
-+#include <linux/module.h>
-+#include <linux/of_address.h>
-+#include <linux/of_platform.h>
-+#include <linux/platform_device.h>
-+#include <linux/rp1-firmware.h>
-+
-+#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 <phil@raspberrypi.com>");
-+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 <linux/types.h>
-+#include <linux/of_device.h>
-+
-+#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 (file)
index 521129b..0000000
+++ /dev/null
@@ -1,1385 +0,0 @@
-From 55fd5c9018e1520d45f08cf08630a493ec7dedea Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- 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 <linux/rp1-firmware.h>
-+
-+#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 <linux/cdev.h>
-+#include <linux/compat.h>
-+#include <linux/device.h>
-+#include <linux/dmaengine.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/fs.h>
-+#include <linux/init.h>
-+#include <linux/ioctl.h>
-+#include <linux/module.h>
-+#include <linux/rp1-firmware.h>
-+#include <linux/semaphore.h>
-+#include <linux/slab.h>
-+#include <linux/spinlock.h>
-+#include <linux/uaccess.h>
-+#include <uapi/misc/rp1_pio_if.h>
-+
-+#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 <linux/ioctl.h>
-+
-+#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 (file)
index 0000000..5b89f60
--- /dev/null
@@ -0,0 +1,330 @@
+From 3ab72fc21ea8576e59f6aad10bd6b1a0eae6e5eb Mon Sep 17 00:00:00 2001
+From: Vincent Whitchurch <vincent.whitchurch@axis.com>
+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 <vincent.whitchurch@axis.com>
+Co-developed-by: Stefan Wahren <wahrenst@gmx.net>
+Signed-off-by: Stefan Wahren <wahrenst@gmx.net>
+Co-developed-by: Linus Walleij <linus.walleij@linaro.org>
+Reviewed-by: Andy Shevchenko <andy@kernel.org>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Reviewed-by: Dhruva Gole <d-gole@ti.com>
+Link: https://lore.kernel.org/r/20240604-pwm-gpio-v7-2-6b67cf60db92@linaro.org
+Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
+Signed-off-by: Tim Gover <tim.gover@raspberrypi.com>
+
+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 <linux/cleanup.h>
++#include <linux/container_of.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/gpio/consumer.h>
++#include <linux/hrtimer.h>
++#include <linux/math.h>
++#include <linux/module.h>
++#include <linux/mod_devicetable.h>
++#include <linux/platform_device.h>
++#include <linux/property.h>
++#include <linux/pwm.h>
++#include <linux/spinlock.h>
++#include <linux/time.h>
++#include <linux/types.h>
++
++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 (file)
index cd197f7..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-From 1b5acd42281ad102b79f4e1794f0a0cccdafda05 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Sat, 16 Nov 2024 16:53:31 +0000
-Subject: [PATCH] fixup! misc: Add RP1 PIO driver
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- 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 (file)
index 0000000..65c6be6
--- /dev/null
@@ -0,0 +1,79 @@
+From ff0fe12ab875d587348b6f2b9e73ae928049ebee Mon Sep 17 00:00:00 2001
+From: Tim Gover <tim.gover@raspberrypi.com>
+Date: Thu, 31 Oct 2024 16:12:54 +0000
+Subject: [PATCH] dtoverlay: Add a dtoverlay for pwm-gpio
+
+Signed-off-by: Tim Gover <tim.gover@raspberrypi.com>
+---
+ 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,<param>=<val>
++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 (file)
index 6accaf0..0000000
+++ /dev/null
@@ -1,1797 +0,0 @@
-From 2819a61eb000c207589c97eef9d69a237c6cfdf3 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- 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 <linux/cdev.h>
- #include <linux/compat.h>
-@@ -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 <n> 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 <addr>`
-+ *
-+ * \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 <addr>`
-+ *
-+ * \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-- <addr>`
-+ *
-+ * \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 <addr>`
-+ *
-+ * \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-- <addr>`
-+ *
-+ * \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 <addr>`
-+ *
-+ * \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 <addr>`
-+ *
-+ * \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 <addr>`
-+ *
-+ * \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 <polarity> GPIO <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 <polarity> PIN <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 <polarity> IRQ <irq> <relative>`
-+ *
-+ * \param polarity true for `WAIT 1`, false for `WAIT 0`
-+ * \param relative true for a `WAIT IRQ <irq> REL`, false for regular `WAIT IRQ <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 <src>, <count>`
-+ *
-+ * \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 <src>, <count>`
-+ *
-+ * \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 <if_full>, <block>`
-+ *
-+ * \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 <if_empty>, <block>`
-+ *
-+ * \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 <dest>, <src>`
-+ *
-+ * \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 <dest>, ~<src>`
-+ *
-+ * \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 <dest>, ::<src>`
-+ *
-+ * \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 <irq> <relative>`
-+ *
-+ * \param relative true for a `IRQ SET <irq> REL`, false for regular `IRQ SET <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_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 <irq> <relative>`
-+ *
-+ * \param relative true for a `IRQ WAIT <irq> REL`, false for regular `IRQ 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_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 <irq> <relative>`
-+ *
-+ * \param relative true for a `IRQ CLEAR <irq> REL`, false for regular `IRQ CLEAR <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_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 <dest>, <value>`
-+ *
-+ * \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 <uapi/misc/rp1_pio_if.h>
-+
-+#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 <linux/pio_instructions.h>
-+
-+#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 (file)
index 0000000..245c575
--- /dev/null
@@ -0,0 +1,29 @@
+From 624eb357e1a16385b3d6171e9194e4c5f8d4fd5f Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+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=<n> is used.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ 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 (file)
index 3f787de..0000000
+++ /dev/null
@@ -1,299 +0,0 @@
-From 4d20aadc3188ecfb62b309a9924ee9696a94fc33 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- 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 <naush@raspberrypi.com>
-+ * and on the pwm-gpio driver by:
-+ *   Vincent Whitchurch <vincent.whitchurch@axis.com>
-+ */
-+
-+#include <linux/err.h>
-+#include <linux/gpio/consumer.h>
-+#include <linux/module.h>
-+#include <linux/mutex.h>
-+#include <linux/of.h>
-+#include <linux/pio_rp1.h>
-+#include <linux/platform_device.h>
-+#include <linux/pwm.h>
-+
-+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 (file)
index 0746e3c..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-From 99a0201bb0abc946dc431214b638b2cc6b01dda5 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- 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 (file)
index 0000000..ef5a5a5
--- /dev/null
@@ -0,0 +1,42 @@
+From 74f3ca5e39586ea26201fe6eaf1b9e6793b101b7 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+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 <jonathan@raspberrypi.com>
+---
+ 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 (file)
index c871fa6..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-From df8a2f6dc114b2c5c7685a069f717f2b06186b74 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- 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 (file)
index 0000000..eaa340f
--- /dev/null
@@ -0,0 +1,35 @@
+From 6c0f34fb0f83741f7f03f6bfd3fcbc89cb2c7cde Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+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 <jonathan@raspberrypi.com>
+---
+ 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 (file)
index 92bde52..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-From d1f0c94e974a5f26d210b1d13a6ef9543bee4984 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- 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 <linux/init.h>
- #include <linux/ioctl.h>
- #include <linux/module.h>
-+#include <linux/of.h>
-+#include <linux/pio_rp1.h>
-+#include <linux/platform_device.h>
- #include <linux/rp1-firmware.h>
- #include <linux/semaphore.h>
- #include <linux/slab.h>
---- 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 (file)
index fff033f..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-From dd2394360860d15146c96635796a75b05bb32b61 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- 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 (file)
index 0000000..ed7dbb0
--- /dev/null
@@ -0,0 +1,77 @@
+From bb53ca75f9e3631e753f397ccab704a8f975658b Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+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 <jonathan@raspberrypi.com>
+---
+ 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 (file)
index 0000000..a305dc1
--- /dev/null
@@ -0,0 +1,30 @@
+From 803757627b48bdad9530b50053321fdea6dfcab4 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+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 <jonathan@raspberrypi.com>
+---
+ 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 (file)
index e68bd17..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-From 3687701e8d252864f440f91f1aedf8ffd58d6ee6 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- 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 (file)
index 0000000..d3ea104
--- /dev/null
@@ -0,0 +1,50 @@
+From e9e852af347ae3ccee4e7abb01f9ef91387980f9 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+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 <jonathan@raspberrypi.com>
+---
+ 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 (file)
index 0000000..19935d0
--- /dev/null
@@ -0,0 +1,32 @@
+From ce65ed02cb6707ae5c9f3a304f5b0124f4eed559 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..9b01824
--- /dev/null
@@ -0,0 +1,41 @@
+From c3393ac1098d1f191e37eed73bf366ebc88ac4ee Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..5a7b3e4
--- /dev/null
@@ -0,0 +1,59 @@
+From ca621585c573cae54dc1235d90822e8bcef2f73d Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..e8f52cb
--- /dev/null
@@ -0,0 +1,30 @@
+From 68b0ff3549148e614e1733d773cee8e689c763c6 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..e27058d
--- /dev/null
@@ -0,0 +1,101 @@
+From 8181e682d6f4ef209845ec24f0a1eb37764d6731 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..6eae6fc
--- /dev/null
@@ -0,0 +1,42 @@
+From 5dc4cef7d7fcda4ea59b9e456a835fa54336af6b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..40eff3c
--- /dev/null
@@ -0,0 +1,71 @@
+From dd340cb082a020fbd42b794493ffd063dd8e15b4 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..ccda8b0
--- /dev/null
@@ -0,0 +1,47 @@
+From b3b3d12cf0734318a0fed0b33e13d714188369db Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..e803a77
--- /dev/null
@@ -0,0 +1,164 @@
+From 8fec3ff870499256f2c18fe7983f6ed3fea4faaf Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..b0e5566
--- /dev/null
@@ -0,0 +1,65 @@
+From 8346446098032c62d1de891a97c7f62264b18f81 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 <drm/drm_atomic.h>
+ #include <drm/drm_atomic_helper.h>
++#include <drm/drm_blend.h>
+ #include <drm/drm_drv.h>
+ #include <drm/drm_edid.h>
+ #include <drm/drm_fb_dma_helper.h>
+@@ -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 (file)
index 0000000..9068866
--- /dev/null
@@ -0,0 +1,40 @@
+From a2fa911d90495762047c05dec4241308ae61ca36 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..786af04
--- /dev/null
@@ -0,0 +1,31 @@
+From 5cf7209c294a58029984880d4858e2d3c7e46a3c Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..860d35b
--- /dev/null
@@ -0,0 +1,25 @@
+From 8894298105f4cb41dfa41e0b0d3c40c3f7b92c44 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..230537f
--- /dev/null
@@ -0,0 +1,56 @@
+From 66aef6ce3557edd9d58d794e4a800c5be49ca0e7 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+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 <jonathan@raspberrypi.com>
+---
+ 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 (file)
index 0000000..ee06051
--- /dev/null
@@ -0,0 +1,199 @@
+From 35e50ee3d66e014d869f0d7a3468bef964d26d32 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+
+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 (file)
index 0000000..47d3d39
--- /dev/null
@@ -0,0 +1,43 @@
+From 7e253a062d5a14de13ccfb410570975099c238be Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..81b7f80
--- /dev/null
@@ -0,0 +1,29 @@
+From 59a8855b51c1d8acf37d3c80f34782d71f474617 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..23bdb6a
--- /dev/null
@@ -0,0 +1,252 @@
+From 0d58d8cfb6f989f290d983552fcaa116e582e84a Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 <linux/compat.h>
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/kernel.h>
++#include <linux/mailbox_controller.h>
++#include <linux/miscdevice.h>
++#include <linux/module.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/platform_device.h>
++
++/*
++ * 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 <phil@raspberrypi.com>");
++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 (file)
index 0000000..3a9eb03
--- /dev/null
@@ -0,0 +1,421 @@
+From 67daeadcaa7cee1f4b9df7aa108d199e73f35451 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 <eric@anholt.net>
++ *    Copyright (C) 2015 Broadcom
++ */
++
++#include <linux/dma-mapping.h>
++#include <linux/kref.h>
++#include <linux/mailbox_client.h>
++#include <linux/module.h>
++#include <linux/of_address.h>
++#include <linux/of_platform.h>
++#include <linux/platform_device.h>
++#include <linux/rp1-firmware.h>
++
++#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 <phil@raspberrypi.com>");
++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 <linux/types.h>
++#include <linux/of_device.h>
++
++#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 (file)
index 0000000..521129b
--- /dev/null
@@ -0,0 +1,1385 @@
+From 55fd5c9018e1520d45f08cf08630a493ec7dedea Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 <linux/rp1-firmware.h>
++
++#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 <linux/cdev.h>
++#include <linux/compat.h>
++#include <linux/device.h>
++#include <linux/dmaengine.h>
++#include <linux/dma-mapping.h>
++#include <linux/fs.h>
++#include <linux/init.h>
++#include <linux/ioctl.h>
++#include <linux/module.h>
++#include <linux/rp1-firmware.h>
++#include <linux/semaphore.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/uaccess.h>
++#include <uapi/misc/rp1_pio_if.h>
++
++#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 <linux/ioctl.h>
++
++#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 (file)
index 0000000..2afb809
--- /dev/null
@@ -0,0 +1,118 @@
+From 0b76dec8dfba8c1a4793dff0c86bf73d088a812e Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ .../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<n> should expose the registers for the interface with DT alias
+  * gpio<n>.
+--- 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 = <RP1_INT_SYSCFG IRQ_TYPE_LEVEL_HIGH>;
++                      #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 (file)
index 0000000..29e5427
--- /dev/null
@@ -0,0 +1,55 @@
+From 3e3c1b9922b22d362a4a9133361597ac80b974bb Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..6accaf0
--- /dev/null
@@ -0,0 +1,1797 @@
+From 2819a61eb000c207589c97eef9d69a237c6cfdf3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 <linux/cdev.h>
+ #include <linux/compat.h>
+@@ -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 <n> 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 <addr>`
++ *
++ * \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 <addr>`
++ *
++ * \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-- <addr>`
++ *
++ * \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 <addr>`
++ *
++ * \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-- <addr>`
++ *
++ * \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 <addr>`
++ *
++ * \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 <addr>`
++ *
++ * \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 <addr>`
++ *
++ * \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 <polarity> GPIO <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 <polarity> PIN <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 <polarity> IRQ <irq> <relative>`
++ *
++ * \param polarity true for `WAIT 1`, false for `WAIT 0`
++ * \param relative true for a `WAIT IRQ <irq> REL`, false for regular `WAIT IRQ <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 <src>, <count>`
++ *
++ * \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 <src>, <count>`
++ *
++ * \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 <if_full>, <block>`
++ *
++ * \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 <if_empty>, <block>`
++ *
++ * \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 <dest>, <src>`
++ *
++ * \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 <dest>, ~<src>`
++ *
++ * \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 <dest>, ::<src>`
++ *
++ * \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 <irq> <relative>`
++ *
++ * \param relative true for a `IRQ SET <irq> REL`, false for regular `IRQ SET <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_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 <irq> <relative>`
++ *
++ * \param relative true for a `IRQ WAIT <irq> REL`, false for regular `IRQ 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_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 <irq> <relative>`
++ *
++ * \param relative true for a `IRQ CLEAR <irq> REL`, false for regular `IRQ CLEAR <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_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 <dest>, <value>`
++ *
++ * \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 <uapi/misc/rp1_pio_if.h>
++
++#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 <linux/pio_instructions.h>
++
++#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 (file)
index 0000000..9eed5f5
--- /dev/null
@@ -0,0 +1,299 @@
+From 4d20aadc3188ecfb62b309a9924ee9696a94fc33 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 <naush@raspberrypi.com>
++ * and on the pwm-gpio driver by:
++ *   Vincent Whitchurch <vincent.whitchurch@axis.com>
++ */
++
++#include <linux/err.h>
++#include <linux/gpio/consumer.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/of.h>
++#include <linux/pio_rp1.h>
++#include <linux/platform_device.h>
++#include <linux/pwm.h>
++
++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 (file)
index 0000000..bd6d633
--- /dev/null
@@ -0,0 +1,99 @@
+From ba7e2e3d03a432acbc338c6c03e46dcd97cfa1b3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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,<param>=<val>
++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 (file)
index 0000000..cd197f7
--- /dev/null
@@ -0,0 +1,29 @@
+From 1b5acd42281ad102b79f4e1794f0a0cccdafda05 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Sat, 16 Nov 2024 16:53:31 +0000
+Subject: [PATCH] fixup! misc: Add RP1 PIO driver
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ 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)
index bffa2418a15644e04b3ed62758c7fcff9b33f9f5..6862a74e21450b523727de08b3c5d4ddab184f78 100644 (file)
@@ -1,7 +1,7 @@
 From b4472d09b1ffdafd8132803ffbec62596e559fd8 Mon Sep 17 00:00:00 2001
 From: Phil Elwell <phil@raspberrypi.com>
 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 <phil@raspberrypi.com>
 
 --- 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 (file)
index 0000000..bc20db2
--- /dev/null
@@ -0,0 +1,157 @@
+From 0e4968617aad7d0f88e0a630499202eaae407a19 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..3ad28d6
--- /dev/null
@@ -0,0 +1,3592 @@
+From 32511f035b086bca254d8adab234cef3541492b4 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+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 <naush@raspberrypi.com>
+---
+ 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(&notification_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 <linux/io.h>
+ #include <linux/errno.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#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 <linux/errno.h>
+ #include <linux/bug.h>
+@@ -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(&notification_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 <linux/types.h>
+ #include <linux/firmware.h>
+@@ -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 <linux/types.h>
++
++#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 <linux/bitops.h>
++#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 <linux/version.h>
+@@ -19,14 +19,14 @@
+ #include <linux/sched/signal.h>
+ #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(&params, (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, &params))) {
+-        hailo_err(pBoard, "HAILO_READ_LOG, reading from log failed with error: %ld \n", err);
+-        return err;
+-    }
+-
+-    if (copy_to_user((void*)arg, &params, 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(&notif_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, &current_waiting_thread);
+-    if (0 != err) {
+-        goto l_exit;
+-    }
+-    up(&pBoard->mutex);
+-
+-    if (0 > (err = wait_for_completion_interruptible(&current_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(&current_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 <linux/uaccess.h>
++
++#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, &current_waiting_thread);
++    if (0 != err) {
++        goto l_exit;
++    }
++    up(&board->mutex);
++
++    if (0 > (err = wait_for_completion_interruptible(&current_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(&current_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(&params, (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, &params))) {
++        hailo_err(board, "HAILO_READ_LOG, reading from log failed with error: %ld \n", err);
++        return err;
++    }
++
++    if (copy_to_user((void*)arg, &params, 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(&params, (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, &params, 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(&params, (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 <linux/version.h>
+@@ -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 <linux/uaccess.h>
++
++#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(&params, (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, &params, 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(&params, (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 <linux/version.h>
+-#include <linux/init.h>
+-#include <linux/module.h>
+-#include <linux/pci.h>
+-
+-#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 <linux/uaccess.h>
+-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 <linux/slab.h>
+@@ -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 (file)
index 0000000..ecd26a7
--- /dev/null
@@ -0,0 +1,108 @@
+From dbf12796d1368286672529d7b03f81066a8c36f3 Mon Sep 17 00:00:00 2001
+From: Iker Pedrosa <ikerpedrosam@gmail.com>
+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 <ikerpedrosam@gmail.com>
+---
+ 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,<param>=<val>
++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,<param>=<val>
+--- /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 <dt-bindings/gpio/gpio.h>
++
++/ {
++      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 (file)
index 0000000..8f8af00
--- /dev/null
@@ -0,0 +1,64 @@
+From 57b528e557890f25e010b6bc7356b5a716c79db2 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..da21526
--- /dev/null
@@ -0,0 +1,215 @@
+From dd2394360860d15146c96635796a75b05bb32b61 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..1a70957
--- /dev/null
@@ -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 <foonerd@github.com>
+---
+ 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 = <&gt911>,"touchscreen-swapped-x-y?";
+               disable_touch = <&gt911>, "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 (file)
index 0000000..643e83b
--- /dev/null
@@ -0,0 +1,42 @@
+From df8a2f6dc114b2c5c7685a069f717f2b06186b74 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..92bde52
--- /dev/null
@@ -0,0 +1,39 @@
+From d1f0c94e974a5f26d210b1d13a6ef9543bee4984 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 <linux/init.h>
+ #include <linux/ioctl.h>
+ #include <linux/module.h>
++#include <linux/of.h>
++#include <linux/pio_rp1.h>
++#include <linux/platform_device.h>
+ #include <linux/rp1-firmware.h>
+ #include <linux/semaphore.h>
+ #include <linux/slab.h>
+--- 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 (file)
index 0000000..fd46f25
--- /dev/null
@@ -0,0 +1,1167 @@
+From 8a6f640708627ac8ebf79f88793038933f169198 Mon Sep 17 00:00:00 2001
+From: Giedrius <giedrius@blokas.io>
+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 <giedrius@blokas.io>
+---
+ 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 <linux/completion.h>
++#include <linux/module.h>
++#include <linux/i2c.h>
++#include <linux/irq.h>
++#include <linux/irqdesc.h>
++#include <linux/bitops.h>
++#include <linux/of_irq.h>
++#include <linux/kfifo.h>
++#include <linux/list.h>
++#include <linux/workqueue.h>
++#include <linux/gpio.h>
++#include <linux/gpio/consumer.h>
++#include <linux/interrupt.h>
++#include <linux/mutex.h>
++#include <linux/refcount.h>
++#include <linux/crc8.h>
++#include <linux/delay.h>
++
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <sound/rawmidi.h>
++#include <sound/asequencer.h>
++#include <sound/info.h>
++
++#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 <giedrius@blokas.io>");
++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 (file)
index 0000000..ab46dbd
--- /dev/null
@@ -0,0 +1,100 @@
+From 75ab92b077602734458f0a77e19a3599be29b93b Mon Sep 17 00:00:00 2001
+From: Giedrius <giedrius@blokas.io>
+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 <giedrius@blokas.io>
+---
+ 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: <None>
++Name:   pimidi
++Info:   Configures the Blokas Labs Pimidi card
++Load:   dtoverlay=pimidi,<param>=<val>
++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 <dt-bindings/gpio/gpio.h>
++#include <dt-bindings/interrupt-controller/irq.h>
++
++/ {
++      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 (file)
index 0000000..871b276
--- /dev/null
@@ -0,0 +1,256 @@
+From a1e4b72997dc3ef423b6f510bfead470475750d4 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ .../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 (file)
index 0000000..df93f3a
--- /dev/null
@@ -0,0 +1,36 @@
+From 1129091b2d95273d930acf2926a569b90512a248 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ .../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 (file)
index 0000000..7120123
--- /dev/null
@@ -0,0 +1,118 @@
+From 2d26a598ceceaea8a6837146c741eb742bbd4baa Mon Sep 17 00:00:00 2001
+From: detule <ogjoneski@gmail.com>
+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 <ogjoneski@gmail.com>
+---
+ .../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 <linux/platform_device.h>
+ #include <linux/compat.h>
+ #include <linux/dma-mapping.h>
++#include <linux/dmapool.h>
+ #include <linux/rcupdate.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+@@ -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 (file)
index 0000000..d85765b
--- /dev/null
@@ -0,0 +1,27 @@
+From 5b29221e96d1ba60a78d5c804a20fa35a6d0517a Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ .../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 (file)
index 0000000..9b5b0e4
--- /dev/null
@@ -0,0 +1,25 @@
+From 8691544f688bd3ae9b6db0845a75ce230fc9e90f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..cf13be0
--- /dev/null
@@ -0,0 +1,25 @@
+From 99a0201bb0abc946dc431214b638b2cc6b01dda5 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..e68bd17
--- /dev/null
@@ -0,0 +1,25 @@
+From 3687701e8d252864f440f91f1aedf8ffd58d6ee6 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..5e50af7
--- /dev/null
@@ -0,0 +1,147 @@
+From 008c93b47b9b965368eb5bbfbef60b816931e0ab Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..0aab104
--- /dev/null
@@ -0,0 +1,30 @@
+From eafaa6015fc0ed676f6115905e7c4145d23f5b7d Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..e2a0121
--- /dev/null
@@ -0,0 +1,25 @@
+From d128c123754e9dd03ad72c16851a1652331d6da1 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..8cdb250
--- /dev/null
@@ -0,0 +1,33 @@
+From 77389e715039b1feac9c6261727600892cc12fdb Mon Sep 17 00:00:00 2001
+From: Michael Heimpold <michael.heimpold@chargebyte.com>
+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 <michael.heimpold@chargebyte.com>
+---
+ 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 (file)
index 0000000..1364b4f
--- /dev/null
@@ -0,0 +1,24 @@
+From 178f1c2747c3920723242f26ba290785d45bffae Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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
index 3941d69edf035d79e7d5c284a96979fabfe1e943..6a9b7b36bd40750dc4a98d7999c5075771734450 100644 (file)
@@ -1,8 +1,8 @@
 From f9f0024bd9bf04a58b64bae356be4c04022d23bc Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <dave.stevenson@raspberrypi.com>
 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.
index d3f216d31d0669c8a7ff536a5c3672a177e89e4d..91d836f71cabbedc3767a31e314bab2ec9aff52a 100644 (file)
@@ -1,8 +1,7 @@
 From 33c225f622d596034a9261316666089a92aa6834 Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <dave.stevenson@raspberrypi.com>
 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
index 247ae455ac448df4144fd8edcd446d788ef3984e..3f4850c8990d10f23a7ec2207fbb6368da3e0949 100644 (file)
@@ -1,8 +1,7 @@
 From eb836a6a299322a8e2b9627cccd23c7a76d068ba Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <dave.stevenson@raspberrypi.com>
 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
index ad6569e99ce50c8cab105305c7a4c25d051c6d03..f213d8def67c51381c1e552563ae6e8bf0be62d0 100644 (file)
@@ -1,7 +1,7 @@
 From 0b4af929b7125abd3a262577b380c7c81ee9b1c5 Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <dave.stevenson@raspberrypi.com>
 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 <dave.stevenson@raspberrypi.com>
 
 --- 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 <dave.stevenson@raspberrypi.com>
                                          <&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 <dave.stevenson@raspberrypi.com>
                                               <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>;
index 9026cebaf95b316c69117d04729531e34c9cc2ca..b13becd187125bb4dddfa0a1371f96a6efcb0c6c 100644 (file)
@@ -1,7 +1,7 @@
 From d4e41ed9954fa86c4774f98d393aa401c81a68e7 Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <dave.stevenson@raspberrypi.com>
 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
index 8fa06045a5734c36e754483447104b4f5679e8fe..662a2485dd151b42fa9543d58c37e94d74898cf1 100644 (file)
@@ -1,7 +1,7 @@
 From 9049e4df2c54b5e620f855f66db3a18c9f2e181f Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <dave.stevenson@raspberrypi.com>
 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.
index 6d0eb133892ba2d2c12fd0d79b3bcf6df7e7125a..937b1257175c340414ac296754f748791855e228 100644 (file)
@@ -1,7 +1,7 @@
 From 542d0f7f2e9f90fc0f02f8cb141f7c3fbf46081b Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <dave.stevenson@raspberrypi.com>
 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 <dave.stevenson@raspberrypi.com>
 
 --- 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 = <RP1_INT_ETH IRQ_TYPE_LEVEL_HIGH>;
@@ -24,7 +24,7 @@ Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
                                  &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>;
        };
index 36d4f8b7236e35ff9ff72328ef39e3a59b17f81a..623fcc6cc362ed1828dc53212b6356d04d1e8ee1 100644 (file)
@@ -1,7 +1,7 @@
 From efecbda4014b490e042c7fd090942b32316f9345 Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <dave.stevenson@raspberrypi.com>
 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 <dave.stevenson@raspberrypi.com>
 
 --- 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 = <RP1_INT_DMA IRQ_TYPE_LEVEL_HIGH>;
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 (file)
index 0000000..7e518bf
--- /dev/null
@@ -0,0 +1,23 @@
+From eb035f3ad7da1324d310ef83b42398f47d5bafe7 Mon Sep 17 00:00:00 2001
+From: Tim Gover <tim.gover@raspberrypi.com>
+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 <tim.gover@raspberrypi.com>
+---
+ 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 (file)
index 0000000..5767f40
--- /dev/null
@@ -0,0 +1,22 @@
+From b8a0e563fd181205565a0edaaebc82b1abf0c5be Mon Sep 17 00:00:00 2001
+From: Tim Gover <tim.gover@raspberrypi.com>
+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 <tim.gover@raspberrypi.com>
+---
+ 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 (file)
index 0000000..f26c1be
--- /dev/null
@@ -0,0 +1,630 @@
+From 2548d954d78bca44c5cf430f8ea6de7c771312d7 Mon Sep 17 00:00:00 2001
+From: Tim Gover <tim.gover@raspberrypi.com>
+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 <tim.gover@raspberrypi.com>
+---
+ 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 <linux/console.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/gpio/consumer.h>
++#include <linux/platform_device.h>
++#include <linux/serial.h>
++#include <linux/serial_core.h>
++#include <linux/slab.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <soc/bcm2835/raspberrypi-firmware.h>
++#include <linux/dma-mapping.h>
++
++#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 <tim.gover@rasberrypi.com>");
++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 (file)
index 0000000..0e4b3e3
--- /dev/null
@@ -0,0 +1,95 @@
+From b6b126861062020fb50859c5af71d8846ce43d7c Mon Sep 17 00:00:00 2001
+From: Tim Gover <tim.gover@raspberrypi.com>
+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 <phil@raspberrypi.com>
+Signed-off-by: Tim Gover <tim.gover@raspberrypi.com>
+---
+ 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,<param>[=<val>]
++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,<param>[=<val>]
+--- /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 (file)
index 0000000..7829a4b
--- /dev/null
@@ -0,0 +1,142 @@
+From 1993b453dc4a62378e90d91e9e0006a6c085f38a Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 <dt-bindings/gpio/gpio.h>
+ / {
+--- 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 <dt-bindings/gpio/gpio.h>
+--- 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 <dt-bindings/clock/bcm2835.h>
+ #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 <dt-bindings/clock/bcm2835.h>
+ #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 (file)
index 0000000..1d14a0b
--- /dev/null
@@ -0,0 +1,25 @@
+From e33702e5e5fe9fef6ec967961e2e5e1c2285ba36 Mon Sep 17 00:00:00 2001
+From: gtrainavicius <gtrainavicius@users.noreply.github.com>
+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 (file)
index 0000000..4f1ea92
--- /dev/null
@@ -0,0 +1,48 @@
+From fda47c026dee7acd975ee2c0f7a440d4038cfaa3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..0f18dc4
--- /dev/null
@@ -0,0 +1,23 @@
+From 0313a0961b685973f7833017479a277e3a4c05a4 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..271f743
--- /dev/null
@@ -0,0 +1,28 @@
+From 0a5be0fe6ba3a981508421131def7eab55d6d75c Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..354ff96
--- /dev/null
@@ -0,0 +1,55 @@
+From 02dee262a9c7295ea514e9db7b9aa4b239922cb3 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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>,
++                     <&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 (file)
index 0000000..d13982e
--- /dev/null
@@ -0,0 +1,118 @@
+From 97638920f1a40e2e0cab363d1e03837ff50c5478 Mon Sep 17 00:00:00 2001
+From: eng33 <eng33@waveshare.com>
+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 <eng33@waveshare.com>
+---
+ 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 (file)
index 0000000..c6861c6
--- /dev/null
@@ -0,0 +1,309 @@
+From 4a89fda8f73df89e009a6188ef07ab97b1d03c7f Mon Sep 17 00:00:00 2001
+From: eng33 <eng33@waveshare.com>
+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 <eng33@waveshare.com>
+---
+ 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 (file)
index 0000000..1d144bf
--- /dev/null
@@ -0,0 +1,46 @@
+From e442e5c1ab6bff5b5460b4fc949beb72aaf77970 Mon Sep 17 00:00:00 2001
+From: eng33 <eng33@waveshare.com>
+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 <eng33@waveshare.com>
+---
+ 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 (file)
index 0000000..8c4a1c3
--- /dev/null
@@ -0,0 +1,37 @@
+From 166dfc4399643681f2e4277bf7b7407e926861e5 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..f673f54
--- /dev/null
@@ -0,0 +1,31 @@
+From e23afbf2c7aae9264322eee8e5c72ca1887606df Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..e222ea8
--- /dev/null
@@ -0,0 +1,22 @@
+From 448a2db3990534810b45d3e4202df96ab2dc5815 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+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 (file)
index 0000000..5aa8866
--- /dev/null
@@ -0,0 +1,29 @@
+From 746662562995125ef7fb2c294300b0bd061b1251 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+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 <popcornmix@gmail.com>
+---
+ 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 (file)
index 0000000..9e4ba2a
--- /dev/null
@@ -0,0 +1,25 @@
+From 7d294fbff4863e53a64685335b30aed9604cae49 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+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 <nick.hollinghurst@raspberrypi.com>
+---
+ 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 (file)
index 0000000..1d97923
--- /dev/null
@@ -0,0 +1,34 @@
+From 2b0acbe8fd008e09a904b7a3c796a2dc79bf10ea Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+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 <nick.hollinghurst@raspberrypi.com>
+---
+ 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";
index bbaee270c52e683ef180998da3777a0f65ed854c..e78ee378a46caed80f5b84d06c189d73906d52d0 100644 (file)
@@ -1,8 +1,8 @@
 From 7735dd0736322cff23aff95490bae1d69937a9bf Mon Sep 17 00:00:00 2001
 From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
 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 (file)
index 357cd05..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-From f85f3509692f966ec32e4db499f7e64dc6b6b952 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- 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 (file)
index eabf388..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-From 73fb1e979a210094935f4af4c3d6e700fba30c5f Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 (file)
index 0000000..8e279df
--- /dev/null
@@ -0,0 +1,79 @@
+From ac0cd73932aa1e371ffaf0b974855ed3cd22937f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 (file)
index 0000000..93de0f9
--- /dev/null
@@ -0,0 +1,30 @@
+From af4ab4fb77dfc697c8ae068b18f27de1ee5d609f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..2a61159
--- /dev/null
@@ -0,0 +1,29 @@
+From 0b216b3988e5b7035cd5ed8a9910eacbb3420ce0 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..67bce02
--- /dev/null
@@ -0,0 +1,73 @@
+From 61494a7aa2ea887fa1cd1399a8db1317c87f661b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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);
index 52e87daa386ad93aac23b4b6bef1d298a2446620..13c735eb4e66f9091db7dfeff956ed8c4a5baa08 100644 (file)
@@ -1,8 +1,7 @@
 From 80533a952218696c0ef1b346bab50dc401e6b74c Mon Sep 17 00:00:00 2001
 From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
 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 (file)
index 0000000..f78402d
--- /dev/null
@@ -0,0 +1,42 @@
+From 694247173f2e136196d7cb3a392c84cda65674d2 Mon Sep 17 00:00:00 2001
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+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 <hvilleneuve@dimonoff.com>
+Suggested-by: Konstantin Pugin <ria.freelander@gmail.com>
+Link: https://lore.kernel.org/lkml/20240422133219.2710061-2-ria.freelander@gmail.com
+Reviewed-by: Andy Shevchenko <andy@kernel.org>
+Tested-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Link: https://lore.kernel.org/r/20241007162716.3122912-1-hugo@hugovil.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..d5f72d1
--- /dev/null
@@ -0,0 +1,550 @@
+From b75fd2a9385e1358fa82218184e73513f9a5e57f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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,<param>=<val>
+ 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,<param>=
+ 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,<param>=
+ 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 @@
+                       <&gt9271>,"irq-gpios:4";
+               reset = <&goodix_pins>,"brcm,pins:4",
+                       <&gt9271>,"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=", <MUX_IDLE_DISCONNECT>,
+                       <&pca9545>,"idle-state:0=", <MUX_IDLE_DISCONNECT>,
+--- 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";
+       };
+ };
index 828efdb79bc64454b5a25b5a9e8d0264ac6a4b3c..3183fb580e3fceb25249b6e1440acc80eee80fb6 100644 (file)
@@ -1,7 +1,7 @@
 From 4b0ca96738bb937529655a0062d60775f47b0f5e Mon Sep 17 00:00:00 2001
 From: Phil Elwell <phil@raspberrypi.com>
 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 (file)
index 0000000..c59e72e
--- /dev/null
@@ -0,0 +1,34 @@
+From a4a4d7f9183bae11d81616346038e9efaba2fce1 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 (file)
index 0000000..508896b
--- /dev/null
@@ -0,0 +1,36 @@
+From 62085522016ee2dadbe8668a6a97919770020817 Mon Sep 17 00:00:00 2001
+From: Renjaya Raga Zenta <ragazenta@gmail.com>
+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 <ragazenta@gmail.com>
+---
+ 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,<param>=<val>
+-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 = <&gt9271>,"reg:0";
+               interrupt = <&goodix_pins>,"brcm,pins:0",
+                       <&gt9271>,"interrupts:0",
+                       <&gt9271>,"irq-gpios:4";
index 696c0d918618aac6d1f1d811ab55bb1e9fb4939a..5adc2a69c76ddc667f18cee4e509e1dfa569eac8 100644 (file)
@@ -1,7 +1,7 @@
 From cd26850713088942ca4f9a248a8bed1f0504a58f Mon Sep 17 00:00:00 2001
 From: Phil Elwell <phil@raspberrypi.com>
 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.
index 3204997b6ec04b7132abac9f2c206af116d0296d..b67b0290105cd16ca5afc30525ac08442c26f34a 100644 (file)
@@ -1,7 +1,7 @@
 From 468b525d45a726e4ba704b33c4eba53de47ac684 Mon Sep 17 00:00:00 2001
 From: Phil Elwell <phil@raspberrypi.com>
 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.
 
index ba997136bd485c228b07dc629fdb15f49ce435f0..d843f1f913a7ca4ca9946509cc67820b235e8b96 100644 (file)
@@ -1,7 +1,7 @@
 From 5c07ba20630a629399eaa6583457aca93ff74606 Mon Sep 17 00:00:00 2001
 From: Phil Elwell <phil@raspberrypi.com>
 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.
index 73cbacb328d2d1c755134a0f2c9e9a0d7fd6a539..439698d3ef30914e265c6cb9b23f1bd4aadfb854 100644 (file)
@@ -1,7 +1,7 @@
 From 75203c6641cfe47dfb817b095430021b0981ff47 Mon Sep 17 00:00:00 2001
 From: Phil Elwell <phil@raspberrypi.com>
 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.
 
index 05c6de2b01a982bbfba16d986bf39b70205eb1c9..facfb23c95ce29cd5bf2d48b54249726c6227ef3 100644 (file)
@@ -1,7 +1,7 @@
 From fddd3e9318dbf01fb763b6880021abc558fce8e6 Mon Sep 17 00:00:00 2001
 From: Phil Elwell <phil@raspberrypi.com>
 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.
index 469a8a9b91a7e21eaa29c8aa9fd099aa3c55a41c..b50bdea197e14a7afb07eec668a28f11187ae7c2 100644 (file)
@@ -1,7 +1,7 @@
 From d6d83ad3d9a3a594909a1ad1c82b735ab711cd12 Mon Sep 17 00:00:00 2001
 From: Phil Elwell <phil@raspberrypi.com>
 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<n>,
index 8e5051f385506cc67aabbeee72012ab00e42fc0b..ab98fa139f9a9e5de781a359cf5f0b5483ffcbd0 100644 (file)
@@ -1,7 +1,7 @@
 From 4a8f2b39157825fefc505fe4b94f3a9ce101e170 Mon Sep 17 00:00:00 2001
 From: Phil Elwell <phil@raspberrypi.com>
 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 <phil@raspberrypi.com>
 
 --- 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 <phil@raspberrypi.com>
  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 (file)
index 0000000..ab0f051
--- /dev/null
@@ -0,0 +1,342 @@
+From 489570796a5789f849683fc3fb034c55cb13e4c6 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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,<param>=<val>
+ 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 (file)
index 0000000..4830310
--- /dev/null
@@ -0,0 +1,56 @@
+From 147ddfdaf626fe5484596235bba8bdc6dcfde501 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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";
+       };
+ };