18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci// Copyright(c) 2015-17 Intel Corporation. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* 58c2ecf20Sopenharmony_ci * SDW Intel Init Routines 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Initializes and creates SDW devices based on ACPI and Hardware values 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/acpi.h> 118c2ecf20Sopenharmony_ci#include <linux/export.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 178c2ecf20Sopenharmony_ci#include <linux/soundwire/sdw_intel.h> 188c2ecf20Sopenharmony_ci#include "cadence_master.h" 198c2ecf20Sopenharmony_ci#include "intel.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define SDW_LINK_TYPE 4 /* from Intel ACPI documentation */ 228c2ecf20Sopenharmony_ci#define SDW_MAX_LINKS 4 238c2ecf20Sopenharmony_ci#define SDW_SHIM_LCAP 0x0 248c2ecf20Sopenharmony_ci#define SDW_SHIM_BASE 0x2C000 258c2ecf20Sopenharmony_ci#define SDW_ALH_BASE 0x2C800 268c2ecf20Sopenharmony_ci#define SDW_LINK_BASE 0x30000 278c2ecf20Sopenharmony_ci#define SDW_LINK_SIZE 0x10000 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int ctrl_link_mask; 308c2ecf20Sopenharmony_cimodule_param_named(sdw_link_mask, ctrl_link_mask, int, 0444); 318c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)"); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic bool is_link_enabled(struct fwnode_handle *fw_node, int i) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct fwnode_handle *link; 368c2ecf20Sopenharmony_ci char name[32]; 378c2ecf20Sopenharmony_ci u32 quirk_mask = 0; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci /* Find master handle */ 408c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), 418c2ecf20Sopenharmony_ci "mipi-sdw-link-%d-subproperties", i); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci link = fwnode_get_named_child_node(fw_node, name); 448c2ecf20Sopenharmony_ci if (!link) 458c2ecf20Sopenharmony_ci return false; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci fwnode_property_read_u32(link, 488c2ecf20Sopenharmony_ci "intel-quirk-mask", 498c2ecf20Sopenharmony_ci &quirk_mask); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE) 528c2ecf20Sopenharmony_ci return false; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return true; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic int sdw_intel_cleanup(struct sdw_intel_ctx *ctx) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct sdw_intel_link_res *link = ctx->links; 608c2ecf20Sopenharmony_ci u32 link_mask; 618c2ecf20Sopenharmony_ci int i; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (!link) 648c2ecf20Sopenharmony_ci return 0; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci link_mask = ctx->link_mask; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci for (i = 0; i < ctx->count; i++, link++) { 698c2ecf20Sopenharmony_ci if (!(link_mask & BIT(i))) 708c2ecf20Sopenharmony_ci continue; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (link->pdev) { 738c2ecf20Sopenharmony_ci pm_runtime_disable(&link->pdev->dev); 748c2ecf20Sopenharmony_ci platform_device_unregister(link->pdev); 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (!link->clock_stop_quirks) 788c2ecf20Sopenharmony_ci pm_runtime_put_noidle(link->dev); 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int 858c2ecf20Sopenharmony_cisdw_intel_scan_controller(struct sdw_intel_acpi_info *info) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct acpi_device *adev; 888c2ecf20Sopenharmony_ci int ret, i; 898c2ecf20Sopenharmony_ci u8 count; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (acpi_bus_get_device(info->handle, &adev)) 928c2ecf20Sopenharmony_ci return -EINVAL; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* Found controller, find links supported */ 958c2ecf20Sopenharmony_ci count = 0; 968c2ecf20Sopenharmony_ci ret = fwnode_property_read_u8_array(acpi_fwnode_handle(adev), 978c2ecf20Sopenharmony_ci "mipi-sdw-master-count", &count, 1); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* 1008c2ecf20Sopenharmony_ci * In theory we could check the number of links supported in 1018c2ecf20Sopenharmony_ci * hardware, but in that step we cannot assume SoundWire IP is 1028c2ecf20Sopenharmony_ci * powered. 1038c2ecf20Sopenharmony_ci * 1048c2ecf20Sopenharmony_ci * In addition, if the BIOS doesn't even provide this 1058c2ecf20Sopenharmony_ci * 'master-count' property then all the inits based on link 1068c2ecf20Sopenharmony_ci * masks will fail as well. 1078c2ecf20Sopenharmony_ci * 1088c2ecf20Sopenharmony_ci * We will check the hardware capabilities in the startup() step 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (ret) { 1128c2ecf20Sopenharmony_ci dev_err(&adev->dev, 1138c2ecf20Sopenharmony_ci "Failed to read mipi-sdw-master-count: %d\n", ret); 1148c2ecf20Sopenharmony_ci return -EINVAL; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* Check count is within bounds */ 1188c2ecf20Sopenharmony_ci if (count > SDW_MAX_LINKS) { 1198c2ecf20Sopenharmony_ci dev_err(&adev->dev, "Link count %d exceeds max %d\n", 1208c2ecf20Sopenharmony_ci count, SDW_MAX_LINKS); 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (!count) { 1258c2ecf20Sopenharmony_ci dev_warn(&adev->dev, "No SoundWire links detected\n"); 1268c2ecf20Sopenharmony_ci return -EINVAL; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci dev_dbg(&adev->dev, "ACPI reports %d SDW Link devices\n", count); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci info->count = count; 1318c2ecf20Sopenharmony_ci info->link_mask = 0; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 1348c2ecf20Sopenharmony_ci if (ctrl_link_mask && !(ctrl_link_mask & BIT(i))) { 1358c2ecf20Sopenharmony_ci dev_dbg(&adev->dev, 1368c2ecf20Sopenharmony_ci "Link %d masked, will not be enabled\n", i); 1378c2ecf20Sopenharmony_ci continue; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (!is_link_enabled(acpi_fwnode_handle(adev), i)) { 1418c2ecf20Sopenharmony_ci dev_dbg(&adev->dev, 1428c2ecf20Sopenharmony_ci "Link %d not selected in firmware\n", i); 1438c2ecf20Sopenharmony_ci continue; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci info->link_mask |= BIT(i); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci#define HDA_DSP_REG_ADSPIC2 (0x10) 1538c2ecf20Sopenharmony_ci#define HDA_DSP_REG_ADSPIS2 (0x14) 1548c2ecf20Sopenharmony_ci#define HDA_DSP_REG_ADSPIC2_SNDW BIT(5) 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/** 1578c2ecf20Sopenharmony_ci * sdw_intel_enable_irq() - enable/disable Intel SoundWire IRQ 1588c2ecf20Sopenharmony_ci * @mmio_base: The mmio base of the control register 1598c2ecf20Sopenharmony_ci * @enable: true if enable 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_civoid sdw_intel_enable_irq(void __iomem *mmio_base, bool enable) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci u32 val; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci val = readl(mmio_base + HDA_DSP_REG_ADSPIC2); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (enable) 1688c2ecf20Sopenharmony_ci val |= HDA_DSP_REG_ADSPIC2_SNDW; 1698c2ecf20Sopenharmony_ci else 1708c2ecf20Sopenharmony_ci val &= ~HDA_DSP_REG_ADSPIC2_SNDW; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci writel(val, mmio_base + HDA_DSP_REG_ADSPIC2); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS(sdw_intel_enable_irq, SOUNDWIRE_INTEL_INIT); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ciirqreturn_t sdw_intel_thread(int irq, void *dev_id) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct sdw_intel_ctx *ctx = dev_id; 1798c2ecf20Sopenharmony_ci struct sdw_intel_link_res *link; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci list_for_each_entry(link, &ctx->link_list, list) 1828c2ecf20Sopenharmony_ci sdw_cdns_irq(irq, link->cdns); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci sdw_intel_enable_irq(ctx->mmio_base, true); 1858c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS(sdw_intel_thread, SOUNDWIRE_INTEL_INIT); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic struct sdw_intel_ctx 1908c2ecf20Sopenharmony_ci*sdw_intel_probe_controller(struct sdw_intel_res *res) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct platform_device_info pdevinfo; 1938c2ecf20Sopenharmony_ci struct platform_device *pdev; 1948c2ecf20Sopenharmony_ci struct sdw_intel_link_res *link; 1958c2ecf20Sopenharmony_ci struct sdw_intel_ctx *ctx; 1968c2ecf20Sopenharmony_ci struct acpi_device *adev; 1978c2ecf20Sopenharmony_ci struct sdw_slave *slave; 1988c2ecf20Sopenharmony_ci struct list_head *node; 1998c2ecf20Sopenharmony_ci struct sdw_bus *bus; 2008c2ecf20Sopenharmony_ci u32 link_mask; 2018c2ecf20Sopenharmony_ci int num_slaves = 0; 2028c2ecf20Sopenharmony_ci int count; 2038c2ecf20Sopenharmony_ci int i; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (!res) 2068c2ecf20Sopenharmony_ci return NULL; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (acpi_bus_get_device(res->handle, &adev)) 2098c2ecf20Sopenharmony_ci return NULL; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (!res->count) 2128c2ecf20Sopenharmony_ci return NULL; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci count = res->count; 2158c2ecf20Sopenharmony_ci dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci ctx = devm_kzalloc(&adev->dev, sizeof(*ctx), GFP_KERNEL); 2188c2ecf20Sopenharmony_ci if (!ctx) 2198c2ecf20Sopenharmony_ci return NULL; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci ctx->count = count; 2228c2ecf20Sopenharmony_ci ctx->links = devm_kcalloc(&adev->dev, ctx->count, 2238c2ecf20Sopenharmony_ci sizeof(*ctx->links), GFP_KERNEL); 2248c2ecf20Sopenharmony_ci if (!ctx->links) 2258c2ecf20Sopenharmony_ci return NULL; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci ctx->count = count; 2288c2ecf20Sopenharmony_ci ctx->mmio_base = res->mmio_base; 2298c2ecf20Sopenharmony_ci ctx->link_mask = res->link_mask; 2308c2ecf20Sopenharmony_ci ctx->handle = res->handle; 2318c2ecf20Sopenharmony_ci mutex_init(&ctx->shim_lock); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci link = ctx->links; 2348c2ecf20Sopenharmony_ci link_mask = ctx->link_mask; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ctx->link_list); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* Create SDW Master devices */ 2398c2ecf20Sopenharmony_ci for (i = 0; i < count; i++, link++) { 2408c2ecf20Sopenharmony_ci if (!(link_mask & BIT(i))) { 2418c2ecf20Sopenharmony_ci dev_dbg(&adev->dev, 2428c2ecf20Sopenharmony_ci "Link %d masked, will not be enabled\n", i); 2438c2ecf20Sopenharmony_ci continue; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci link->mmio_base = res->mmio_base; 2478c2ecf20Sopenharmony_ci link->registers = res->mmio_base + SDW_LINK_BASE 2488c2ecf20Sopenharmony_ci + (SDW_LINK_SIZE * i); 2498c2ecf20Sopenharmony_ci link->shim = res->mmio_base + SDW_SHIM_BASE; 2508c2ecf20Sopenharmony_ci link->alh = res->mmio_base + SDW_ALH_BASE; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci link->ops = res->ops; 2538c2ecf20Sopenharmony_ci link->dev = res->dev; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci link->clock_stop_quirks = res->clock_stop_quirks; 2568c2ecf20Sopenharmony_ci link->shim_lock = &ctx->shim_lock; 2578c2ecf20Sopenharmony_ci link->shim_mask = &ctx->shim_mask; 2588c2ecf20Sopenharmony_ci link->link_mask = link_mask; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci memset(&pdevinfo, 0, sizeof(pdevinfo)); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci pdevinfo.parent = res->parent; 2638c2ecf20Sopenharmony_ci pdevinfo.name = "intel-sdw"; 2648c2ecf20Sopenharmony_ci pdevinfo.id = i; 2658c2ecf20Sopenharmony_ci pdevinfo.fwnode = acpi_fwnode_handle(adev); 2668c2ecf20Sopenharmony_ci pdevinfo.data = link; 2678c2ecf20Sopenharmony_ci pdevinfo.size_data = sizeof(*link); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci pdev = platform_device_register_full(&pdevinfo); 2708c2ecf20Sopenharmony_ci if (IS_ERR(pdev)) { 2718c2ecf20Sopenharmony_ci dev_err(&adev->dev, 2728c2ecf20Sopenharmony_ci "platform device creation failed: %ld\n", 2738c2ecf20Sopenharmony_ci PTR_ERR(pdev)); 2748c2ecf20Sopenharmony_ci goto err; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci link->pdev = pdev; 2778c2ecf20Sopenharmony_ci link->cdns = platform_get_drvdata(pdev); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci list_add_tail(&link->list, &ctx->link_list); 2808c2ecf20Sopenharmony_ci bus = &link->cdns->bus; 2818c2ecf20Sopenharmony_ci /* Calculate number of slaves */ 2828c2ecf20Sopenharmony_ci list_for_each(node, &bus->slaves) 2838c2ecf20Sopenharmony_ci num_slaves++; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci ctx->ids = devm_kcalloc(&adev->dev, num_slaves, 2878c2ecf20Sopenharmony_ci sizeof(*ctx->ids), GFP_KERNEL); 2888c2ecf20Sopenharmony_ci if (!ctx->ids) 2898c2ecf20Sopenharmony_ci goto err; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci ctx->num_slaves = num_slaves; 2928c2ecf20Sopenharmony_ci i = 0; 2938c2ecf20Sopenharmony_ci list_for_each_entry(link, &ctx->link_list, list) { 2948c2ecf20Sopenharmony_ci bus = &link->cdns->bus; 2958c2ecf20Sopenharmony_ci list_for_each_entry(slave, &bus->slaves, node) { 2968c2ecf20Sopenharmony_ci ctx->ids[i].id = slave->id; 2978c2ecf20Sopenharmony_ci ctx->ids[i].link_id = bus->link_id; 2988c2ecf20Sopenharmony_ci i++; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return ctx; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cierr: 3058c2ecf20Sopenharmony_ci ctx->count = i; 3068c2ecf20Sopenharmony_ci sdw_intel_cleanup(ctx); 3078c2ecf20Sopenharmony_ci return NULL; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic int 3118c2ecf20Sopenharmony_cisdw_intel_startup_controller(struct sdw_intel_ctx *ctx) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct acpi_device *adev; 3148c2ecf20Sopenharmony_ci struct sdw_intel_link_res *link; 3158c2ecf20Sopenharmony_ci u32 caps; 3168c2ecf20Sopenharmony_ci u32 link_mask; 3178c2ecf20Sopenharmony_ci int i; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (acpi_bus_get_device(ctx->handle, &adev)) 3208c2ecf20Sopenharmony_ci return -EINVAL; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* Check SNDWLCAP.LCOUNT */ 3238c2ecf20Sopenharmony_ci caps = ioread32(ctx->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP); 3248c2ecf20Sopenharmony_ci caps &= GENMASK(2, 0); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* Check HW supported vs property value */ 3278c2ecf20Sopenharmony_ci if (caps < ctx->count) { 3288c2ecf20Sopenharmony_ci dev_err(&adev->dev, 3298c2ecf20Sopenharmony_ci "BIOS master count is larger than hardware capabilities\n"); 3308c2ecf20Sopenharmony_ci return -EINVAL; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (!ctx->links) 3348c2ecf20Sopenharmony_ci return -EINVAL; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci link = ctx->links; 3378c2ecf20Sopenharmony_ci link_mask = ctx->link_mask; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* Startup SDW Master devices */ 3408c2ecf20Sopenharmony_ci for (i = 0; i < ctx->count; i++, link++) { 3418c2ecf20Sopenharmony_ci if (!(link_mask & BIT(i))) 3428c2ecf20Sopenharmony_ci continue; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci intel_master_startup(link->pdev); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (!link->clock_stop_quirks) { 3478c2ecf20Sopenharmony_ci /* 3488c2ecf20Sopenharmony_ci * we need to prevent the parent PCI device 3498c2ecf20Sopenharmony_ci * from entering pm_runtime suspend, so that 3508c2ecf20Sopenharmony_ci * power rails to the SoundWire IP are not 3518c2ecf20Sopenharmony_ci * turned off. 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_ci pm_runtime_get_noresume(link->dev); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, 3618c2ecf20Sopenharmony_ci void *cdata, void **return_value) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct sdw_intel_acpi_info *info = cdata; 3648c2ecf20Sopenharmony_ci struct acpi_device *adev; 3658c2ecf20Sopenharmony_ci acpi_status status; 3668c2ecf20Sopenharmony_ci u64 adr; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); 3698c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 3708c2ecf20Sopenharmony_ci return AE_OK; /* keep going */ 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (acpi_bus_get_device(handle, &adev)) { 3738c2ecf20Sopenharmony_ci pr_err("%s: Couldn't find ACPI handle\n", __func__); 3748c2ecf20Sopenharmony_ci return AE_NOT_FOUND; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci info->handle = handle; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* 3808c2ecf20Sopenharmony_ci * On some Intel platforms, multiple children of the HDAS 3818c2ecf20Sopenharmony_ci * device can be found, but only one of them is the SoundWire 3828c2ecf20Sopenharmony_ci * controller. The SNDW device is always exposed with 3838c2ecf20Sopenharmony_ci * Name(_ADR, 0x40000000), with bits 31..28 representing the 3848c2ecf20Sopenharmony_ci * SoundWire link so filter accordingly 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci if (FIELD_GET(GENMASK(31, 28), adr) != SDW_LINK_TYPE) 3878c2ecf20Sopenharmony_ci return AE_OK; /* keep going */ 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* device found, stop namespace walk */ 3908c2ecf20Sopenharmony_ci return AE_CTRL_TERMINATE; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/** 3948c2ecf20Sopenharmony_ci * sdw_intel_acpi_scan() - SoundWire Intel init routine 3958c2ecf20Sopenharmony_ci * @parent_handle: ACPI parent handle 3968c2ecf20Sopenharmony_ci * @info: description of what firmware/DSDT tables expose 3978c2ecf20Sopenharmony_ci * 3988c2ecf20Sopenharmony_ci * This scans the namespace and queries firmware to figure out which 3998c2ecf20Sopenharmony_ci * links to enable. A follow-up use of sdw_intel_probe() and 4008c2ecf20Sopenharmony_ci * sdw_intel_startup() is required for creation of devices and bus 4018c2ecf20Sopenharmony_ci * startup 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ciint sdw_intel_acpi_scan(acpi_handle *parent_handle, 4048c2ecf20Sopenharmony_ci struct sdw_intel_acpi_info *info) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci acpi_status status; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci info->handle = NULL; 4098c2ecf20Sopenharmony_ci status = acpi_walk_namespace(ACPI_TYPE_DEVICE, 4108c2ecf20Sopenharmony_ci parent_handle, 1, 4118c2ecf20Sopenharmony_ci sdw_intel_acpi_cb, 4128c2ecf20Sopenharmony_ci NULL, info, NULL); 4138c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status) || info->handle == NULL) 4148c2ecf20Sopenharmony_ci return -ENODEV; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return sdw_intel_scan_controller(info); 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS(sdw_intel_acpi_scan, SOUNDWIRE_INTEL_INIT); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci/** 4218c2ecf20Sopenharmony_ci * sdw_intel_probe() - SoundWire Intel probe routine 4228c2ecf20Sopenharmony_ci * @res: resource data 4238c2ecf20Sopenharmony_ci * 4248c2ecf20Sopenharmony_ci * This registers a platform device for each Master handled by the controller, 4258c2ecf20Sopenharmony_ci * and SoundWire Master and Slave devices will be created by the platform 4268c2ecf20Sopenharmony_ci * device probe. All the information necessary is stored in the context, and 4278c2ecf20Sopenharmony_ci * the res argument pointer can be freed after this step. 4288c2ecf20Sopenharmony_ci * This function will be called after sdw_intel_acpi_scan() by SOF probe. 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_cistruct sdw_intel_ctx 4318c2ecf20Sopenharmony_ci*sdw_intel_probe(struct sdw_intel_res *res) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci return sdw_intel_probe_controller(res); 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS(sdw_intel_probe, SOUNDWIRE_INTEL_INIT); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci/** 4388c2ecf20Sopenharmony_ci * sdw_intel_startup() - SoundWire Intel startup 4398c2ecf20Sopenharmony_ci * @ctx: SoundWire context allocated in the probe 4408c2ecf20Sopenharmony_ci * 4418c2ecf20Sopenharmony_ci * Startup Intel SoundWire controller. This function will be called after 4428c2ecf20Sopenharmony_ci * Intel Audio DSP is powered up. 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_ciint sdw_intel_startup(struct sdw_intel_ctx *ctx) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci return sdw_intel_startup_controller(ctx); 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS(sdw_intel_startup, SOUNDWIRE_INTEL_INIT); 4498c2ecf20Sopenharmony_ci/** 4508c2ecf20Sopenharmony_ci * sdw_intel_exit() - SoundWire Intel exit 4518c2ecf20Sopenharmony_ci * @ctx: SoundWire context allocated in the probe 4528c2ecf20Sopenharmony_ci * 4538c2ecf20Sopenharmony_ci * Delete the controller instances created and cleanup 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_civoid sdw_intel_exit(struct sdw_intel_ctx *ctx) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci sdw_intel_cleanup(ctx); 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS(sdw_intel_exit, SOUNDWIRE_INTEL_INIT); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_civoid sdw_intel_process_wakeen_event(struct sdw_intel_ctx *ctx) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct sdw_intel_link_res *link; 4648c2ecf20Sopenharmony_ci u32 link_mask; 4658c2ecf20Sopenharmony_ci int i; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (!ctx->links) 4688c2ecf20Sopenharmony_ci return; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci link = ctx->links; 4718c2ecf20Sopenharmony_ci link_mask = ctx->link_mask; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* Startup SDW Master devices */ 4748c2ecf20Sopenharmony_ci for (i = 0; i < ctx->count; i++, link++) { 4758c2ecf20Sopenharmony_ci if (!(link_mask & BIT(i))) 4768c2ecf20Sopenharmony_ci continue; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci intel_master_process_wakeen_event(link->pdev); 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS(sdw_intel_process_wakeen_event, SOUNDWIRE_INTEL_INIT); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 4848c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel Soundwire Init Library"); 485