18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Greybus bundles
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2014-2015 Google Inc.
68c2ecf20Sopenharmony_ci * Copyright 2014-2015 Linaro Ltd.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/greybus.h>
108c2ecf20Sopenharmony_ci#include "greybus_trace.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic ssize_t bundle_class_show(struct device *dev,
138c2ecf20Sopenharmony_ci				 struct device_attribute *attr, char *buf)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	struct gb_bundle *bundle = to_gb_bundle(dev);
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%02x\n", bundle->class);
188c2ecf20Sopenharmony_ci}
198c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(bundle_class);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic ssize_t bundle_id_show(struct device *dev,
228c2ecf20Sopenharmony_ci			      struct device_attribute *attr, char *buf)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	struct gb_bundle *bundle = to_gb_bundle(dev);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", bundle->id);
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(bundle_id);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic ssize_t state_show(struct device *dev, struct device_attribute *attr,
318c2ecf20Sopenharmony_ci			  char *buf)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct gb_bundle *bundle = to_gb_bundle(dev);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	if (!bundle->state)
368c2ecf20Sopenharmony_ci		return sprintf(buf, "\n");
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", bundle->state);
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic ssize_t state_store(struct device *dev, struct device_attribute *attr,
428c2ecf20Sopenharmony_ci			   const char *buf, size_t size)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct gb_bundle *bundle = to_gb_bundle(dev);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	kfree(bundle->state);
478c2ecf20Sopenharmony_ci	bundle->state = kstrdup(buf, GFP_KERNEL);
488c2ecf20Sopenharmony_ci	if (!bundle->state)
498c2ecf20Sopenharmony_ci		return -ENOMEM;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/* Tell userspace that the file contents changed */
528c2ecf20Sopenharmony_ci	sysfs_notify(&bundle->dev.kobj, NULL, "state");
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return size;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(state);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic struct attribute *bundle_attrs[] = {
598c2ecf20Sopenharmony_ci	&dev_attr_bundle_class.attr,
608c2ecf20Sopenharmony_ci	&dev_attr_bundle_id.attr,
618c2ecf20Sopenharmony_ci	&dev_attr_state.attr,
628c2ecf20Sopenharmony_ci	NULL,
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(bundle);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic struct gb_bundle *gb_bundle_find(struct gb_interface *intf,
688c2ecf20Sopenharmony_ci					u8 bundle_id)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct gb_bundle *bundle;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	list_for_each_entry(bundle, &intf->bundles, links) {
738c2ecf20Sopenharmony_ci		if (bundle->id == bundle_id)
748c2ecf20Sopenharmony_ci			return bundle;
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	return NULL;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic void gb_bundle_release(struct device *dev)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct gb_bundle *bundle = to_gb_bundle(dev);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	trace_gb_bundle_release(bundle);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	kfree(bundle->state);
878c2ecf20Sopenharmony_ci	kfree(bundle->cport_desc);
888c2ecf20Sopenharmony_ci	kfree(bundle);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
928c2ecf20Sopenharmony_cistatic void gb_bundle_disable_all_connections(struct gb_bundle *bundle)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct gb_connection *connection;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	list_for_each_entry(connection, &bundle->connections, bundle_links)
978c2ecf20Sopenharmony_ci		gb_connection_disable(connection);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic void gb_bundle_enable_all_connections(struct gb_bundle *bundle)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct gb_connection *connection;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	list_for_each_entry(connection, &bundle->connections, bundle_links)
1058c2ecf20Sopenharmony_ci		gb_connection_enable(connection);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic int gb_bundle_suspend(struct device *dev)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct gb_bundle *bundle = to_gb_bundle(dev);
1118c2ecf20Sopenharmony_ci	const struct dev_pm_ops *pm = dev->driver->pm;
1128c2ecf20Sopenharmony_ci	int ret;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (pm && pm->runtime_suspend) {
1158c2ecf20Sopenharmony_ci		ret = pm->runtime_suspend(&bundle->dev);
1168c2ecf20Sopenharmony_ci		if (ret)
1178c2ecf20Sopenharmony_ci			return ret;
1188c2ecf20Sopenharmony_ci	} else {
1198c2ecf20Sopenharmony_ci		gb_bundle_disable_all_connections(bundle);
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	ret = gb_control_bundle_suspend(bundle->intf->control, bundle->id);
1238c2ecf20Sopenharmony_ci	if (ret) {
1248c2ecf20Sopenharmony_ci		if (pm && pm->runtime_resume)
1258c2ecf20Sopenharmony_ci			ret = pm->runtime_resume(dev);
1268c2ecf20Sopenharmony_ci		else
1278c2ecf20Sopenharmony_ci			gb_bundle_enable_all_connections(bundle);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		return ret;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int gb_bundle_resume(struct device *dev)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct gb_bundle *bundle = to_gb_bundle(dev);
1388c2ecf20Sopenharmony_ci	const struct dev_pm_ops *pm = dev->driver->pm;
1398c2ecf20Sopenharmony_ci	int ret;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	ret = gb_control_bundle_resume(bundle->intf->control, bundle->id);
1428c2ecf20Sopenharmony_ci	if (ret)
1438c2ecf20Sopenharmony_ci		return ret;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (pm && pm->runtime_resume) {
1468c2ecf20Sopenharmony_ci		ret = pm->runtime_resume(dev);
1478c2ecf20Sopenharmony_ci		if (ret)
1488c2ecf20Sopenharmony_ci			return ret;
1498c2ecf20Sopenharmony_ci	} else {
1508c2ecf20Sopenharmony_ci		gb_bundle_enable_all_connections(bundle);
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return 0;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic int gb_bundle_idle(struct device *dev)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(dev);
1598c2ecf20Sopenharmony_ci	pm_request_autosuspend(dev);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return 0;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci#endif
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic const struct dev_pm_ops gb_bundle_pm_ops = {
1668c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(gb_bundle_suspend, gb_bundle_resume, gb_bundle_idle)
1678c2ecf20Sopenharmony_ci};
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistruct device_type greybus_bundle_type = {
1708c2ecf20Sopenharmony_ci	.name =		"greybus_bundle",
1718c2ecf20Sopenharmony_ci	.release =	gb_bundle_release,
1728c2ecf20Sopenharmony_ci	.pm =		&gb_bundle_pm_ops,
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/*
1768c2ecf20Sopenharmony_ci * Create a gb_bundle structure to represent a discovered
1778c2ecf20Sopenharmony_ci * bundle.  Returns a pointer to the new bundle or a null
1788c2ecf20Sopenharmony_ci * pointer if a failure occurs due to memory exhaustion.
1798c2ecf20Sopenharmony_ci */
1808c2ecf20Sopenharmony_cistruct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id,
1818c2ecf20Sopenharmony_ci				   u8 class)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct gb_bundle *bundle;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (bundle_id == BUNDLE_ID_NONE) {
1868c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "can't use bundle id %u\n", bundle_id);
1878c2ecf20Sopenharmony_ci		return NULL;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	/*
1918c2ecf20Sopenharmony_ci	 * Reject any attempt to reuse a bundle id.  We initialize
1928c2ecf20Sopenharmony_ci	 * these serially, so there's no need to worry about keeping
1938c2ecf20Sopenharmony_ci	 * the interface bundle list locked here.
1948c2ecf20Sopenharmony_ci	 */
1958c2ecf20Sopenharmony_ci	if (gb_bundle_find(intf, bundle_id)) {
1968c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "duplicate bundle id %u\n", bundle_id);
1978c2ecf20Sopenharmony_ci		return NULL;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	bundle = kzalloc(sizeof(*bundle), GFP_KERNEL);
2018c2ecf20Sopenharmony_ci	if (!bundle)
2028c2ecf20Sopenharmony_ci		return NULL;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	bundle->intf = intf;
2058c2ecf20Sopenharmony_ci	bundle->id = bundle_id;
2068c2ecf20Sopenharmony_ci	bundle->class = class;
2078c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&bundle->connections);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	bundle->dev.parent = &intf->dev;
2108c2ecf20Sopenharmony_ci	bundle->dev.bus = &greybus_bus_type;
2118c2ecf20Sopenharmony_ci	bundle->dev.type = &greybus_bundle_type;
2128c2ecf20Sopenharmony_ci	bundle->dev.groups = bundle_groups;
2138c2ecf20Sopenharmony_ci	bundle->dev.dma_mask = intf->dev.dma_mask;
2148c2ecf20Sopenharmony_ci	device_initialize(&bundle->dev);
2158c2ecf20Sopenharmony_ci	dev_set_name(&bundle->dev, "%s.%d", dev_name(&intf->dev), bundle_id);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	list_add(&bundle->links, &intf->bundles);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	trace_gb_bundle_create(bundle);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return bundle;
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ciint gb_bundle_add(struct gb_bundle *bundle)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	int ret;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	ret = device_add(&bundle->dev);
2298c2ecf20Sopenharmony_ci	if (ret) {
2308c2ecf20Sopenharmony_ci		dev_err(&bundle->dev, "failed to register bundle: %d\n", ret);
2318c2ecf20Sopenharmony_ci		return ret;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	trace_gb_bundle_add(bundle);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return 0;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci/*
2408c2ecf20Sopenharmony_ci * Tear down a previously set up bundle.
2418c2ecf20Sopenharmony_ci */
2428c2ecf20Sopenharmony_civoid gb_bundle_destroy(struct gb_bundle *bundle)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	trace_gb_bundle_destroy(bundle);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (device_is_registered(&bundle->dev))
2478c2ecf20Sopenharmony_ci		device_del(&bundle->dev);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	list_del(&bundle->links);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	put_device(&bundle->dev);
2528c2ecf20Sopenharmony_ci}
253