18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Greybus Module code
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2016 Google Inc.
68c2ecf20Sopenharmony_ci * Copyright 2016 Linaro Ltd.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/greybus.h>
108c2ecf20Sopenharmony_ci#include "greybus_trace.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic ssize_t eject_store(struct device *dev,
138c2ecf20Sopenharmony_ci			   struct device_attribute *attr,
148c2ecf20Sopenharmony_ci			   const char *buf, size_t len)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	struct gb_module *module = to_gb_module(dev);
178c2ecf20Sopenharmony_ci	struct gb_interface *intf;
188c2ecf20Sopenharmony_ci	size_t i;
198c2ecf20Sopenharmony_ci	long val;
208c2ecf20Sopenharmony_ci	int ret;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	ret = kstrtol(buf, 0, &val);
238c2ecf20Sopenharmony_ci	if (ret)
248c2ecf20Sopenharmony_ci		return ret;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	if (!val)
278c2ecf20Sopenharmony_ci		return len;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	for (i = 0; i < module->num_interfaces; ++i) {
308c2ecf20Sopenharmony_ci		intf = module->interfaces[i];
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci		mutex_lock(&intf->mutex);
338c2ecf20Sopenharmony_ci		/* Set flag to prevent concurrent activation. */
348c2ecf20Sopenharmony_ci		intf->ejected = true;
358c2ecf20Sopenharmony_ci		gb_interface_disable(intf);
368c2ecf20Sopenharmony_ci		gb_interface_deactivate(intf);
378c2ecf20Sopenharmony_ci		mutex_unlock(&intf->mutex);
388c2ecf20Sopenharmony_ci	}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	/* Tell the SVC to eject the primary interface. */
418c2ecf20Sopenharmony_ci	ret = gb_svc_intf_eject(module->hd->svc, module->module_id);
428c2ecf20Sopenharmony_ci	if (ret)
438c2ecf20Sopenharmony_ci		return ret;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	return len;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(eject);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic ssize_t module_id_show(struct device *dev,
508c2ecf20Sopenharmony_ci			      struct device_attribute *attr, char *buf)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct gb_module *module = to_gb_module(dev);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", module->module_id);
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(module_id);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic ssize_t num_interfaces_show(struct device *dev,
598c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct gb_module *module = to_gb_module(dev);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return sprintf(buf, "%zu\n", module->num_interfaces);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(num_interfaces);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic struct attribute *module_attrs[] = {
688c2ecf20Sopenharmony_ci	&dev_attr_eject.attr,
698c2ecf20Sopenharmony_ci	&dev_attr_module_id.attr,
708c2ecf20Sopenharmony_ci	&dev_attr_num_interfaces.attr,
718c2ecf20Sopenharmony_ci	NULL,
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(module);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic void gb_module_release(struct device *dev)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct gb_module *module = to_gb_module(dev);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	trace_gb_module_release(module);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	kfree(module);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistruct device_type greybus_module_type = {
858c2ecf20Sopenharmony_ci	.name		= "greybus_module",
868c2ecf20Sopenharmony_ci	.release	= gb_module_release,
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistruct gb_module *gb_module_create(struct gb_host_device *hd, u8 module_id,
908c2ecf20Sopenharmony_ci				   size_t num_interfaces)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct gb_interface *intf;
938c2ecf20Sopenharmony_ci	struct gb_module *module;
948c2ecf20Sopenharmony_ci	int i;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	module = kzalloc(struct_size(module, interfaces, num_interfaces),
978c2ecf20Sopenharmony_ci			 GFP_KERNEL);
988c2ecf20Sopenharmony_ci	if (!module)
998c2ecf20Sopenharmony_ci		return NULL;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	module->hd = hd;
1028c2ecf20Sopenharmony_ci	module->module_id = module_id;
1038c2ecf20Sopenharmony_ci	module->num_interfaces = num_interfaces;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	module->dev.parent = &hd->dev;
1068c2ecf20Sopenharmony_ci	module->dev.bus = &greybus_bus_type;
1078c2ecf20Sopenharmony_ci	module->dev.type = &greybus_module_type;
1088c2ecf20Sopenharmony_ci	module->dev.groups = module_groups;
1098c2ecf20Sopenharmony_ci	module->dev.dma_mask = hd->dev.dma_mask;
1108c2ecf20Sopenharmony_ci	device_initialize(&module->dev);
1118c2ecf20Sopenharmony_ci	dev_set_name(&module->dev, "%d-%u", hd->bus_id, module_id);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	trace_gb_module_create(module);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	for (i = 0; i < num_interfaces; ++i) {
1168c2ecf20Sopenharmony_ci		intf = gb_interface_create(module, module_id + i);
1178c2ecf20Sopenharmony_ci		if (!intf) {
1188c2ecf20Sopenharmony_ci			dev_err(&module->dev, "failed to create interface %u\n",
1198c2ecf20Sopenharmony_ci				module_id + i);
1208c2ecf20Sopenharmony_ci			goto err_put_interfaces;
1218c2ecf20Sopenharmony_ci		}
1228c2ecf20Sopenharmony_ci		module->interfaces[i] = intf;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return module;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cierr_put_interfaces:
1288c2ecf20Sopenharmony_ci	for (--i; i >= 0; --i)
1298c2ecf20Sopenharmony_ci		gb_interface_put(module->interfaces[i]);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	put_device(&module->dev);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return NULL;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/*
1378c2ecf20Sopenharmony_ci * Register and enable an interface after first attempting to activate it.
1388c2ecf20Sopenharmony_ci */
1398c2ecf20Sopenharmony_cistatic void gb_module_register_interface(struct gb_interface *intf)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct gb_module *module = intf->module;
1428c2ecf20Sopenharmony_ci	u8 intf_id = intf->interface_id;
1438c2ecf20Sopenharmony_ci	int ret;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	mutex_lock(&intf->mutex);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	ret = gb_interface_activate(intf);
1488c2ecf20Sopenharmony_ci	if (ret) {
1498c2ecf20Sopenharmony_ci		if (intf->type != GB_INTERFACE_TYPE_DUMMY) {
1508c2ecf20Sopenharmony_ci			dev_err(&module->dev,
1518c2ecf20Sopenharmony_ci				"failed to activate interface %u: %d\n",
1528c2ecf20Sopenharmony_ci				intf_id, ret);
1538c2ecf20Sopenharmony_ci		}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci		gb_interface_add(intf);
1568c2ecf20Sopenharmony_ci		goto err_unlock;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	ret = gb_interface_add(intf);
1608c2ecf20Sopenharmony_ci	if (ret)
1618c2ecf20Sopenharmony_ci		goto err_interface_deactivate;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	ret = gb_interface_enable(intf);
1648c2ecf20Sopenharmony_ci	if (ret) {
1658c2ecf20Sopenharmony_ci		dev_err(&module->dev, "failed to enable interface %u: %d\n",
1668c2ecf20Sopenharmony_ci			intf_id, ret);
1678c2ecf20Sopenharmony_ci		goto err_interface_deactivate;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	mutex_unlock(&intf->mutex);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cierr_interface_deactivate:
1758c2ecf20Sopenharmony_ci	gb_interface_deactivate(intf);
1768c2ecf20Sopenharmony_cierr_unlock:
1778c2ecf20Sopenharmony_ci	mutex_unlock(&intf->mutex);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic void gb_module_deregister_interface(struct gb_interface *intf)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	/* Mark as disconnected to prevent I/O during disable. */
1838c2ecf20Sopenharmony_ci	if (intf->module->disconnected)
1848c2ecf20Sopenharmony_ci		intf->disconnected = true;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	mutex_lock(&intf->mutex);
1878c2ecf20Sopenharmony_ci	intf->removed = true;
1888c2ecf20Sopenharmony_ci	gb_interface_disable(intf);
1898c2ecf20Sopenharmony_ci	gb_interface_deactivate(intf);
1908c2ecf20Sopenharmony_ci	mutex_unlock(&intf->mutex);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	gb_interface_del(intf);
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/* Register a module and its interfaces. */
1968c2ecf20Sopenharmony_ciint gb_module_add(struct gb_module *module)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	size_t i;
1998c2ecf20Sopenharmony_ci	int ret;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	ret = device_add(&module->dev);
2028c2ecf20Sopenharmony_ci	if (ret) {
2038c2ecf20Sopenharmony_ci		dev_err(&module->dev, "failed to register module: %d\n", ret);
2048c2ecf20Sopenharmony_ci		return ret;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	trace_gb_module_add(module);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	for (i = 0; i < module->num_interfaces; ++i)
2108c2ecf20Sopenharmony_ci		gb_module_register_interface(module->interfaces[i]);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	return 0;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci/* Deregister a module and its interfaces. */
2168c2ecf20Sopenharmony_civoid gb_module_del(struct gb_module *module)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	size_t i;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	for (i = 0; i < module->num_interfaces; ++i)
2218c2ecf20Sopenharmony_ci		gb_module_deregister_interface(module->interfaces[i]);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	trace_gb_module_del(module);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	device_del(&module->dev);
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_civoid gb_module_put(struct gb_module *module)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	size_t i;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	for (i = 0; i < module->num_interfaces; ++i)
2338c2ecf20Sopenharmony_ci		gb_interface_put(module->interfaces[i]);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	put_device(&module->dev);
2368c2ecf20Sopenharmony_ci}
237