|  | // SPDX-License-Identifier: GPL-2.0-only | 
|  | /* | 
|  | * Copyright (C) 2023, STMicroelectronics - All Rights Reserved | 
|  | */ | 
|  |  | 
|  | #include <linux/bitfield.h> | 
|  | #include <linux/bits.h> | 
|  | #include <linux/bus/stm32_firewall_device.h> | 
|  | #include <linux/device.h> | 
|  | #include <linux/err.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/io.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/of_platform.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/types.h> | 
|  | #include <linux/slab.h> | 
|  |  | 
|  | #include "stm32_firewall.h" | 
|  |  | 
|  | /* Corresponds to STM32_FIREWALL_MAX_EXTRA_ARGS + firewall ID */ | 
|  | #define STM32_FIREWALL_MAX_ARGS		(STM32_FIREWALL_MAX_EXTRA_ARGS + 1) | 
|  |  | 
|  | static LIST_HEAD(firewall_controller_list); | 
|  | static DEFINE_MUTEX(firewall_controller_list_lock); | 
|  |  | 
|  | /* Firewall device API */ | 
|  |  | 
|  | int stm32_firewall_get_firewall(struct device_node *np, struct stm32_firewall *firewall, | 
|  | unsigned int nb_firewall) | 
|  | { | 
|  | struct stm32_firewall_controller *ctrl; | 
|  | struct of_phandle_iterator it; | 
|  | unsigned int i, j = 0; | 
|  | int err; | 
|  |  | 
|  | if (!firewall || !nb_firewall) | 
|  | return -EINVAL; | 
|  |  | 
|  | /* Parse property with phandle parsed out */ | 
|  | of_for_each_phandle(&it, err, np, "access-controllers", "#access-controller-cells", 0) { | 
|  | struct of_phandle_args provider_args; | 
|  | struct device_node *provider = it.node; | 
|  | const char *fw_entry; | 
|  | bool match = false; | 
|  |  | 
|  | if (err) { | 
|  | pr_err("Unable to get access-controllers property for node %s\n, err: %d", | 
|  | np->full_name, err); | 
|  | of_node_put(provider); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | if (j >= nb_firewall) { | 
|  | pr_err("Too many firewall controllers"); | 
|  | of_node_put(provider); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | provider_args.args_count = of_phandle_iterator_args(&it, provider_args.args, | 
|  | STM32_FIREWALL_MAX_ARGS); | 
|  |  | 
|  | /* Check if the parsed phandle corresponds to a registered firewall controller */ | 
|  | mutex_lock(&firewall_controller_list_lock); | 
|  | list_for_each_entry(ctrl, &firewall_controller_list, entry) { | 
|  | if (ctrl->dev->of_node->phandle == it.phandle) { | 
|  | match = true; | 
|  | firewall[j].firewall_ctrl = ctrl; | 
|  | break; | 
|  | } | 
|  | } | 
|  | mutex_unlock(&firewall_controller_list_lock); | 
|  |  | 
|  | if (!match) { | 
|  | firewall[j].firewall_ctrl = NULL; | 
|  | pr_err("No firewall controller registered for %s\n", np->full_name); | 
|  | of_node_put(provider); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | err = of_property_read_string_index(np, "access-controller-names", j, &fw_entry); | 
|  | if (err == 0) | 
|  | firewall[j].entry = fw_entry; | 
|  |  | 
|  | /* Handle the case when there are no arguments given along with the phandle */ | 
|  | if (provider_args.args_count < 0 || | 
|  | provider_args.args_count > STM32_FIREWALL_MAX_ARGS) { | 
|  | of_node_put(provider); | 
|  | return -EINVAL; | 
|  | } else if (provider_args.args_count == 0) { | 
|  | firewall[j].extra_args_size = 0; | 
|  | firewall[j].firewall_id = U32_MAX; | 
|  | j++; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* The firewall ID is always the first argument */ | 
|  | firewall[j].firewall_id = provider_args.args[0]; | 
|  |  | 
|  | /* Extra args start at the second argument */ | 
|  | for (i = 0; i < provider_args.args_count - 1; i++) | 
|  | firewall[j].extra_args[i] = provider_args.args[i + 1]; | 
|  |  | 
|  | /* Remove the firewall ID arg that is not an extra argument */ | 
|  | firewall[j].extra_args_size = provider_args.args_count - 1; | 
|  |  | 
|  | j++; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(stm32_firewall_get_firewall); | 
|  |  | 
|  | int stm32_firewall_grant_access(struct stm32_firewall *firewall) | 
|  | { | 
|  | struct stm32_firewall_controller *firewall_controller; | 
|  |  | 
|  | if (!firewall || firewall->firewall_id == U32_MAX) | 
|  | return -EINVAL; | 
|  |  | 
|  | firewall_controller = firewall->firewall_ctrl; | 
|  |  | 
|  | if (!firewall_controller) | 
|  | return -ENODEV; | 
|  |  | 
|  | return firewall_controller->grant_access(firewall_controller, firewall->firewall_id); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(stm32_firewall_grant_access); | 
|  |  | 
|  | int stm32_firewall_grant_access_by_id(struct stm32_firewall *firewall, u32 subsystem_id) | 
|  | { | 
|  | struct stm32_firewall_controller *firewall_controller; | 
|  |  | 
|  | if (!firewall || subsystem_id == U32_MAX || firewall->firewall_id == U32_MAX) | 
|  | return -EINVAL; | 
|  |  | 
|  | firewall_controller = firewall->firewall_ctrl; | 
|  |  | 
|  | if (!firewall_controller) | 
|  | return -ENODEV; | 
|  |  | 
|  | return firewall_controller->grant_access(firewall_controller, subsystem_id); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(stm32_firewall_grant_access_by_id); | 
|  |  | 
|  | void stm32_firewall_release_access(struct stm32_firewall *firewall) | 
|  | { | 
|  | struct stm32_firewall_controller *firewall_controller; | 
|  |  | 
|  | if (!firewall || firewall->firewall_id == U32_MAX) { | 
|  | pr_debug("Incorrect arguments when releasing a firewall access\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | firewall_controller = firewall->firewall_ctrl; | 
|  |  | 
|  | if (!firewall_controller) { | 
|  | pr_debug("No firewall controller to release\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | firewall_controller->release_access(firewall_controller, firewall->firewall_id); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(stm32_firewall_release_access); | 
|  |  | 
|  | void stm32_firewall_release_access_by_id(struct stm32_firewall *firewall, u32 subsystem_id) | 
|  | { | 
|  | struct stm32_firewall_controller *firewall_controller; | 
|  |  | 
|  | if (!firewall || subsystem_id == U32_MAX || firewall->firewall_id == U32_MAX) { | 
|  | pr_debug("Incorrect arguments when releasing a firewall access"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | firewall_controller = firewall->firewall_ctrl; | 
|  |  | 
|  | if (!firewall_controller) { | 
|  | pr_debug("No firewall controller to release"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | firewall_controller->release_access(firewall_controller, subsystem_id); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(stm32_firewall_release_access_by_id); | 
|  |  | 
|  | /* Firewall controller API */ | 
|  |  | 
|  | int stm32_firewall_controller_register(struct stm32_firewall_controller *firewall_controller) | 
|  | { | 
|  | struct stm32_firewall_controller *ctrl; | 
|  |  | 
|  | if (!firewall_controller) | 
|  | return -ENODEV; | 
|  |  | 
|  | pr_info("Registering %s firewall controller\n", firewall_controller->name); | 
|  |  | 
|  | mutex_lock(&firewall_controller_list_lock); | 
|  | list_for_each_entry(ctrl, &firewall_controller_list, entry) { | 
|  | if (ctrl == firewall_controller) { | 
|  | pr_debug("%s firewall controller already registered\n", | 
|  | firewall_controller->name); | 
|  | mutex_unlock(&firewall_controller_list_lock); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | list_add_tail(&firewall_controller->entry, &firewall_controller_list); | 
|  | mutex_unlock(&firewall_controller_list_lock); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(stm32_firewall_controller_register); | 
|  |  | 
|  | void stm32_firewall_controller_unregister(struct stm32_firewall_controller *firewall_controller) | 
|  | { | 
|  | struct stm32_firewall_controller *ctrl; | 
|  | bool controller_removed = false; | 
|  |  | 
|  | if (!firewall_controller) { | 
|  | pr_debug("Null reference while unregistering firewall controller\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | mutex_lock(&firewall_controller_list_lock); | 
|  | list_for_each_entry(ctrl, &firewall_controller_list, entry) { | 
|  | if (ctrl == firewall_controller) { | 
|  | controller_removed = true; | 
|  | list_del_init(&ctrl->entry); | 
|  | break; | 
|  | } | 
|  | } | 
|  | mutex_unlock(&firewall_controller_list_lock); | 
|  |  | 
|  | if (!controller_removed) | 
|  | pr_debug("There was no firewall controller named %s to unregister\n", | 
|  | firewall_controller->name); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(stm32_firewall_controller_unregister); | 
|  |  | 
|  | int stm32_firewall_populate_bus(struct stm32_firewall_controller *firewall_controller) | 
|  | { | 
|  | struct stm32_firewall *firewalls; | 
|  | struct device_node *child; | 
|  | struct device *parent; | 
|  | unsigned int i; | 
|  | int len; | 
|  | int err; | 
|  |  | 
|  | parent = firewall_controller->dev; | 
|  |  | 
|  | dev_dbg(parent, "Populating %s system bus\n", dev_name(firewall_controller->dev)); | 
|  |  | 
|  | for_each_available_child_of_node(dev_of_node(parent), child) { | 
|  | /* The access-controllers property is mandatory for firewall bus devices */ | 
|  | len = of_count_phandle_with_args(child, "access-controllers", | 
|  | "#access-controller-cells"); | 
|  | if (len <= 0) { | 
|  | of_node_put(child); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | firewalls = kcalloc(len, sizeof(*firewalls), GFP_KERNEL); | 
|  | if (!firewalls) { | 
|  | of_node_put(child); | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | err = stm32_firewall_get_firewall(child, firewalls, (unsigned int)len); | 
|  | if (err) { | 
|  | kfree(firewalls); | 
|  | of_node_put(child); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < len; i++) { | 
|  | if (firewall_controller->grant_access(firewall_controller, | 
|  | firewalls[i].firewall_id)) { | 
|  | /* | 
|  | * Peripheral access not allowed or not defined. | 
|  | * Mark the node as populated so platform bus won't probe it | 
|  | */ | 
|  | of_detach_node(child); | 
|  | dev_err(parent, "%s: Device driver will not be probed\n", | 
|  | child->full_name); | 
|  | } | 
|  | } | 
|  |  | 
|  | kfree(firewalls); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(stm32_firewall_populate_bus); |