diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-24 08:46:03 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-24 08:46:03 -0700 |
| commit | cf9610f9116d55c5a66ef9f1faecacabd93a9322 (patch) | |
| tree | 757a3d280760c654767631cab5ad8af2d86d1848 /drivers/soundwire/amd_init.c | |
| download | linux-stable-cf9610f9116d55c5a66ef9f1faecacabd93a9322.tar.gz linux-stable-cf9610f9116d55c5a66ef9f1faecacabd93a9322.zip | |
Merge tag 'pinctrl-v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrlgrafted
Pull pin control updates from Linus Walleij:
"Core changes:
- Use the non-blocking mux_state_try_select() in the generic MUX pin
control back-end
- Free pin maps on pinctrl_generic_to_map() failure in the core
helpers
New hardware pin control:
- Qualcomm Maili TLMM SoC, PMG1110 PMIC, and Eliza LPASS LPI TLMM SoC
(this is for the low power audio, LPASS = Low Power Audio SubSystem
portions)
- Upboard support on top of the Intel pin controller
- Apple T6030 and T6031 support (just compatible strings)
- Samsung Exynos 8855 SoC
- Mediatek MT6858 SoC
- Rockchip RK3308B and RV1106 SoCs
- Airoha EN7523 and AN7563 SoCs
Improvements:
- Qualcomm TLMM GPIOs are unconditionally marked as wakeup capable
- Qualcomm improvements on top of the Qualcomm PDC (power domain
controller) irqchip improvements from tglx:s tree
- Qualcomm IPQ806x and IPQ9650 non-urgent pin grop fixups
- The NPCM8xx driver has been put into shape fixing many pin and
group definitions
- Some non-urgent Tegra 264 pin assignments are fixed up
- Some non-urgent fixups to Airoha AN7581 and AN7583 registers and
pin assignments, missing features etc"
* tag 'pinctrl-v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl: (130 commits)
dt-bindings: pinctrl: Convert TI DA850 pupd to DT schema
pinctrl: meson: sync some modify from A4
pinctrl: meson: a4: Add input enable pin configuration
pinctrl: realtek: rtd1625: remove unused group name spdif_sel
pinctrl: realtek: rtd1625: fix base_bit for VE4 GPIO 13
dt-bindings: pinctrl: microchip,pic32mzda-pinctrl: Convert to DT schema
Revert "Merge branch 'ib-rsk7204' into devel"
pinctrl: rockchip: Add RV1106 pinctrl support
dt-bindings: pinctrl: rockchip: Add RV1106 compatible
pinctrl: rockchip: Decode drive strength in the get function
pinctrl: fix PINCTRL_GENERIC_MUX not always being selectable
pinctrl: airoha: add support of an7563 SoC
dt-bindings: pinctrl: airoha: add support of an7563 pin controller
pinctrl: airoha: try to find chip scu node by phandle first
pinctrl: airoha: add support of en7523 SoC
dt-bindings: pinctrl: airoha: add support of en7523 pin controller
pinctrl: airoha: an7583: add support for olt pinmux
pinctrl: airoha: an7583: add support for pon_alt pinmux
pinctrl: airoha: an7583: add support for npu_uart pinmux
dt-bindings: pinctrl: airoha: an7583: add missed features
...
Diffstat (limited to 'drivers/soundwire/amd_init.c')
| -rw-r--r-- | drivers/soundwire/amd_init.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/drivers/soundwire/amd_init.c b/drivers/soundwire/amd_init.c new file mode 100644 index 000000000..8e419ddfa --- /dev/null +++ b/drivers/soundwire/amd_init.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * SoundWire AMD Manager Initialize routines + * + * Initializes and creates SDW devices based on ACPI and Hardware values + * + * Copyright 2024 Advanced Micro Devices, Inc. + */ + +#include <linux/acpi.h> +#include <linux/cleanup.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "amd_init.h" + +#define ACP_PAD_PULLDOWN_CTRL 0x0001448 +#define ACP_SW_PAD_KEEPER_EN 0x0001454 +#define AMD_SDW0_PAD_CTRL_MASK 0x60 +#define AMD_SDW1_PAD_CTRL_MASK 5 +#define AMD_SDW_PAD_CTRL_MASK (AMD_SDW0_PAD_CTRL_MASK | AMD_SDW1_PAD_CTRL_MASK) +#define AMD_SDW0_PAD_EN 1 +#define AMD_SDW1_PAD_EN 0x10 +#define AMD_SDW_PAD_EN (AMD_SDW0_PAD_EN | AMD_SDW1_PAD_EN) + +static int amd_enable_sdw_pads(void __iomem *mmio, u32 link_mask, struct device *dev) +{ + u32 pad_keeper_en, pad_pulldown_ctrl_mask; + + switch (link_mask) { + case 1: + pad_keeper_en = AMD_SDW0_PAD_EN; + pad_pulldown_ctrl_mask = AMD_SDW0_PAD_CTRL_MASK; + break; + case 2: + pad_keeper_en = AMD_SDW1_PAD_EN; + pad_pulldown_ctrl_mask = AMD_SDW1_PAD_CTRL_MASK; + break; + case 3: + pad_keeper_en = AMD_SDW_PAD_EN; + pad_pulldown_ctrl_mask = AMD_SDW_PAD_CTRL_MASK; + break; + default: + dev_err(dev, "No SDW Links are enabled\n"); + return -ENODEV; + } + + amd_updatel(mmio, ACP_SW_PAD_KEEPER_EN, pad_keeper_en, pad_keeper_en); + amd_updatel(mmio, ACP_PAD_PULLDOWN_CTRL, pad_pulldown_ctrl_mask, 0); + + return 0; +} + +static int sdw_amd_cleanup(struct sdw_amd_ctx *ctx) +{ + int i; + + for (i = 0; i < ctx->count; i++) { + if (!(ctx->link_mask & BIT(i))) + continue; + platform_device_unregister(ctx->pdev[i]); + } + + return 0; +} + +static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res) +{ + struct sdw_amd_ctx *ctx; + struct acpi_device *adev; + struct acp_sdw_pdata sdw_pdata[2]; + struct platform_device_info pdevinfo[2]; + u32 link_mask; + int count, index; + int ret; + + if (!res) + return NULL; + + adev = acpi_fetch_acpi_dev(res->handle); + if (!adev) + return NULL; + + if (!res->count) + return NULL; + + count = res->count; + dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count); + ret = amd_enable_sdw_pads(res->mmio_base, res->link_mask, res->parent); + if (ret) + return NULL; + + /* + * we need to alloc/free memory manually and can't use devm: + * this routine may be called from a workqueue, and not from + * the parent .probe. + * If devm_ was used, the memory might never be freed on errors. + */ + ctx = kzalloc_obj(*ctx); + if (!ctx) + return NULL; + + ctx->count = count; + ctx->link_mask = res->link_mask; + struct resource *sdw_res __free(kfree) = kzalloc_obj(*sdw_res); + if (!sdw_res) { + kfree(ctx); + return NULL; + } + sdw_res->flags = IORESOURCE_MEM; + sdw_res->start = res->addr; + sdw_res->end = res->addr + res->reg_range; + memset(&pdevinfo, 0, sizeof(pdevinfo)); + link_mask = ctx->link_mask; + for (index = 0; index < count; index++) { + if (!(link_mask & BIT(index))) + continue; + + sdw_pdata[index].instance = index; + sdw_pdata[index].acp_sdw_lock = res->acp_lock; + sdw_pdata[index].acp_rev = res->acp_rev; + pdevinfo[index].name = "amd_sdw_manager"; + pdevinfo[index].id = index; + pdevinfo[index].parent = res->parent; + pdevinfo[index].num_res = 1; + pdevinfo[index].res = sdw_res; + pdevinfo[index].data = &sdw_pdata[index]; + pdevinfo[index].size_data = sizeof(struct acp_sdw_pdata); + pdevinfo[index].fwnode = acpi_fwnode_handle(adev); + ctx->pdev[index] = platform_device_register_full(&pdevinfo[index]); + if (IS_ERR(ctx->pdev[index])) + goto err; + } + return ctx; +err: + while (index--) { + if (!(link_mask & BIT(index))) + continue; + + platform_device_unregister(ctx->pdev[index]); + } + + kfree(ctx); + return NULL; +} + +static int sdw_amd_startup(struct sdw_amd_ctx *ctx) +{ + struct amd_sdw_manager *amd_manager; + int i, ret; + + /* Startup SDW Manager devices */ + for (i = 0; i < ctx->count; i++) { + if (!(ctx->link_mask & BIT(i))) + continue; + amd_manager = dev_get_drvdata(&ctx->pdev[i]->dev); + ret = amd_sdw_manager_start(amd_manager); + if (ret) + return ret; + } + + return 0; +} + +int sdw_amd_probe(struct sdw_amd_res *res, struct sdw_amd_ctx **sdw_ctx) +{ + *sdw_ctx = sdw_amd_probe_controller(res); + if (!*sdw_ctx) + return -ENODEV; + + return sdw_amd_startup(*sdw_ctx); +} +EXPORT_SYMBOL_NS(sdw_amd_probe, "SOUNDWIRE_AMD_INIT"); + +void sdw_amd_exit(struct sdw_amd_ctx *ctx) +{ + sdw_amd_cleanup(ctx); + kfree(ctx->peripherals); + kfree(ctx); +} +EXPORT_SYMBOL_NS(sdw_amd_exit, "SOUNDWIRE_AMD_INIT"); + +int sdw_amd_get_slave_info(struct sdw_amd_ctx *ctx) +{ + struct amd_sdw_manager *amd_manager; + struct sdw_bus *bus; + struct sdw_slave *slave; + struct list_head *node; + int index; + int i = 0; + int num_slaves = 0; + + for (index = 0; index < ctx->count; index++) { + if (!(ctx->link_mask & BIT(index))) + continue; + amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev); + if (!amd_manager) + return -ENODEV; + bus = &amd_manager->bus; + /* Calculate number of slaves */ + list_for_each(node, &bus->slaves) + num_slaves++; + } + + ctx->peripherals = kmalloc_flex(*ctx->peripherals, array, num_slaves); + if (!ctx->peripherals) + return -ENOMEM; + ctx->peripherals->num_peripherals = num_slaves; + for (index = 0; index < ctx->count; index++) { + if (!(ctx->link_mask & BIT(index))) + continue; + amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev); + if (amd_manager) { + bus = &amd_manager->bus; + list_for_each_entry(slave, &bus->slaves, node) { + ctx->peripherals->array[i] = slave; + i++; + } + } + } + return 0; +} +EXPORT_SYMBOL_NS(sdw_amd_get_slave_info, "SOUNDWIRE_AMD_INIT"); + +MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +MODULE_DESCRIPTION("AMD SoundWire Init Library"); +MODULE_LICENSE("Dual BSD/GPL"); |
