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