Pierre-Louis Bossart | 08c2a4b | 2021-03-01 18:31:24 -0600 | [diff] [blame] | 1 | // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) |
| 2 | // Copyright(c) 2015-2021 Intel Corporation. |
| 3 | |
| 4 | /* |
| 5 | * SDW Intel ACPI scan helpers |
| 6 | */ |
| 7 | |
| 8 | #include <linux/acpi.h> |
Pierre-Louis Bossart | ffd7e70 | 2021-03-01 18:31:25 -0600 | [diff] [blame] | 9 | #include <linux/bits.h> |
| 10 | #include <linux/bitfield.h> |
| 11 | #include <linux/device.h> |
| 12 | #include <linux/errno.h> |
Pierre-Louis Bossart | 08c2a4b | 2021-03-01 18:31:24 -0600 | [diff] [blame] | 13 | #include <linux/export.h> |
Pierre-Louis Bossart | ffd7e70 | 2021-03-01 18:31:25 -0600 | [diff] [blame] | 14 | #include <linux/fwnode.h> |
Pierre-Louis Bossart | 08c2a4b | 2021-03-01 18:31:24 -0600 | [diff] [blame] | 15 | #include <linux/module.h> |
| 16 | #include <linux/soundwire/sdw_intel.h> |
| 17 | #include <linux/string.h> |
| 18 | |
| 19 | #define SDW_LINK_TYPE 4 /* from Intel ACPI documentation */ |
| 20 | #define SDW_MAX_LINKS 4 |
| 21 | |
| 22 | static int ctrl_link_mask; |
| 23 | module_param_named(sdw_link_mask, ctrl_link_mask, int, 0444); |
| 24 | MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)"); |
| 25 | |
| 26 | static bool is_link_enabled(struct fwnode_handle *fw_node, int i) |
| 27 | { |
| 28 | struct fwnode_handle *link; |
| 29 | char name[32]; |
| 30 | u32 quirk_mask = 0; |
| 31 | |
| 32 | /* Find master handle */ |
| 33 | snprintf(name, sizeof(name), |
| 34 | "mipi-sdw-link-%d-subproperties", i); |
| 35 | |
| 36 | link = fwnode_get_named_child_node(fw_node, name); |
| 37 | if (!link) |
| 38 | return false; |
| 39 | |
| 40 | fwnode_property_read_u32(link, |
| 41 | "intel-quirk-mask", |
| 42 | &quirk_mask); |
| 43 | |
| 44 | if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE) |
| 45 | return false; |
| 46 | |
| 47 | return true; |
| 48 | } |
| 49 | |
| 50 | static int |
| 51 | sdw_intel_scan_controller(struct sdw_intel_acpi_info *info) |
| 52 | { |
Rafael J. Wysocki | ff4865b | 2022-01-26 20:48:49 +0100 | [diff] [blame] | 53 | struct acpi_device *adev = acpi_fetch_acpi_dev(info->handle); |
Pierre-Louis Bossart | 08c2a4b | 2021-03-01 18:31:24 -0600 | [diff] [blame] | 54 | int ret, i; |
| 55 | u8 count; |
| 56 | |
Rafael J. Wysocki | ff4865b | 2022-01-26 20:48:49 +0100 | [diff] [blame] | 57 | if (!adev) |
Pierre-Louis Bossart | 08c2a4b | 2021-03-01 18:31:24 -0600 | [diff] [blame] | 58 | return -EINVAL; |
| 59 | |
| 60 | /* Found controller, find links supported */ |
| 61 | count = 0; |
| 62 | ret = fwnode_property_read_u8_array(acpi_fwnode_handle(adev), |
| 63 | "mipi-sdw-master-count", &count, 1); |
| 64 | |
| 65 | /* |
| 66 | * In theory we could check the number of links supported in |
| 67 | * hardware, but in that step we cannot assume SoundWire IP is |
| 68 | * powered. |
| 69 | * |
| 70 | * In addition, if the BIOS doesn't even provide this |
| 71 | * 'master-count' property then all the inits based on link |
| 72 | * masks will fail as well. |
| 73 | * |
| 74 | * We will check the hardware capabilities in the startup() step |
| 75 | */ |
| 76 | |
| 77 | if (ret) { |
| 78 | dev_err(&adev->dev, |
| 79 | "Failed to read mipi-sdw-master-count: %d\n", ret); |
| 80 | return -EINVAL; |
| 81 | } |
| 82 | |
| 83 | /* Check count is within bounds */ |
| 84 | if (count > SDW_MAX_LINKS) { |
| 85 | dev_err(&adev->dev, "Link count %d exceeds max %d\n", |
| 86 | count, SDW_MAX_LINKS); |
| 87 | return -EINVAL; |
| 88 | } |
| 89 | |
| 90 | if (!count) { |
| 91 | dev_warn(&adev->dev, "No SoundWire links detected\n"); |
| 92 | return -EINVAL; |
| 93 | } |
| 94 | dev_dbg(&adev->dev, "ACPI reports %d SDW Link devices\n", count); |
| 95 | |
| 96 | info->count = count; |
| 97 | info->link_mask = 0; |
| 98 | |
| 99 | for (i = 0; i < count; i++) { |
| 100 | if (ctrl_link_mask && !(ctrl_link_mask & BIT(i))) { |
| 101 | dev_dbg(&adev->dev, |
| 102 | "Link %d masked, will not be enabled\n", i); |
| 103 | continue; |
| 104 | } |
| 105 | |
| 106 | if (!is_link_enabled(acpi_fwnode_handle(adev), i)) { |
| 107 | dev_dbg(&adev->dev, |
| 108 | "Link %d not selected in firmware\n", i); |
| 109 | continue; |
| 110 | } |
| 111 | |
| 112 | info->link_mask |= BIT(i); |
| 113 | } |
| 114 | |
| 115 | return 0; |
| 116 | } |
| 117 | |
| 118 | static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, |
| 119 | void *cdata, void **return_value) |
| 120 | { |
| 121 | struct sdw_intel_acpi_info *info = cdata; |
Pierre-Louis Bossart | 08c2a4b | 2021-03-01 18:31:24 -0600 | [diff] [blame] | 122 | acpi_status status; |
| 123 | u64 adr; |
| 124 | |
| 125 | status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); |
| 126 | if (ACPI_FAILURE(status)) |
| 127 | return AE_OK; /* keep going */ |
| 128 | |
Rafael J. Wysocki | ff4865b | 2022-01-26 20:48:49 +0100 | [diff] [blame] | 129 | if (!acpi_fetch_acpi_dev(handle)) { |
Pierre-Louis Bossart | 08c2a4b | 2021-03-01 18:31:24 -0600 | [diff] [blame] | 130 | pr_err("%s: Couldn't find ACPI handle\n", __func__); |
| 131 | return AE_NOT_FOUND; |
| 132 | } |
| 133 | |
Pierre-Louis Bossart | 08c2a4b | 2021-03-01 18:31:24 -0600 | [diff] [blame] | 134 | /* |
| 135 | * On some Intel platforms, multiple children of the HDAS |
| 136 | * device can be found, but only one of them is the SoundWire |
| 137 | * controller. The SNDW device is always exposed with |
| 138 | * Name(_ADR, 0x40000000), with bits 31..28 representing the |
| 139 | * SoundWire link so filter accordingly |
| 140 | */ |
| 141 | if (FIELD_GET(GENMASK(31, 28), adr) != SDW_LINK_TYPE) |
| 142 | return AE_OK; /* keep going */ |
| 143 | |
Libin Yang | 385f287 | 2021-12-21 09:08:16 +0800 | [diff] [blame] | 144 | /* found the correct SoundWire controller */ |
| 145 | info->handle = handle; |
| 146 | |
Pierre-Louis Bossart | 08c2a4b | 2021-03-01 18:31:24 -0600 | [diff] [blame] | 147 | /* device found, stop namespace walk */ |
| 148 | return AE_CTRL_TERMINATE; |
| 149 | } |
| 150 | |
| 151 | /** |
| 152 | * sdw_intel_acpi_scan() - SoundWire Intel init routine |
| 153 | * @parent_handle: ACPI parent handle |
| 154 | * @info: description of what firmware/DSDT tables expose |
| 155 | * |
| 156 | * This scans the namespace and queries firmware to figure out which |
| 157 | * links to enable. A follow-up use of sdw_intel_probe() and |
| 158 | * sdw_intel_startup() is required for creation of devices and bus |
| 159 | * startup |
| 160 | */ |
| 161 | int sdw_intel_acpi_scan(acpi_handle *parent_handle, |
| 162 | struct sdw_intel_acpi_info *info) |
| 163 | { |
| 164 | acpi_status status; |
| 165 | |
| 166 | info->handle = NULL; |
Libin Yang | 78ea40e | 2021-12-21 09:08:17 +0800 | [diff] [blame] | 167 | /* |
| 168 | * In the HDAS ACPI scope, 'SNDW' may be either the child of |
| 169 | * 'HDAS' or the grandchild of 'HDAS'. So let's go through |
| 170 | * the ACPI from 'HDAS' at max depth of 2 to find the 'SNDW' |
| 171 | * device. |
| 172 | */ |
Pierre-Louis Bossart | 08c2a4b | 2021-03-01 18:31:24 -0600 | [diff] [blame] | 173 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, |
Libin Yang | 78ea40e | 2021-12-21 09:08:17 +0800 | [diff] [blame] | 174 | parent_handle, 2, |
Pierre-Louis Bossart | 08c2a4b | 2021-03-01 18:31:24 -0600 | [diff] [blame] | 175 | sdw_intel_acpi_cb, |
| 176 | NULL, info, NULL); |
| 177 | if (ACPI_FAILURE(status) || info->handle == NULL) |
| 178 | return -ENODEV; |
| 179 | |
| 180 | return sdw_intel_scan_controller(info); |
| 181 | } |
| 182 | EXPORT_SYMBOL_NS(sdw_intel_acpi_scan, SND_INTEL_SOUNDWIRE_ACPI); |
| 183 | |
| 184 | MODULE_LICENSE("Dual BSD/GPL"); |
| 185 | MODULE_DESCRIPTION("Intel Soundwire ACPI helpers"); |