Commit | Line | Data |
---|---|---|
08c2a4bc PLB |
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> | |
ffd7e705 PLB |
9 | #include <linux/bits.h> |
10 | #include <linux/bitfield.h> | |
11 | #include <linux/device.h> | |
12 | #include <linux/errno.h> | |
08c2a4bc | 13 | #include <linux/export.h> |
ffd7e705 | 14 | #include <linux/fwnode.h> |
08c2a4bc PLB |
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 | { | |
ff4865b3 | 53 | struct acpi_device *adev = acpi_fetch_acpi_dev(info->handle); |
08c2a4bc PLB |
54 | int ret, i; |
55 | u8 count; | |
56 | ||
ff4865b3 | 57 | if (!adev) |
08c2a4bc PLB |
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; | |
08c2a4bc PLB |
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 | ||
ff4865b3 | 129 | if (!acpi_fetch_acpi_dev(handle)) { |
08c2a4bc PLB |
130 | pr_err("%s: Couldn't find ACPI handle\n", __func__); |
131 | return AE_NOT_FOUND; | |
132 | } | |
133 | ||
08c2a4bc PLB |
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 | ||
385f287f LY |
144 | /* found the correct SoundWire controller */ |
145 | info->handle = handle; | |
146 | ||
08c2a4bc PLB |
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; | |
78ea40ef LY |
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 | */ | |
08c2a4bc | 173 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, |
78ea40ef | 174 | parent_handle, 2, |
08c2a4bc PLB |
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"); |