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