From 5c3077cf45630c8a7981820e64a74b4d90b785c9 Mon Sep 17 00:00:00 2001 From: Ryan Murray Date: Mon, 26 Oct 2026 14:18:53 +1100 Subject: [PATCH] dmaengine: apple-sio: select firmware behavior from compatible data The T6030 backend is already enabled in the shared kernel fragment, but it has a separate build option and repeated conditional code. Compile it with the common SIO driver and select its boot-table validator, FIFO threshold and descriptor handling through the existing apple,t6030-sio compatible. The generic apple,sio profile retains the M1/M2 behavior. Keep the protocol-10 validation and refuse incompatible tables before DMA or hardware setup. Remove APPLE_SIO_T6030 and its shared fragment request. Update the binding description, synthetic DT overlay and KUnit probe cases to cover the specific match, missing/older tables and an unknown compatible. Assisted-by: LLM Signed-off-by: Ryan Murray --- .../devicetree/bindings/dma/apple,sio.yaml | 4 +- arch/arm64/configs/asahi.config | 1 - drivers/dma/Kconfig | 20 +---- drivers/dma/apple-sio-probe-test.c | 46 +++++++++-- drivers/dma/apple-sio-probe-test.dtso | 10 +++ drivers/dma/apple-sio.c | 79 +++++++++---------- 6 files changed, 93 insertions(+), 67 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/apple,sio.yaml b/Documentation/devicetree/bindings/dma/apple,sio.yaml index dbaaa2d3d1a1a..708d7bb8bed96 100644 --- a/Documentation/devicetree/bindings/dma/apple,sio.yaml +++ b/Documentation/devicetree/bindings/dma/apple,sio.yaml @@ -9,7 +9,9 @@ title: Apple SIO Coprocessor description: SIO is a coprocessor on Apple M1 and later chips (and maybe also on earlier chips). Its role is to offload SPI, UART and DisplayPort audio transfers, - being a pretend DMA controller. + being a pretend DMA controller. The SoC-specific compatible selects the + firmware protocol and channel configuration. In particular, apple,t6030-sio + requires protocol-10 boot tables before the coprocessor can be started. maintainers: - Martin PoviĊĦer diff --git a/arch/arm64/configs/asahi.config b/arch/arm64/configs/asahi.config index d07b70afd9294..590df15222c8b 100644 --- a/arch/arm64/configs/asahi.config +++ b/arch/arm64/configs/asahi.config @@ -63,7 +63,6 @@ CONFIG_USB_XHCI_PCI_ASMEDIA=y CONFIG_TYPEC_SN201202X=m CONFIG_RTC_DRV_APPLE_PMU=m CONFIG_APPLE_SIO=m -CONFIG_APPLE_SIO_T6030=y CONFIG_APPLE_DART=y CONFIG_APPLE_DART_J713_DCP=y CONFIG_APPLE_DOCKCHANNEL=m diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 94f9c2e2860d9..6ba3a178c636b 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -111,26 +111,12 @@ config APPLE_SIO_KUNIT_TEST default KUNIT_ALL_TESTS help Check which SIO device-tree nodes the probe goes on with: the - generic SIO of the M1 and M2 in every configuration, and a T6030 - SIO only with APPLE_SIO_T6030 and protocol-10 boot tables. Run it - both with and without APPLE_SIO_T6030. - -config APPLE_SIO_T6030 - bool "Apple SIO on the M3 Pro (T6030)" - depends on APPLE_SIO - help - Configure the SIO channels of the T6030 (apple,t6030-sio) the way - its firmware expects them: the DisplayPort audio channels refill - their FIFO at the threshold the system's device tree records for - them (0x600 bytes, not 0x800), and every period descriptor is - published whole. The firmware's boot tables come from the boot - loader, as on the other SoCs; the SIO is only started when they - are protocol-10 tables, which a boot loader without T6030 support - does not build. Without this option a T6030 SIO is left stopped. + generic SIO of the M1 and M2, and a T6030 SIO only with validated + protocol-10 boot tables. The compatible selects the firmware layout. config APPLE_SIO_T6030_KUNIT_TEST bool "KUnit tests for the T6030 SIO boot-table check" if !KUNIT_ALL_TESTS - depends on APPLE_SIO=y && APPLE_SIO_T6030 && KUNIT=y + depends on APPLE_SIO=y && KUNIT=y depends on OF_OVERLAY default KUNIT_ALL_TESTS help diff --git a/drivers/dma/apple-sio-probe-test.c b/drivers/dma/apple-sio-probe-test.c index 7049c8acb532a..c486c5072f07c 100644 --- a/drivers/dma/apple-sio-probe-test.c +++ b/drivers/dma/apple-sio-probe-test.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT /* - * Included by apple-sio.c: which SIO nodes the probe goes on with, with and - * without APPLE_SIO_T6030. The test devices have no register range, so a probe + * Included by apple-sio.c: the compatible selects which firmware tables a + * node needs. The test devices have no register range, so a probe * that goes on toward the hardware stops there with -EINVAL; a refused node * returns -ENODEV first. */ @@ -13,16 +13,17 @@ OF_OVERLAY_DECLARE(apple_sio_probe_test); static void sio_probe_nodes_test(struct kunit *test) { - const bool t6030 = IS_ENABLED(CONFIG_APPLE_SIO_T6030); const struct { const char *path; int err; } cases[] = { - /* M1/M2: the generic SIO, either way */ + /* M1/M2: the generic SIO */ { "/sio-probe-test/sio-probe-test-t8103", -EINVAL }, - /* T6030: only with its support built, and with protocol-10 tables */ - { "/sio-probe-test/sio-probe-test-t6030-v10", t6030 ? -EINVAL : -ENODEV }, + /* T6030: protocol-10 tables select the admitted path */ + { "/sio-probe-test/sio-probe-test-t6030-v10", -EINVAL }, { "/sio-probe-test/sio-probe-test-t6030-earlier", -ENODEV }, + { "/sio-probe-test/sio-probe-test-t6030-missing", -ENODEV }, + { "/sio-probe-test/sio-probe-test-unknown", -ENODEV }, }; unsigned int i; @@ -38,15 +39,44 @@ static void sio_probe_nodes_test(struct kunit *test) KUNIT_ASSERT_NOT_NULL_MSG(test, pdev->dev.of_node, "%s", cases[i].path); mask = pdev->dev.coherent_dma_mask; KUNIT_EXPECT_EQ_MSG(test, sio_probe(pdev), cases[i].err, "%s", cases[i].path); - /* Without its support, a T6030 is refused before its DMA is set up */ - if (!t6030 && cases[i].err == -ENODEV) + /* Bad protocol tables are refused before DMA setup. */ + if (cases[i].err == -ENODEV) KUNIT_EXPECT_EQ_MSG(test, pdev->dev.coherent_dma_mask, mask, "%s", cases[i].path); } } +static void sio_match_data_test(struct kunit *test) +{ + const struct of_device_id *match; + struct device_node *np; + const struct sio_hw_data *hw; + + KUNIT_ASSERT_EQ(test, 0, of_overlay_apply_kunit(test, apple_sio_probe_test)); + np = of_find_node_by_path("/sio-probe-test/sio-probe-test-t6030-v10"); + KUNIT_ASSERT_NOT_NULL(test, np); + match = of_match_node(sio_of_match, np); + of_node_put(np); + KUNIT_ASSERT_NOT_NULL(test, match); + hw = match->data; + KUNIT_EXPECT_EQ(test, hw->fifo_threshold, 0x600U); + KUNIT_EXPECT_TRUE(test, hw->clear_period_desc); + KUNIT_EXPECT_TRUE(test, hw->check_boot_tables == sio_t6030_check_boot_tables); + + np = of_find_node_by_path("/sio-probe-test/sio-probe-test-t8103"); + KUNIT_ASSERT_NOT_NULL(test, np); + match = of_match_node(sio_of_match, np); + of_node_put(np); + KUNIT_ASSERT_NOT_NULL(test, match); + hw = match->data; + KUNIT_EXPECT_EQ(test, hw->fifo_threshold, 0x800U); + KUNIT_EXPECT_FALSE(test, hw->clear_period_desc); + KUNIT_EXPECT_FALSE(test, hw->check_boot_tables); +} + static struct kunit_case sio_probe_test_cases[] = { KUNIT_CASE(sio_probe_nodes_test), + KUNIT_CASE(sio_match_data_test), {} }; diff --git a/drivers/dma/apple-sio-probe-test.dtso b/drivers/dma/apple-sio-probe-test.dtso index 79f3346beaad8..93816e9f6b202 100644 --- a/drivers/dma/apple-sio-probe-test.dtso +++ b/drivers/dma/apple-sio-probe-test.dtso @@ -17,6 +17,16 @@ dma-channels = <8>; }; + sio-probe-test-t6030-missing { + compatible = "apple,t6030-sio", "apple,sio"; + dma-channels = <8>; + }; + + sio-probe-test-unknown { + compatible = "apple,sio-probe-test-unknown"; + dma-channels = <8>; + }; + v10_w: v10-w { iommu-addresses = <&v10 0x0 0x30000 0x0 0x4000>, <&v10 0x0 0x34000 0x0 0x20000>, <&v10 0x0 0x54000 0x0 0x4000>, <&v10 0x0 0x58000 0x0 0x4000>; diff --git a/drivers/dma/apple-sio.c b/drivers/dma/apple-sio.c index 0c06e0264b7b6..65ba4511501c7 100644 --- a/drivers/dma/apple-sio.c +++ b/drivers/dma/apple-sio.c @@ -108,7 +108,14 @@ struct sio_chan { typedef void (*sio_ack_callback)(struct sio_chan *, void *, bool); +struct sio_hw_data { + u32 fifo_threshold; + bool clear_period_desc; + int (*check_boot_tables)(struct device *dev); +}; + struct sio_data { + const struct sio_hw_data *hw; void __iomem *base; struct dma_device dma; struct device *dev; @@ -130,9 +137,6 @@ struct sio_data { } tags; int nchannels; -#if IS_ENABLED(CONFIG_APPLE_SIO_T6030) - bool t6030; -#endif struct sio_chan channels[]; }; @@ -155,16 +159,6 @@ static int sio_send_siomsg_atomic(struct sio_data *sio, u64 msg, void *cookie); static int sio_call(struct sio_data *sio, u64 msg); -static inline bool sio_is_t6030(struct sio_data *sio) -{ -#if IS_ENABLED(CONFIG_APPLE_SIO_T6030) - return sio->t6030; -#else - return false; -#endif -} - -#if IS_ENABLED(CONFIG_APPLE_SIO_T6030) /* * Boot tables of the T6030 SIO firmware, protocol 10. The boot loader builds them from the ADT * and passes each one as two apple,sio-firmware-params: the table's IOVA page under its key and @@ -320,7 +314,23 @@ static int sio_t6030_check_boot_tables(struct device *dev) } return 0; } -#endif + +static const struct sio_hw_data sio_hw_generic = { + .fifo_threshold = 0x800, +}; + +static const struct sio_hw_data sio_hw_t6030 = { + .fifo_threshold = 0x600, + .clear_period_desc = true, + .check_boot_tables = sio_t6030_check_boot_tables, +}; + +static const struct of_device_id sio_of_match[] = { + { .compatible = "apple,t6030-sio", .data = &sio_hw_t6030 }, + { .compatible = "apple,sio", .data = &sio_hw_generic }, + { } +}; +MODULE_DEVICE_TABLE(of, sio_of_match); static struct sio_chan *to_sio_chan(struct dma_chan *chan) { @@ -459,7 +469,7 @@ static struct dma_async_tx_descriptor *sio_prep_dma_cyclic( * T6030: publish the whole descriptor, its other fields zero; * a slot may come back from an earlier transfer. */ - if (sio_is_t6030(siochan->host)) + if (siochan->host->hw->clear_period_desc) memset(d, 0, sizeof(*d)); d->flag = 1; /* not sure what's up with this */ d->iova = buf_addr + period_len * i; @@ -839,7 +849,7 @@ static int sio_device_config(struct dma_chan *chan, * dp-audioN dma-channels: channel, data shape, timeout, FIFO size, * threshold). T6030's DisplayPort audio channels refill at 0x600. */ - cfg.threshold = sio_is_t6030(sio) ? 0x600 : 0x800; + cfg.threshold = sio->hw->fifo_threshold; /* * Dmaengine prescribes we ought to apply the new configuration only @@ -967,20 +977,23 @@ static int sio_send_dt_params(struct sio_data *sio) static int sio_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; + const struct sio_hw_data *hw; struct sio_data *sio; struct dma_device *dma; int nchannels; int err, i; - /* - * Only APPLE_SIO_T6030 checks a T6030's boot tables and configures its - * channels: without it, leave the SIO stopped rather than start it as - * the generic one. - */ - if (!IS_ENABLED(CONFIG_APPLE_SIO_T6030) && - of_device_is_compatible(np, "apple,t6030-sio")) - return dev_err_probe(&pdev->dev, -ENODEV, - "T6030 SIO support (APPLE_SIO_T6030) not built; leaving the SIO stopped\n"); + match = of_match_node(sio_of_match, np); + if (!match) + return -ENODEV; + hw = match->data; + /* Refuse incompatible firmware tables before DMA or hardware setup. */ + if (hw->check_boot_tables) { + err = hw->check_boot_tables(&pdev->dev); + if (err) + return err; + } err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(42)); if (err) @@ -998,15 +1011,7 @@ static int sio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sio); sio->dev = &pdev->dev; sio->nchannels = nchannels; -#if IS_ENABLED(CONFIG_APPLE_SIO_T6030) - sio->t6030 = of_device_is_compatible(np, "apple,t6030-sio"); - /* Before any register, power or firmware access */ - if (sio->t6030) { - err = sio_t6030_check_boot_tables(&pdev->dev); - if (err) - return err; - } -#endif + sio->hw = hw; sio->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sio->base)) @@ -1107,12 +1112,6 @@ static void sio_remove(struct platform_device *pdev) dma_async_device_unregister(&sio->dma); } -static const struct of_device_id sio_of_match[] = { - { .compatible = "apple,sio", }, - { } -}; -MODULE_DEVICE_TABLE(of, sio_of_match); - static __maybe_unused int sio_suspend(struct device *dev) { /*