/** * struct agent_cb - Registered callback function and private data. * @agent_data: Data passed back to handler function. * @eve_cb: Function pointer to store the callback function. * @list: member to create list.
*/ struct agent_cb { void *agent_data;
event_cb_func_t eve_cb; struct list_head list;
};
/** * struct registered_event_data - Registered Event Data. * @key: key is the combine id(Node-Id | Event-Id) of type u64 * where upper u32 for Node-Id and lower u32 for Event-Id, * And this used as key to index into hashmap. * @cb_type: Type of Api callback, like PM_NOTIFY_CB, etc. * @wake: If this flag set, firmware will wake up processor if is * in sleep or power down state. * @cb_list_head: Head of call back data list which contain the information * about registered handler and private data. * @hentry: hlist_node that hooks this entry into hashtable.
*/ struct registered_event_data {
u64 key; enum pm_api_cb_id cb_type; bool wake; struct list_head cb_list_head; struct hlist_node hentry;
};
/* Add into callback list */
list_add(&cb_data->list, &eve_data->cb_list_head);
/* Add into HASH table */
hash_add(reg_driver_map, &eve_data->hentry, key);
} else { /* Search for callback function and private data in list */
list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) { if (cb_pos->eve_cb == cb_fun &&
cb_pos->agent_data == data) { return 0;
}
}
/* Add multiple handler and private data in list */
cb_data = kmalloc(sizeof(*cb_data), GFP_KERNEL); if (!cb_data) return -ENOMEM;
cb_data->eve_cb = cb_fun;
cb_data->agent_data = data;
/* Check for existing entry in hash table for given cb_type */
hash_for_each_possible(reg_driver_map, eve_data, hentry, PM_INIT_SUSPEND_CB) { if (eve_data->cb_type == PM_INIT_SUSPEND_CB) {
pr_err("Found as already registered\n"); return -EINVAL;
}
}
/* Add new entry if not present */
eve_data = kmalloc(sizeof(*eve_data), GFP_KERNEL); if (!eve_data) return -ENOMEM;
/* Check for existing entry in hash table for given key id */
hash_for_each_possible_safe(reg_driver_map, eve_data, tmp, hentry, key) { if (eve_data->key == key) { /* Delete the list of callback */
list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) { if (cb_pos->eve_cb == cb_fun &&
cb_pos->agent_data == data) {
is_callback_found = true;
list_del_init(&cb_pos->list);
kfree(cb_pos);
}
}
/* Remove HASH table if callback list is empty */ if (list_empty(&eve_data->cb_list_head)) { /* remove an object from a HASH table */
hash_del(&eve_data->hentry);
kfree(eve_data);
is_need_to_unregister = true;
}
}
} if (!is_callback_found) {
pr_warn("Didn't find any registered callback for 0x%x 0x%x\n",
node_id, event); return -EINVAL;
}
return 0;
}
/** * xlnx_register_event() - Register for the event. * @cb_type: Type of callback from pm_api_cb_id, * PM_NOTIFY_CB - for Error Events, * PM_INIT_SUSPEND_CB - for suspend callback. * @node_id: Node-Id related to event. * @event: Event Mask for the Error Event. * @wake: Flag specifying whether the subsystem should be woken upon * event notification. * @cb_fun: Function pointer to store the callback function. * @data: Pointer for the driver instance. * * Return: Returns 0 on successful registration else error code.
*/ int xlnx_register_event(constenum pm_api_cb_id cb_type, const u32 node_id, const u32 event, constbool wake, event_cb_func_t cb_fun, void *data)
{ int ret = 0;
u32 eve; int pos;
if (event_manager_availability) return event_manager_availability;
if (cb_type == PM_INIT_SUSPEND_CB) {
ret = xlnx_add_cb_for_suspend(cb_fun, data);
} else { if (!xlnx_is_error_event(node_id)) { /* Add entry for Node-Id/Event in hash table */
ret = xlnx_add_cb_for_notify_event(node_id, event, wake, cb_fun, data);
} else { /* Add into Hash table */ for (pos = 0; pos < MAX_BITS; pos++) {
eve = event & (1 << pos); if (!eve) continue;
/* Add entry for Node-Id/Eve in hash table */
ret = xlnx_add_cb_for_notify_event(node_id, eve, wake, cb_fun,
data); /* Break the loop if got error */ if (ret) break;
} if (ret) { /* Skip the Event for which got the error */
pos--; /* Remove registered(during this call) event from hash table */ for ( ; pos >= 0; pos--) {
eve = event & (1 << pos); if (!eve) continue;
xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);
}
}
}
if (ret) {
pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__, node_id,
event, ret); return ret;
}
/* Register for Node-Id/Event combination in firmware */
ret = zynqmp_pm_register_notifier(node_id, event, wake, true); if (ret) {
pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__, node_id,
event, ret); /* Remove already registered event from hash table */ if (xlnx_is_error_event(node_id)) { for (pos = 0; pos < MAX_BITS; pos++) {
eve = event & (1 << pos); if (!eve) continue;
xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);
}
} else {
xlnx_remove_cb_for_notify_event(node_id, event, cb_fun, data);
} return ret;
}
}
/** * xlnx_unregister_event() - Unregister for the event. * @cb_type: Type of callback from pm_api_cb_id, * PM_NOTIFY_CB - for Error Events, * PM_INIT_SUSPEND_CB - for suspend callback. * @node_id: Node-Id related to event. * @event: Event Mask for the Error Event. * @cb_fun: Function pointer of callback function. * @data: Pointer of agent's private data. * * Return: Returns 0 on successful unregistration else error code.
*/ int xlnx_unregister_event(constenum pm_api_cb_id cb_type, const u32 node_id, const u32 event,
event_cb_func_t cb_fun, void *data)
{ int ret = 0;
u32 eve, pos;
is_need_to_unregister = false;
if (event_manager_availability) return event_manager_availability;
/* Un-register if list is empty */ if (is_need_to_unregister) { /* Un-register for Node-Id/Event combination */
ret = zynqmp_pm_register_notifier(node_id, event, false, false); if (ret) {
pr_err("%s() failed for 0x%x and 0x%x: %d\n",
__func__, node_id, event, ret); return ret;
}
}
}
/* Get event data */
xlnx_get_event_callback_data(payload);
/* First element is callback type, others are callback arguments */
cb_type = payload[0];
if (cb_type == PM_NOTIFY_CB) {
node_id = payload[1];
event = payload[2]; if (!xlnx_is_error_event(node_id)) {
xlnx_call_notify_cb_handler(payload);
} else { /* * Each call back function expecting payload as an input arguments. * We can get multiple error events as in one call back through error * mask. So payload[2] may can contain multiple error events. * In reg_driver_map database we store data in the combination of single * node_id-error combination. * So coping the payload message into event_data and update the * event_data[2] with Error Mask for single error event and use * event_data as input argument for registered call back function. *
*/
memcpy(event_data, payload, (4 * CB_MAX_PAYLOAD_SIZE)); /* Support Multiple Error Event */ for (pos = 0; pos < MAX_BITS; pos++) { if ((0 == (event & (1 << pos)))) continue;
event_data[2] = (event & (1 << pos));
xlnx_call_notify_cb_handler(event_data);
}
}
} elseif (cb_type == PM_INIT_SUSPEND_CB) {
xlnx_call_suspend_cb_handler(payload);
} else {
pr_err("%s() Unsupported Callback %d\n", __func__, cb_type);
}
staticint xlnx_event_init_sgi(struct platform_device *pdev)
{ int ret = 0; /* * IRQ related structures are used for the following: * for each SGI interrupt ensure its mapped by GIC IRQ domain * and that each corresponding linux IRQ for the HW IRQ has * a handler for when receiving an interrupt from the remote * processor.
*/ struct irq_domain *domain; struct irq_fwspec sgi_fwspec; struct device_node *interrupt_parent = NULL; struct device *parent = pdev->dev.parent;
/* Find GIC controller to map SGIs. */
interrupt_parent = of_irq_find_parent(parent->of_node); if (!interrupt_parent) {
dev_err(&pdev->dev, "Failed to find property for Interrupt parent\n"); return -EINVAL;
}
/* Each SGI needs to be associated with GIC's IRQ domain. */
domain = irq_find_host(interrupt_parent);
of_node_put(interrupt_parent);
/* Each mapping needs GIC domain when finding IRQ mapping. */
sgi_fwspec.fwnode = domain->fwnode;
/* * When irq domain looks at mapping each arg is as follows: * 3 args for: interrupt type (SGI), interrupt # (set later), type
*/
sgi_fwspec.param_count = 1;
staticint xlnx_event_manager_probe(struct platform_device *pdev)
{ int ret;
ret = zynqmp_pm_feature(PM_REGISTER_NOTIFIER); if (ret < 0) {
dev_err(&pdev->dev, "Feature check failed with %d\n", ret); return ret;
}
if ((ret & FIRMWARE_VERSION_MASK) <
REGISTER_NOTIFIER_FIRMWARE_VERSION) {
dev_err(&pdev->dev, "Register notifier version error. Expected Firmware: v%d - Found: v%d\n",
REGISTER_NOTIFIER_FIRMWARE_VERSION,
ret & FIRMWARE_VERSION_MASK); return -EOPNOTSUPP;
}
/* Initialize the SGI */
ret = xlnx_event_init_sgi(pdev); if (ret) {
dev_err(&pdev->dev, "SGI Init has been failed with %d\n", ret); return ret;
}
/* Setup function for the CPU hot-plug cases */
cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/event:starting",
xlnx_event_cpuhp_start, xlnx_event_cpuhp_down);
ret = zynqmp_pm_register_sgi(sgi_num, 0); if (ret) { if (ret == -EOPNOTSUPP)
dev_err(&pdev->dev, "SGI registration not supported by TF-A or Xen\n"); else
dev_err(&pdev->dev, "SGI %d registration failed, err %d\n", sgi_num, ret);
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.