// SPDX-License-Identifier: GPL-2.0 /* * System Control and Management Interface (SCMI) Message Protocol bus layer * * Copyright (C) 2018-2021 ARM Ltd.
*/
/* Track globally the creation of SCMI SystemPower related devices */ static atomic_t scmi_syspower_registered = ATOMIC_INIT(0);
/** * scmi_protocol_device_request - Helper to request a device * * @id_table: A protocol/name pair descriptor for the device to be created. * * This helper let an SCMI driver request specific devices identified by the * @id_table to be created for each active SCMI instance. * * The requested device name MUST NOT be already existent for this protocol; * at first the freshly requested @id_table is annotated in the IDR table * @scmi_requested_devices and then the requested device is advertised to any * registered party via the @scmi_requested_devices_nh notification chain. * * Return: 0 on Success
*/ staticint scmi_protocol_device_request(conststruct scmi_device_id *id_table)
{ int ret = 0; struct list_head *head, *phead = NULL; struct scmi_requested_dev *rdev;
pr_debug("Requesting SCMI device (%s) for protocol %x\n",
id_table->name, id_table->protocol_id);
if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT) &&
!IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT_COEX)) {
pr_warn("SCMI Raw mode active. Rejecting '%s'/0x%02X\n",
id_table->name, id_table->protocol_id); return -EINVAL;
}
/* * Find the matching protocol rdev list and then search of any * existent equally named device...fails if any duplicate found.
*/
mutex_lock(&scmi_requested_devices_mtx);
phead = idr_find(&scmi_requested_devices, id_table->protocol_id); if (phead) {
head = phead;
list_for_each_entry(rdev, head, node) { if (!strcmp(rdev->id_table->name, id_table->name)) {
pr_err("Ignoring duplicate request [%d] %s\n",
rdev->id_table->protocol_id,
rdev->id_table->name);
ret = -EINVAL; goto out;
}
}
}
/* * No duplicate found for requested id_table, so let's create a new * requested device entry for this new valid request.
*/
rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); if (!rdev) {
ret = -ENOMEM; goto out;
}
rdev->id_table = id_table;
/* * Append the new requested device table descriptor to the head of the * related protocol list, eventually creating such head if not already * there.
*/ if (!phead) {
phead = kzalloc(sizeof(*phead), GFP_KERNEL); if (!phead) {
kfree(rdev);
ret = -ENOMEM; goto out;
}
INIT_LIST_HEAD(phead);
ret = idr_alloc(&scmi_requested_devices, (void *)phead,
id_table->protocol_id,
id_table->protocol_id + 1, GFP_KERNEL); if (ret != id_table->protocol_id) {
pr_err("Failed to save SCMI device - ret:%d\n", ret);
kfree(rdev);
kfree(phead);
ret = -EINVAL; goto out;
}
ret = 0;
}
list_add(&rdev->node, phead);
out:
mutex_unlock(&scmi_requested_devices_mtx);
if (!ret)
blocking_notifier_call_chain(&scmi_requested_devices_nh,
SCMI_BUS_NOTIFY_DEVICE_REQUEST,
(void *)rdev->id_table);
return ret;
}
staticint scmi_protocol_table_register(conststruct scmi_device_id *id_table)
{ int ret = 0; conststruct scmi_device_id *entry;
for (entry = id_table; entry->name && ret == 0; entry++)
ret = scmi_protocol_device_request(entry);
return ret;
}
/** * scmi_protocol_device_unrequest - Helper to unrequest a device * * @id_table: A protocol/name pair descriptor for the device to be unrequested. * * The unrequested device, described by the provided id_table, is at first * removed from the IDR @scmi_requested_devices and then the removal is * advertised to any registered party via the @scmi_requested_devices_nh * notification chain.
*/ staticvoid scmi_protocol_device_unrequest(conststruct scmi_device_id *id_table)
{ struct list_head *phead;
pr_debug("Unrequesting SCMI device (%s) for protocol %x\n",
id_table->name, id_table->protocol_id);
staticstruct scmi_device *
__scmi_device_create(struct device_node *np, struct device *parent, int protocol, constchar *name)
{ int id, retval; struct scmi_device *scmi_dev;
/* * If the same protocol/name device already exist under the same parent * (i.e. SCMI instance) just return the existent device. * This avoids any race between the SCMI driver, creating devices for * each DT defined protocol at probe time, and the concurrent * registration of SCMI drivers.
*/
scmi_dev = scmi_child_dev_find(parent, protocol, name); if (scmi_dev) return scmi_dev;
/* * Ignore any possible subsequent failures while creating the device * since we are doomed anyway at that point; not using a mutex which * spans across this whole function to keep things simple and to avoid * to serialize all the __scmi_device_create calls across possibly * different SCMI server instances (parent)
*/ if (protocol == SCMI_PROTOCOL_SYSTEM &&
atomic_cmpxchg(&scmi_syspower_registered, 0, 1)) {
dev_warn(parent, "SCMI SystemPower protocol device must be unique !\n"); return NULL;
}
scmi_dev = kzalloc(sizeof(*scmi_dev), GFP_KERNEL); if (!scmi_dev) return NULL;
sdev = __scmi_device_create(np, parent, protocol, name); if (!sdev)
pr_err("(%s) Failed to create device for protocol 0x%x (%s)\n",
of_node_full_name(parent->of_node), protocol, name);
return sdev;
}
/** * scmi_device_create - A method to create one or more SCMI devices * * @np: A reference to the device node to use for the new device(s) * @parent: The parent device to use identifying a specific SCMI instance * @protocol: The SCMI protocol to be associated with this device * @name: The requested-name of the device to be created; this is optional * and if no @name is provided, all the devices currently known to * be requested on the SCMI bus for @protocol will be created. * * This method can be invoked to create a single well-defined device (like * a transport device or a device requested by an SCMI driver loaded after * the core SCMI stack has been probed), or to create all the devices currently * known to have been requested by the loaded SCMI drivers for a specific * protocol (typically during SCMI core protocol enumeration at probe time). * * Return: The created device (or one of them if @name was NOT provided and * multiple devices were created) or NULL if no device was created; * note that NULL indicates an error ONLY in case a specific @name * was provided: when @name param was not provided, a number of devices * could have been potentially created for a whole protocol, unless no * device was found to have been requested for that specific protocol.
*/ struct scmi_device *scmi_device_create(struct device_node *np, struct device *parent, int protocol, constchar *name)
{ struct list_head *phead; struct scmi_requested_dev *rdev; struct scmi_device *scmi_dev = NULL;
if (name) return _scmi_device_create(np, parent, protocol, name);
mutex_lock(&scmi_requested_devices_mtx);
phead = idr_find(&scmi_requested_devices, protocol); /* Nothing to do. */ if (!phead) {
mutex_unlock(&scmi_requested_devices_mtx); return NULL;
}
/* Walk the list of requested devices for protocol and create them */
list_for_each_entry(rdev, phead, node) { struct scmi_device *sdev;
staticint __init scmi_bus_init(void)
{ int retval;
retval = bus_register(&scmi_bus_type); if (retval)
pr_err("SCMI protocol bus register failed (%d)\n", retval);
pr_info("SCMI protocol bus registered\n");
return retval;
}
subsys_initcall(scmi_bus_init);
staticvoid __exit scmi_bus_exit(void)
{ /* * Destroy all remaining devices: just in case the drivers were * manually unbound and at first and then the modules unloaded.
*/
scmi_devices_unregister();
bus_unregister(&scmi_bus_type);
ida_destroy(&scmi_bus_id);
}
module_exit(scmi_bus_exit);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.