2024-01-05 16:03:56 +03:00
// 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 ;
}
2024-04-12 11:25:37 +03:00
if ( j > = nb_firewall ) {
2024-01-05 16:03:56 +03:00
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 ) ;