162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Greybus Host Device 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2014-2015 Google Inc. 662306a36Sopenharmony_ci * Copyright 2014-2015 Linaro Ltd. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/greybus.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "greybus_trace.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_create); 1662306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_release); 1762306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_add); 1862306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_del); 1962306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_in); 2062306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(gb_message_submit); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic struct ida gb_hd_bus_id_map; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ciint gb_hd_output(struct gb_host_device *hd, void *req, u16 size, u8 cmd, 2562306a36Sopenharmony_ci bool async) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci if (!hd || !hd->driver || !hd->driver->output) 2862306a36Sopenharmony_ci return -EINVAL; 2962306a36Sopenharmony_ci return hd->driver->output(hd, req, size, cmd, async); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_output); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic ssize_t bus_id_show(struct device *dev, 3462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct gb_host_device *hd = to_gb_host_device(dev); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci return sprintf(buf, "%d\n", hd->bus_id); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(bus_id); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic struct attribute *bus_attrs[] = { 4362306a36Sopenharmony_ci &dev_attr_bus_id.attr, 4462306a36Sopenharmony_ci NULL 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ciATTRIBUTE_GROUPS(bus); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ciint gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct ida *id_map = &hd->cport_id_map; 5162306a36Sopenharmony_ci int ret; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci ret = ida_simple_get(id_map, cport_id, cport_id + 1, GFP_KERNEL); 5462306a36Sopenharmony_ci if (ret < 0) { 5562306a36Sopenharmony_ci dev_err(&hd->dev, "failed to reserve cport %u\n", cport_id); 5662306a36Sopenharmony_ci return ret; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci return 0; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_cport_reserve); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_civoid gb_hd_cport_release_reserved(struct gb_host_device *hd, u16 cport_id) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct ida *id_map = &hd->cport_id_map; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci ida_simple_remove(id_map, cport_id); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_cport_release_reserved); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* Locking: Caller guarantees serialisation */ 7262306a36Sopenharmony_ciint gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id, 7362306a36Sopenharmony_ci unsigned long flags) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct ida *id_map = &hd->cport_id_map; 7662306a36Sopenharmony_ci int ida_start, ida_end; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (hd->driver->cport_allocate) 7962306a36Sopenharmony_ci return hd->driver->cport_allocate(hd, cport_id, flags); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (cport_id < 0) { 8262306a36Sopenharmony_ci ida_start = 0; 8362306a36Sopenharmony_ci ida_end = hd->num_cports; 8462306a36Sopenharmony_ci } else if (cport_id < hd->num_cports) { 8562306a36Sopenharmony_ci ida_start = cport_id; 8662306a36Sopenharmony_ci ida_end = cport_id + 1; 8762306a36Sopenharmony_ci } else { 8862306a36Sopenharmony_ci dev_err(&hd->dev, "cport %d not available\n", cport_id); 8962306a36Sopenharmony_ci return -EINVAL; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* Locking: Caller guarantees serialisation */ 9662306a36Sopenharmony_civoid gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci if (hd->driver->cport_release) { 9962306a36Sopenharmony_ci hd->driver->cport_release(hd, cport_id); 10062306a36Sopenharmony_ci return; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci ida_simple_remove(&hd->cport_id_map, cport_id); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic void gb_hd_release(struct device *dev) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct gb_host_device *hd = to_gb_host_device(dev); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci trace_gb_hd_release(hd); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (hd->svc) 11362306a36Sopenharmony_ci gb_svc_put(hd->svc); 11462306a36Sopenharmony_ci ida_simple_remove(&gb_hd_bus_id_map, hd->bus_id); 11562306a36Sopenharmony_ci ida_destroy(&hd->cport_id_map); 11662306a36Sopenharmony_ci kfree(hd); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistruct device_type greybus_hd_type = { 12062306a36Sopenharmony_ci .name = "greybus_host_device", 12162306a36Sopenharmony_ci .release = gb_hd_release, 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistruct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, 12562306a36Sopenharmony_ci struct device *parent, 12662306a36Sopenharmony_ci size_t buffer_size_max, 12762306a36Sopenharmony_ci size_t num_cports) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct gb_host_device *hd; 13062306a36Sopenharmony_ci int ret; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* 13362306a36Sopenharmony_ci * Validate that the driver implements all of the callbacks 13462306a36Sopenharmony_ci * so that we don't have to every time we make them. 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci if ((!driver->message_send) || (!driver->message_cancel)) { 13762306a36Sopenharmony_ci dev_err(parent, "mandatory hd-callbacks missing\n"); 13862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (buffer_size_max < GB_OPERATION_MESSAGE_SIZE_MIN) { 14262306a36Sopenharmony_ci dev_err(parent, "greybus host-device buffers too small\n"); 14362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (num_cports == 0 || num_cports > CPORT_ID_MAX + 1) { 14762306a36Sopenharmony_ci dev_err(parent, "Invalid number of CPorts: %zu\n", num_cports); 14862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* 15262306a36Sopenharmony_ci * Make sure to never allocate messages larger than what the Greybus 15362306a36Sopenharmony_ci * protocol supports. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci if (buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) { 15662306a36Sopenharmony_ci dev_warn(parent, "limiting buffer size to %u\n", 15762306a36Sopenharmony_ci GB_OPERATION_MESSAGE_SIZE_MAX); 15862306a36Sopenharmony_ci buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL); 16262306a36Sopenharmony_ci if (!hd) 16362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci ret = ida_simple_get(&gb_hd_bus_id_map, 1, 0, GFP_KERNEL); 16662306a36Sopenharmony_ci if (ret < 0) { 16762306a36Sopenharmony_ci kfree(hd); 16862306a36Sopenharmony_ci return ERR_PTR(ret); 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci hd->bus_id = ret; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci hd->driver = driver; 17362306a36Sopenharmony_ci INIT_LIST_HEAD(&hd->modules); 17462306a36Sopenharmony_ci INIT_LIST_HEAD(&hd->connections); 17562306a36Sopenharmony_ci ida_init(&hd->cport_id_map); 17662306a36Sopenharmony_ci hd->buffer_size_max = buffer_size_max; 17762306a36Sopenharmony_ci hd->num_cports = num_cports; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci hd->dev.parent = parent; 18062306a36Sopenharmony_ci hd->dev.bus = &greybus_bus_type; 18162306a36Sopenharmony_ci hd->dev.type = &greybus_hd_type; 18262306a36Sopenharmony_ci hd->dev.groups = bus_groups; 18362306a36Sopenharmony_ci hd->dev.dma_mask = hd->dev.parent->dma_mask; 18462306a36Sopenharmony_ci device_initialize(&hd->dev); 18562306a36Sopenharmony_ci dev_set_name(&hd->dev, "greybus%d", hd->bus_id); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci trace_gb_hd_create(hd); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci hd->svc = gb_svc_create(hd); 19062306a36Sopenharmony_ci if (!hd->svc) { 19162306a36Sopenharmony_ci dev_err(&hd->dev, "failed to create svc\n"); 19262306a36Sopenharmony_ci put_device(&hd->dev); 19362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return hd; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_create); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ciint gb_hd_add(struct gb_host_device *hd) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci int ret; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci ret = device_add(&hd->dev); 20562306a36Sopenharmony_ci if (ret) 20662306a36Sopenharmony_ci return ret; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci ret = gb_svc_add(hd->svc); 20962306a36Sopenharmony_ci if (ret) { 21062306a36Sopenharmony_ci device_del(&hd->dev); 21162306a36Sopenharmony_ci return ret; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci trace_gb_hd_add(hd); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_add); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_civoid gb_hd_del(struct gb_host_device *hd) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci trace_gb_hd_del(hd); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * Tear down the svc and flush any on-going hotplug processing before 22662306a36Sopenharmony_ci * removing the remaining interfaces. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci gb_svc_del(hd->svc); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci device_del(&hd->dev); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_del); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_civoid gb_hd_shutdown(struct gb_host_device *hd) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci gb_svc_del(hd->svc); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_shutdown); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_civoid gb_hd_put(struct gb_host_device *hd) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci put_device(&hd->dev); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_hd_put); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciint __init gb_hd_init(void) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci ida_init(&gb_hd_bus_id_map); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_civoid gb_hd_exit(void) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci ida_destroy(&gb_hd_bus_id_map); 25662306a36Sopenharmony_ci} 257