18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Greybus Host Device
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2014-2015 Google Inc.
68c2ecf20Sopenharmony_ci * Copyright 2014-2015 Linaro Ltd.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/greybus.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "greybus_trace.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_create);
168c2ecf20Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_release);
178c2ecf20Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_add);
188c2ecf20Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_del);
198c2ecf20Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_in);
208c2ecf20Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(gb_message_submit);
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic struct ida gb_hd_bus_id_map;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ciint gb_hd_output(struct gb_host_device *hd, void *req, u16 size, u8 cmd,
258c2ecf20Sopenharmony_ci		 bool async)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	if (!hd || !hd->driver || !hd->driver->output)
288c2ecf20Sopenharmony_ci		return -EINVAL;
298c2ecf20Sopenharmony_ci	return hd->driver->output(hd, req, size, cmd, async);
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_output);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic ssize_t bus_id_show(struct device *dev,
348c2ecf20Sopenharmony_ci			   struct device_attribute *attr, char *buf)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	struct gb_host_device *hd = to_gb_host_device(dev);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", hd->bus_id);
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(bus_id);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic struct attribute *bus_attrs[] = {
438c2ecf20Sopenharmony_ci	&dev_attr_bus_id.attr,
448c2ecf20Sopenharmony_ci	NULL
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(bus);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ciint gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct ida *id_map = &hd->cport_id_map;
518c2ecf20Sopenharmony_ci	int ret;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	ret = ida_simple_get(id_map, cport_id, cport_id + 1, GFP_KERNEL);
548c2ecf20Sopenharmony_ci	if (ret < 0) {
558c2ecf20Sopenharmony_ci		dev_err(&hd->dev, "failed to reserve cport %u\n", cport_id);
568c2ecf20Sopenharmony_ci		return ret;
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return 0;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_cport_reserve);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_civoid gb_hd_cport_release_reserved(struct gb_host_device *hd, u16 cport_id)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct ida *id_map = &hd->cport_id_map;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	ida_simple_remove(id_map, cport_id);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_cport_release_reserved);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/* Locking: Caller guarantees serialisation */
728c2ecf20Sopenharmony_ciint gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id,
738c2ecf20Sopenharmony_ci			 unsigned long flags)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct ida *id_map = &hd->cport_id_map;
768c2ecf20Sopenharmony_ci	int ida_start, ida_end;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (hd->driver->cport_allocate)
798c2ecf20Sopenharmony_ci		return hd->driver->cport_allocate(hd, cport_id, flags);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (cport_id < 0) {
828c2ecf20Sopenharmony_ci		ida_start = 0;
838c2ecf20Sopenharmony_ci		ida_end = hd->num_cports;
848c2ecf20Sopenharmony_ci	} else if (cport_id < hd->num_cports) {
858c2ecf20Sopenharmony_ci		ida_start = cport_id;
868c2ecf20Sopenharmony_ci		ida_end = cport_id + 1;
878c2ecf20Sopenharmony_ci	} else {
888c2ecf20Sopenharmony_ci		dev_err(&hd->dev, "cport %d not available\n", cport_id);
898c2ecf20Sopenharmony_ci		return -EINVAL;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/* Locking: Caller guarantees serialisation */
968c2ecf20Sopenharmony_civoid gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	if (hd->driver->cport_release) {
998c2ecf20Sopenharmony_ci		hd->driver->cport_release(hd, cport_id);
1008c2ecf20Sopenharmony_ci		return;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	ida_simple_remove(&hd->cport_id_map, cport_id);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic void gb_hd_release(struct device *dev)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct gb_host_device *hd = to_gb_host_device(dev);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	trace_gb_hd_release(hd);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (hd->svc)
1138c2ecf20Sopenharmony_ci		gb_svc_put(hd->svc);
1148c2ecf20Sopenharmony_ci	ida_simple_remove(&gb_hd_bus_id_map, hd->bus_id);
1158c2ecf20Sopenharmony_ci	ida_destroy(&hd->cport_id_map);
1168c2ecf20Sopenharmony_ci	kfree(hd);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistruct device_type greybus_hd_type = {
1208c2ecf20Sopenharmony_ci	.name		= "greybus_host_device",
1218c2ecf20Sopenharmony_ci	.release	= gb_hd_release,
1228c2ecf20Sopenharmony_ci};
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistruct gb_host_device *gb_hd_create(struct gb_hd_driver *driver,
1258c2ecf20Sopenharmony_ci				    struct device *parent,
1268c2ecf20Sopenharmony_ci				    size_t buffer_size_max,
1278c2ecf20Sopenharmony_ci				    size_t num_cports)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	struct gb_host_device *hd;
1308c2ecf20Sopenharmony_ci	int ret;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/*
1338c2ecf20Sopenharmony_ci	 * Validate that the driver implements all of the callbacks
1348c2ecf20Sopenharmony_ci	 * so that we don't have to every time we make them.
1358c2ecf20Sopenharmony_ci	 */
1368c2ecf20Sopenharmony_ci	if ((!driver->message_send) || (!driver->message_cancel)) {
1378c2ecf20Sopenharmony_ci		dev_err(parent, "mandatory hd-callbacks missing\n");
1388c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (buffer_size_max < GB_OPERATION_MESSAGE_SIZE_MIN) {
1428c2ecf20Sopenharmony_ci		dev_err(parent, "greybus host-device buffers too small\n");
1438c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (num_cports == 0 || num_cports > CPORT_ID_MAX + 1) {
1478c2ecf20Sopenharmony_ci		dev_err(parent, "Invalid number of CPorts: %zu\n", num_cports);
1488c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/*
1528c2ecf20Sopenharmony_ci	 * Make sure to never allocate messages larger than what the Greybus
1538c2ecf20Sopenharmony_ci	 * protocol supports.
1548c2ecf20Sopenharmony_ci	 */
1558c2ecf20Sopenharmony_ci	if (buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) {
1568c2ecf20Sopenharmony_ci		dev_warn(parent, "limiting buffer size to %u\n",
1578c2ecf20Sopenharmony_ci			 GB_OPERATION_MESSAGE_SIZE_MAX);
1588c2ecf20Sopenharmony_ci		buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
1628c2ecf20Sopenharmony_ci	if (!hd)
1638c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	ret = ida_simple_get(&gb_hd_bus_id_map, 1, 0, GFP_KERNEL);
1668c2ecf20Sopenharmony_ci	if (ret < 0) {
1678c2ecf20Sopenharmony_ci		kfree(hd);
1688c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci	hd->bus_id = ret;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	hd->driver = driver;
1738c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&hd->modules);
1748c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&hd->connections);
1758c2ecf20Sopenharmony_ci	ida_init(&hd->cport_id_map);
1768c2ecf20Sopenharmony_ci	hd->buffer_size_max = buffer_size_max;
1778c2ecf20Sopenharmony_ci	hd->num_cports = num_cports;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	hd->dev.parent = parent;
1808c2ecf20Sopenharmony_ci	hd->dev.bus = &greybus_bus_type;
1818c2ecf20Sopenharmony_ci	hd->dev.type = &greybus_hd_type;
1828c2ecf20Sopenharmony_ci	hd->dev.groups = bus_groups;
1838c2ecf20Sopenharmony_ci	hd->dev.dma_mask = hd->dev.parent->dma_mask;
1848c2ecf20Sopenharmony_ci	device_initialize(&hd->dev);
1858c2ecf20Sopenharmony_ci	dev_set_name(&hd->dev, "greybus%d", hd->bus_id);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	trace_gb_hd_create(hd);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	hd->svc = gb_svc_create(hd);
1908c2ecf20Sopenharmony_ci	if (!hd->svc) {
1918c2ecf20Sopenharmony_ci		dev_err(&hd->dev, "failed to create svc\n");
1928c2ecf20Sopenharmony_ci		put_device(&hd->dev);
1938c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	return hd;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_create);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ciint gb_hd_add(struct gb_host_device *hd)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	int ret;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	ret = device_add(&hd->dev);
2058c2ecf20Sopenharmony_ci	if (ret)
2068c2ecf20Sopenharmony_ci		return ret;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	ret = gb_svc_add(hd->svc);
2098c2ecf20Sopenharmony_ci	if (ret) {
2108c2ecf20Sopenharmony_ci		device_del(&hd->dev);
2118c2ecf20Sopenharmony_ci		return ret;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	trace_gb_hd_add(hd);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return 0;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_add);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_civoid gb_hd_del(struct gb_host_device *hd)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	trace_gb_hd_del(hd);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/*
2258c2ecf20Sopenharmony_ci	 * Tear down the svc and flush any on-going hotplug processing before
2268c2ecf20Sopenharmony_ci	 * removing the remaining interfaces.
2278c2ecf20Sopenharmony_ci	 */
2288c2ecf20Sopenharmony_ci	gb_svc_del(hd->svc);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	device_del(&hd->dev);
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_del);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_civoid gb_hd_shutdown(struct gb_host_device *hd)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	gb_svc_del(hd->svc);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_shutdown);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_civoid gb_hd_put(struct gb_host_device *hd)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	put_device(&hd->dev);
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_put);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ciint __init gb_hd_init(void)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	ida_init(&gb_hd_bus_id_map);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return 0;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_civoid gb_hd_exit(void)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	ida_destroy(&gb_hd_bus_id_map);
2568c2ecf20Sopenharmony_ci}
257