brintos

brintos / linux-shallow public Read only

0
0
Text · 6.2 KiB · f1a4df6 Raw
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