root/drivers/bus/stm32_firewall.c
// 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 = kzalloc_objs(*firewalls, len);
                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);