// 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);