.. | .. |
---|
2 | 2 | // Copyright(c) 2015-17 Intel Corporation. |
---|
3 | 3 | |
---|
4 | 4 | #include <linux/acpi.h> |
---|
| 5 | +#include <linux/of.h> |
---|
5 | 6 | #include <linux/soundwire/sdw.h> |
---|
6 | 7 | #include <linux/soundwire/sdw_type.h> |
---|
7 | 8 | #include "bus.h" |
---|
| 9 | +#include "sysfs_local.h" |
---|
8 | 10 | |
---|
9 | 11 | static void sdw_slave_release(struct device *dev) |
---|
10 | 12 | { |
---|
.. | .. |
---|
13 | 15 | kfree(slave); |
---|
14 | 16 | } |
---|
15 | 17 | |
---|
16 | | -static int sdw_slave_add(struct sdw_bus *bus, |
---|
17 | | - struct sdw_slave_id *id, struct fwnode_handle *fwnode) |
---|
| 18 | +struct device_type sdw_slave_type = { |
---|
| 19 | + .name = "sdw_slave", |
---|
| 20 | + .release = sdw_slave_release, |
---|
| 21 | + .uevent = sdw_slave_uevent, |
---|
| 22 | +}; |
---|
| 23 | + |
---|
| 24 | +int sdw_slave_add(struct sdw_bus *bus, |
---|
| 25 | + struct sdw_slave_id *id, struct fwnode_handle *fwnode) |
---|
18 | 26 | { |
---|
19 | 27 | struct sdw_slave *slave; |
---|
20 | 28 | int ret; |
---|
| 29 | + int i; |
---|
21 | 30 | |
---|
22 | 31 | slave = kzalloc(sizeof(*slave), GFP_KERNEL); |
---|
23 | 32 | if (!slave) |
---|
.. | .. |
---|
28 | 37 | slave->dev.parent = bus->dev; |
---|
29 | 38 | slave->dev.fwnode = fwnode; |
---|
30 | 39 | |
---|
31 | | - /* name shall be sdw:link:mfg:part:class:unique */ |
---|
32 | | - dev_set_name(&slave->dev, "sdw:%x:%x:%x:%x:%x", |
---|
33 | | - bus->link_id, id->mfg_id, id->part_id, |
---|
34 | | - id->class_id, id->unique_id); |
---|
| 40 | + if (id->unique_id == SDW_IGNORED_UNIQUE_ID) { |
---|
| 41 | + /* name shall be sdw:link:mfg:part:class */ |
---|
| 42 | + dev_set_name(&slave->dev, "sdw:%x:%x:%x:%x", |
---|
| 43 | + bus->link_id, id->mfg_id, id->part_id, |
---|
| 44 | + id->class_id); |
---|
| 45 | + } else { |
---|
| 46 | + /* name shall be sdw:link:mfg:part:class:unique */ |
---|
| 47 | + dev_set_name(&slave->dev, "sdw:%x:%x:%x:%x:%x", |
---|
| 48 | + bus->link_id, id->mfg_id, id->part_id, |
---|
| 49 | + id->class_id, id->unique_id); |
---|
| 50 | + } |
---|
35 | 51 | |
---|
36 | | - slave->dev.release = sdw_slave_release; |
---|
37 | 52 | slave->dev.bus = &sdw_bus_type; |
---|
| 53 | + slave->dev.of_node = of_node_get(to_of_node(fwnode)); |
---|
| 54 | + slave->dev.type = &sdw_slave_type; |
---|
| 55 | + slave->dev.groups = sdw_slave_status_attr_groups; |
---|
38 | 56 | slave->bus = bus; |
---|
39 | 57 | slave->status = SDW_SLAVE_UNATTACHED; |
---|
| 58 | + init_completion(&slave->enumeration_complete); |
---|
| 59 | + init_completion(&slave->initialization_complete); |
---|
40 | 60 | slave->dev_num = 0; |
---|
| 61 | + init_completion(&slave->probe_complete); |
---|
| 62 | + slave->probed = false; |
---|
| 63 | + slave->first_interrupt_done = false; |
---|
| 64 | + |
---|
| 65 | + for (i = 0; i < SDW_MAX_PORTS; i++) |
---|
| 66 | + init_completion(&slave->port_ready[i]); |
---|
41 | 67 | |
---|
42 | 68 | mutex_lock(&bus->bus_lock); |
---|
43 | 69 | list_add_tail(&slave->node, &bus->slaves); |
---|
.. | .. |
---|
55 | 81 | list_del(&slave->node); |
---|
56 | 82 | mutex_unlock(&bus->bus_lock); |
---|
57 | 83 | put_device(&slave->dev); |
---|
| 84 | + |
---|
| 85 | + return ret; |
---|
58 | 86 | } |
---|
| 87 | + sdw_slave_debugfs_init(slave); |
---|
59 | 88 | |
---|
60 | 89 | return ret; |
---|
61 | 90 | } |
---|
62 | 91 | |
---|
63 | 92 | #if IS_ENABLED(CONFIG_ACPI) |
---|
| 93 | + |
---|
| 94 | +static bool find_slave(struct sdw_bus *bus, |
---|
| 95 | + struct acpi_device *adev, |
---|
| 96 | + struct sdw_slave_id *id) |
---|
| 97 | +{ |
---|
| 98 | + unsigned long long addr; |
---|
| 99 | + unsigned int link_id; |
---|
| 100 | + acpi_status status; |
---|
| 101 | + |
---|
| 102 | + status = acpi_evaluate_integer(adev->handle, |
---|
| 103 | + METHOD_NAME__ADR, NULL, &addr); |
---|
| 104 | + |
---|
| 105 | + if (ACPI_FAILURE(status)) { |
---|
| 106 | + dev_err(bus->dev, "_ADR resolution failed: %x\n", |
---|
| 107 | + status); |
---|
| 108 | + return false; |
---|
| 109 | + } |
---|
| 110 | + |
---|
| 111 | + /* Extract link id from ADR, Bit 51 to 48 (included) */ |
---|
| 112 | + link_id = SDW_DISCO_LINK_ID(addr); |
---|
| 113 | + |
---|
| 114 | + /* Check for link_id match */ |
---|
| 115 | + if (link_id != bus->link_id) |
---|
| 116 | + return false; |
---|
| 117 | + |
---|
| 118 | + sdw_extract_slave_id(bus, addr, id); |
---|
| 119 | + |
---|
| 120 | + return true; |
---|
| 121 | +} |
---|
| 122 | + |
---|
64 | 123 | /* |
---|
65 | 124 | * sdw_acpi_find_slaves() - Find Slave devices in Master ACPI node |
---|
66 | 125 | * @bus: SDW bus instance |
---|
.. | .. |
---|
70 | 129 | int sdw_acpi_find_slaves(struct sdw_bus *bus) |
---|
71 | 130 | { |
---|
72 | 131 | struct acpi_device *adev, *parent; |
---|
| 132 | + struct acpi_device *adev2, *parent2; |
---|
73 | 133 | |
---|
74 | 134 | parent = ACPI_COMPANION(bus->dev); |
---|
75 | 135 | if (!parent) { |
---|
.. | .. |
---|
78 | 138 | } |
---|
79 | 139 | |
---|
80 | 140 | list_for_each_entry(adev, &parent->children, node) { |
---|
81 | | - unsigned long long addr; |
---|
82 | 141 | struct sdw_slave_id id; |
---|
83 | | - unsigned int link_id; |
---|
84 | | - acpi_status status; |
---|
| 142 | + struct sdw_slave_id id2; |
---|
| 143 | + bool ignore_unique_id = true; |
---|
85 | 144 | |
---|
86 | | - status = acpi_evaluate_integer(adev->handle, |
---|
87 | | - METHOD_NAME__ADR, NULL, &addr); |
---|
88 | | - |
---|
89 | | - if (ACPI_FAILURE(status)) { |
---|
90 | | - dev_err(bus->dev, "_ADR resolution failed: %x\n", |
---|
91 | | - status); |
---|
92 | | - return status; |
---|
93 | | - } |
---|
94 | | - |
---|
95 | | - /* Extract link id from ADR, Bit 51 to 48 (included) */ |
---|
96 | | - link_id = (addr >> 48) & GENMASK(3, 0); |
---|
97 | | - |
---|
98 | | - /* Check for link_id match */ |
---|
99 | | - if (link_id != bus->link_id) |
---|
| 145 | + if (!find_slave(bus, adev, &id)) |
---|
100 | 146 | continue; |
---|
101 | 147 | |
---|
102 | | - sdw_extract_slave_id(bus, addr, &id); |
---|
| 148 | + /* brute-force O(N^2) search for duplicates */ |
---|
| 149 | + parent2 = parent; |
---|
| 150 | + list_for_each_entry(adev2, &parent2->children, node) { |
---|
| 151 | + |
---|
| 152 | + if (adev == adev2) |
---|
| 153 | + continue; |
---|
| 154 | + |
---|
| 155 | + if (!find_slave(bus, adev2, &id2)) |
---|
| 156 | + continue; |
---|
| 157 | + |
---|
| 158 | + if (id.sdw_version != id2.sdw_version || |
---|
| 159 | + id.mfg_id != id2.mfg_id || |
---|
| 160 | + id.part_id != id2.part_id || |
---|
| 161 | + id.class_id != id2.class_id) |
---|
| 162 | + continue; |
---|
| 163 | + |
---|
| 164 | + if (id.unique_id != id2.unique_id) { |
---|
| 165 | + dev_dbg(bus->dev, |
---|
| 166 | + "Valid unique IDs %x %x for Slave mfg %x part %d\n", |
---|
| 167 | + id.unique_id, id2.unique_id, |
---|
| 168 | + id.mfg_id, id.part_id); |
---|
| 169 | + ignore_unique_id = false; |
---|
| 170 | + } else { |
---|
| 171 | + dev_err(bus->dev, |
---|
| 172 | + "Invalid unique IDs %x %x for Slave mfg %x part %d\n", |
---|
| 173 | + id.unique_id, id2.unique_id, |
---|
| 174 | + id.mfg_id, id.part_id); |
---|
| 175 | + return -ENODEV; |
---|
| 176 | + } |
---|
| 177 | + } |
---|
| 178 | + |
---|
| 179 | + if (ignore_unique_id) |
---|
| 180 | + id.unique_id = SDW_IGNORED_UNIQUE_ID; |
---|
103 | 181 | |
---|
104 | 182 | /* |
---|
105 | 183 | * don't error check for sdw_slave_add as we want to continue |
---|
.. | .. |
---|
112 | 190 | } |
---|
113 | 191 | |
---|
114 | 192 | #endif |
---|
| 193 | + |
---|
| 194 | +/* |
---|
| 195 | + * sdw_of_find_slaves() - Find Slave devices in master device tree node |
---|
| 196 | + * @bus: SDW bus instance |
---|
| 197 | + * |
---|
| 198 | + * Scans Master DT node for SDW child Slave devices and registers it. |
---|
| 199 | + */ |
---|
| 200 | +int sdw_of_find_slaves(struct sdw_bus *bus) |
---|
| 201 | +{ |
---|
| 202 | + struct device *dev = bus->dev; |
---|
| 203 | + struct device_node *node; |
---|
| 204 | + |
---|
| 205 | + for_each_child_of_node(bus->dev->of_node, node) { |
---|
| 206 | + int link_id, ret, len; |
---|
| 207 | + unsigned int sdw_version; |
---|
| 208 | + const char *compat = NULL; |
---|
| 209 | + struct sdw_slave_id id; |
---|
| 210 | + const __be32 *addr; |
---|
| 211 | + |
---|
| 212 | + compat = of_get_property(node, "compatible", NULL); |
---|
| 213 | + if (!compat) |
---|
| 214 | + continue; |
---|
| 215 | + |
---|
| 216 | + ret = sscanf(compat, "sdw%01x%04hx%04hx%02hhx", &sdw_version, |
---|
| 217 | + &id.mfg_id, &id.part_id, &id.class_id); |
---|
| 218 | + |
---|
| 219 | + if (ret != 4) { |
---|
| 220 | + dev_err(dev, "Invalid compatible string found %s\n", |
---|
| 221 | + compat); |
---|
| 222 | + continue; |
---|
| 223 | + } |
---|
| 224 | + |
---|
| 225 | + addr = of_get_property(node, "reg", &len); |
---|
| 226 | + if (!addr || (len < 2 * sizeof(u32))) { |
---|
| 227 | + dev_err(dev, "Invalid Link and Instance ID\n"); |
---|
| 228 | + continue; |
---|
| 229 | + } |
---|
| 230 | + |
---|
| 231 | + link_id = be32_to_cpup(addr++); |
---|
| 232 | + id.unique_id = be32_to_cpup(addr); |
---|
| 233 | + id.sdw_version = sdw_version; |
---|
| 234 | + |
---|
| 235 | + /* Check for link_id match */ |
---|
| 236 | + if (link_id != bus->link_id) |
---|
| 237 | + continue; |
---|
| 238 | + |
---|
| 239 | + sdw_slave_add(bus, &id, of_fwnode_handle(node)); |
---|
| 240 | + } |
---|
| 241 | + |
---|
| 242 | + return 0; |
---|
| 243 | +} |
---|