Commit | Line | Data |
---|---|---|
7c3cd189 VK |
1 | // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) |
2 | // Copyright(c) 2015-17 Intel Corporation. | |
3 | ||
4 | #include <linux/acpi.h> | |
a2e48458 | 5 | #include <linux/of.h> |
7c3cd189 VK |
6 | #include <linux/soundwire/sdw.h> |
7 | #include <linux/soundwire/sdw_type.h> | |
0c673d28 | 8 | #include <sound/sdca.h> |
7c3cd189 | 9 | #include "bus.h" |
0173f525 | 10 | #include "sysfs_local.h" |
7c3cd189 VK |
11 | |
12 | static void sdw_slave_release(struct device *dev) | |
13 | { | |
14 | struct sdw_slave *slave = dev_to_sdw_dev(dev); | |
15 | ||
aac2f836 | 16 | of_node_put(slave->dev.of_node); |
bd29c00e | 17 | mutex_destroy(&slave->sdw_dev_lock); |
7c3cd189 VK |
18 | kfree(slave); |
19 | } | |
20 | ||
99fea943 | 21 | const struct device_type sdw_slave_type = { |
90acca1d PLB |
22 | .name = "sdw_slave", |
23 | .release = sdw_slave_release, | |
24 | .uevent = sdw_slave_uevent, | |
25 | }; | |
26 | ||
fcb9d730 SK |
27 | int sdw_slave_add(struct sdw_bus *bus, |
28 | struct sdw_slave_id *id, struct fwnode_handle *fwnode) | |
7c3cd189 VK |
29 | { |
30 | struct sdw_slave *slave; | |
31 | int ret; | |
60737558 | 32 | int i; |
7c3cd189 VK |
33 | |
34 | slave = kzalloc(sizeof(*slave), GFP_KERNEL); | |
35 | if (!slave) | |
36 | return -ENOMEM; | |
37 | ||
38 | /* Initialize data structure */ | |
39 | memcpy(&slave->id, id, sizeof(*id)); | |
40 | slave->dev.parent = bus->dev; | |
41 | slave->dev.fwnode = fwnode; | |
42 | ||
2e8c4ad1 | 43 | if (id->unique_id == SDW_IGNORED_UNIQUE_ID) { |
8a8a9ac8 KK |
44 | /* name shall be sdw:ctrl:link:mfg:part:class */ |
45 | dev_set_name(&slave->dev, "sdw:%01x:%01x:%04x:%04x:%02x", | |
46 | bus->controller_id, bus->link_id, id->mfg_id, id->part_id, | |
2e8c4ad1 PLB |
47 | id->class_id); |
48 | } else { | |
8a8a9ac8 KK |
49 | /* name shall be sdw:ctrl:link:mfg:part:class:unique */ |
50 | dev_set_name(&slave->dev, "sdw:%01x:%01x:%04x:%04x:%02x:%01x", | |
51 | bus->controller_id, bus->link_id, id->mfg_id, id->part_id, | |
2e8c4ad1 PLB |
52 | id->class_id, id->unique_id); |
53 | } | |
7c3cd189 | 54 | |
7c3cd189 | 55 | slave->dev.bus = &sdw_bus_type; |
a2e48458 | 56 | slave->dev.of_node = of_node_get(to_of_node(fwnode)); |
90acca1d | 57 | slave->dev.type = &sdw_slave_type; |
0173f525 | 58 | slave->dev.groups = sdw_slave_status_attr_groups; |
7c3cd189 VK |
59 | slave->bus = bus; |
60 | slave->status = SDW_SLAVE_UNATTACHED; | |
fb9469e5 | 61 | init_completion(&slave->enumeration_complete); |
a90def06 | 62 | init_completion(&slave->initialization_complete); |
7c3cd189 | 63 | slave->dev_num = 0; |
2140b66b | 64 | slave->probed = false; |
c2819e19 | 65 | slave->first_interrupt_done = false; |
bd29c00e | 66 | mutex_init(&slave->sdw_dev_lock); |
7c3cd189 | 67 | |
60737558 PLB |
68 | for (i = 0; i < SDW_MAX_PORTS; i++) |
69 | init_completion(&slave->port_ready[i]); | |
70 | ||
7c3cd189 VK |
71 | mutex_lock(&bus->bus_lock); |
72 | list_add_tail(&slave->node, &bus->slaves); | |
73 | mutex_unlock(&bus->bus_lock); | |
74 | ||
0c673d28 PLB |
75 | /* |
76 | * The Soundwire driver probe may optionally register SDCA | |
77 | * sub-devices, one per Function. This means the information | |
78 | * on the SDCA revision and the number/type of Functions need | |
79 | * to be extracted from platform firmware before the SoundWire | |
80 | * driver probe, and as a consequence before the SoundWire | |
81 | * device_register() below. | |
82 | */ | |
83 | sdca_lookup_interface_revision(slave); | |
84 | sdca_lookup_functions(slave); | |
85 | ||
7c3cd189 VK |
86 | ret = device_register(&slave->dev); |
87 | if (ret) { | |
88 | dev_err(bus->dev, "Failed to add slave: ret %d\n", ret); | |
89 | ||
90 | /* | |
91 | * On err, don't free but drop ref as this will be freed | |
92 | * when release method is invoked. | |
93 | */ | |
94 | mutex_lock(&bus->bus_lock); | |
95 | list_del(&slave->node); | |
96 | mutex_unlock(&bus->bus_lock); | |
97 | put_device(&slave->dev); | |
8893ab5e PLB |
98 | |
99 | return ret; | |
7c3cd189 | 100 | } |
bf03473d | 101 | sdw_slave_debugfs_init(slave); |
7c3cd189 VK |
102 | |
103 | return ret; | |
104 | } | |
01ad444e | 105 | EXPORT_SYMBOL(sdw_slave_add); |
7c3cd189 VK |
106 | |
107 | #if IS_ENABLED(CONFIG_ACPI) | |
de5b174b PLB |
108 | |
109 | static bool find_slave(struct sdw_bus *bus, | |
110 | struct acpi_device *adev, | |
111 | struct sdw_slave_id *id) | |
112 | { | |
de5b174b | 113 | unsigned int link_id; |
b6212f9b PLB |
114 | u64 addr; |
115 | int ret; | |
de5b174b | 116 | |
b6212f9b PLB |
117 | ret = acpi_get_local_u64_address(adev->handle, &addr); |
118 | if (ret < 0) | |
de5b174b | 119 | return false; |
de5b174b | 120 | |
6558b667 VK |
121 | if (bus->ops->override_adr) |
122 | addr = bus->ops->override_adr(bus, addr); | |
123 | ||
124 | if (!addr) | |
125 | return false; | |
126 | ||
de5b174b | 127 | /* Extract link id from ADR, Bit 51 to 48 (included) */ |
bd6a024f | 128 | link_id = SDW_DISCO_LINK_ID(addr); |
de5b174b PLB |
129 | |
130 | /* Check for link_id match */ | |
131 | if (link_id != bus->link_id) | |
132 | return false; | |
133 | ||
134 | sdw_extract_slave_id(bus, addr, id); | |
135 | ||
136 | return true; | |
137 | } | |
138 | ||
9089d1a4 RW |
139 | struct sdw_acpi_child_walk_data { |
140 | struct sdw_bus *bus; | |
141 | struct acpi_device *adev; | |
142 | struct sdw_slave_id id; | |
143 | bool ignore_unique_id; | |
144 | }; | |
145 | ||
146 | static int sdw_acpi_check_duplicate(struct acpi_device *adev, void *data) | |
147 | { | |
148 | struct sdw_acpi_child_walk_data *cwd = data; | |
149 | struct sdw_bus *bus = cwd->bus; | |
150 | struct sdw_slave_id id; | |
151 | ||
152 | if (adev == cwd->adev) | |
153 | return 0; | |
154 | ||
155 | if (!find_slave(bus, adev, &id)) | |
156 | return 0; | |
157 | ||
158 | if (cwd->id.sdw_version != id.sdw_version || cwd->id.mfg_id != id.mfg_id || | |
159 | cwd->id.part_id != id.part_id || cwd->id.class_id != id.class_id) | |
160 | return 0; | |
161 | ||
162 | if (cwd->id.unique_id != id.unique_id) { | |
163 | dev_dbg(bus->dev, | |
164 | "Valid unique IDs 0x%x 0x%x for Slave mfg_id 0x%04x, part_id 0x%04x\n", | |
165 | cwd->id.unique_id, id.unique_id, cwd->id.mfg_id, | |
166 | cwd->id.part_id); | |
167 | cwd->ignore_unique_id = false; | |
168 | return 0; | |
169 | } | |
170 | ||
171 | dev_err(bus->dev, | |
172 | "Invalid unique IDs 0x%x 0x%x for Slave mfg_id 0x%04x, part_id 0x%04x\n", | |
173 | cwd->id.unique_id, id.unique_id, cwd->id.mfg_id, cwd->id.part_id); | |
174 | return -ENODEV; | |
175 | } | |
176 | ||
177 | static int sdw_acpi_find_one(struct acpi_device *adev, void *data) | |
178 | { | |
179 | struct sdw_bus *bus = data; | |
180 | struct sdw_acpi_child_walk_data cwd = { | |
181 | .bus = bus, | |
182 | .adev = adev, | |
183 | .ignore_unique_id = true, | |
184 | }; | |
185 | int ret; | |
186 | ||
187 | if (!find_slave(bus, adev, &cwd.id)) | |
188 | return 0; | |
189 | ||
190 | /* Brute-force O(N^2) search for duplicates. */ | |
191 | ret = acpi_dev_for_each_child(ACPI_COMPANION(bus->dev), | |
192 | sdw_acpi_check_duplicate, &cwd); | |
193 | if (ret) | |
194 | return ret; | |
195 | ||
196 | if (cwd.ignore_unique_id) | |
197 | cwd.id.unique_id = SDW_IGNORED_UNIQUE_ID; | |
198 | ||
199 | /* Ignore errors and continue. */ | |
200 | sdw_slave_add(bus, &cwd.id, acpi_fwnode_handle(adev)); | |
201 | return 0; | |
202 | } | |
203 | ||
7c3cd189 VK |
204 | /* |
205 | * sdw_acpi_find_slaves() - Find Slave devices in Master ACPI node | |
206 | * @bus: SDW bus instance | |
207 | * | |
208 | * Scans Master ACPI node for SDW child Slave devices and registers it. | |
209 | */ | |
210 | int sdw_acpi_find_slaves(struct sdw_bus *bus) | |
211 | { | |
9089d1a4 | 212 | struct acpi_device *parent; |
7c3cd189 VK |
213 | |
214 | parent = ACPI_COMPANION(bus->dev); | |
215 | if (!parent) { | |
216 | dev_err(bus->dev, "Can't find parent for acpi bind\n"); | |
217 | return -ENODEV; | |
218 | } | |
219 | ||
9089d1a4 | 220 | return acpi_dev_for_each_child(parent, sdw_acpi_find_one, bus); |
7c3cd189 VK |
221 | } |
222 | ||
223 | #endif | |
a2e48458 SK |
224 | |
225 | /* | |
226 | * sdw_of_find_slaves() - Find Slave devices in master device tree node | |
227 | * @bus: SDW bus instance | |
228 | * | |
229 | * Scans Master DT node for SDW child Slave devices and registers it. | |
230 | */ | |
231 | int sdw_of_find_slaves(struct sdw_bus *bus) | |
232 | { | |
233 | struct device *dev = bus->dev; | |
234 | struct device_node *node; | |
235 | ||
236 | for_each_child_of_node(bus->dev->of_node, node) { | |
7b47ad33 PLB |
237 | int link_id, ret, len; |
238 | unsigned int sdw_version; | |
a2e48458 SK |
239 | const char *compat = NULL; |
240 | struct sdw_slave_id id; | |
241 | const __be32 *addr; | |
242 | ||
243 | compat = of_get_property(node, "compatible", NULL); | |
244 | if (!compat) | |
245 | continue; | |
246 | ||
247 | ret = sscanf(compat, "sdw%01x%04hx%04hx%02hhx", &sdw_version, | |
248 | &id.mfg_id, &id.part_id, &id.class_id); | |
249 | ||
250 | if (ret != 4) { | |
251 | dev_err(dev, "Invalid compatible string found %s\n", | |
252 | compat); | |
253 | continue; | |
254 | } | |
255 | ||
256 | addr = of_get_property(node, "reg", &len); | |
257 | if (!addr || (len < 2 * sizeof(u32))) { | |
258 | dev_err(dev, "Invalid Link and Instance ID\n"); | |
259 | continue; | |
260 | } | |
261 | ||
262 | link_id = be32_to_cpup(addr++); | |
263 | id.unique_id = be32_to_cpup(addr); | |
264 | id.sdw_version = sdw_version; | |
265 | ||
266 | /* Check for link_id match */ | |
267 | if (link_id != bus->link_id) | |
268 | continue; | |
269 | ||
270 | sdw_slave_add(bus, &id, of_fwnode_handle(node)); | |
271 | } | |
272 | ||
273 | return 0; | |
274 | } | |
0c673d28 | 275 | |
cdd30ebb | 276 | MODULE_IMPORT_NS("SND_SOC_SDCA"); |