2018-01-17 19:36:44 +03:00
// SPDX-License-Identifier: GPL-2.0
2015-03-06 04:29:11 +03:00
/*
* Freescale data path resource container ( DPRC ) driver
*
2016-10-26 19:20:33 +03:00
* Copyright ( C ) 2014 - 2016 Freescale Semiconductor , Inc .
2020-09-29 11:54:29 +03:00
* Copyright 2019 - 2020 NXP
2015-03-06 04:29:11 +03:00
* Author : German Rivera < German . Rivera @ freescale . com >
*
*/
# include <linux/module.h>
# include <linux/slab.h>
2016-01-07 01:03:26 +03:00
# include <linux/interrupt.h>
2018-02-05 17:07:42 +03:00
# include <linux/fsl/mc.h>
2016-08-24 01:14:23 +03:00
2016-08-24 01:13:30 +03:00
# include "fsl-mc-private.h"
2015-03-06 04:29:11 +03:00
2016-08-24 01:13:35 +03:00
# define FSL_MC_DPRC_DRIVER_NAME "fsl_mc_dprc"
2017-06-27 17:41:24 +03:00
struct fsl_mc_child_objs {
2015-03-06 04:29:11 +03:00
int child_count ;
2017-06-27 17:41:24 +03:00
struct fsl_mc_obj_desc * child_array ;
2015-03-06 04:29:11 +03:00
} ;
2017-06-22 16:35:47 +03:00
static bool fsl_mc_device_match ( struct fsl_mc_device * mc_dev ,
2017-06-27 17:41:24 +03:00
struct fsl_mc_obj_desc * obj_desc )
2017-06-22 16:35:47 +03:00
{
2017-06-27 17:41:21 +03:00
return mc_dev - > obj_desc . id = = obj_desc - > id & &
2017-08-23 13:36:16 +03:00
strcmp ( mc_dev - > obj_desc . type , obj_desc - > type ) = = 0 ;
2020-07-17 18:48:00 +03:00
}
2017-06-27 17:41:21 +03:00
2020-07-17 18:48:00 +03:00
static bool fsl_mc_obj_desc_is_allocatable ( struct fsl_mc_obj_desc * obj )
{
if ( strcmp ( obj - > type , " dpmcp " ) = = 0 | |
strcmp ( obj - > type , " dpcon " ) = = 0 | |
strcmp ( obj - > type , " dpbp " ) = = 0 )
return true ;
else
return false ;
2017-06-22 16:35:47 +03:00
}
2015-03-06 04:29:11 +03:00
static int __fsl_mc_device_remove_if_not_in_mc ( struct device * dev , void * data )
{
int i ;
2017-06-27 17:41:24 +03:00
struct fsl_mc_child_objs * objs ;
2015-03-06 04:29:11 +03:00
struct fsl_mc_device * mc_dev ;
2023-06-13 19:07:18 +03:00
if ( ! dev_is_fsl_mc ( dev ) )
return 0 ;
2015-03-06 04:29:11 +03:00
mc_dev = to_fsl_mc_device ( dev ) ;
objs = data ;
for ( i = 0 ; i < objs - > child_count ; i + + ) {
2017-06-27 17:41:24 +03:00
struct fsl_mc_obj_desc * obj_desc = & objs - > child_array [ i ] ;
2015-03-06 04:29:11 +03:00
if ( strlen ( obj_desc - > type ) ! = 0 & &
2017-06-22 16:35:47 +03:00
fsl_mc_device_match ( mc_dev , obj_desc ) )
2015-03-06 04:29:11 +03:00
break ;
}
if ( i = = objs - > child_count )
fsl_mc_device_remove ( mc_dev ) ;
return 0 ;
}
static int __fsl_mc_device_remove ( struct device * dev , void * data )
{
2023-06-13 19:07:18 +03:00
if ( ! dev_is_fsl_mc ( dev ) )
return 0 ;
2015-03-06 04:29:11 +03:00
fsl_mc_device_remove ( to_fsl_mc_device ( dev ) ) ;
return 0 ;
}
/**
* dprc_remove_devices - Removes devices for objects removed from a DPRC
*
* @ mc_bus_dev : pointer to the fsl - mc device that represents a DPRC object
* @ obj_desc_array : array of object descriptors for child objects currently
* present in the DPRC in the MC .
* @ num_child_objects_in_mc : number of entries in obj_desc_array
*
* Synchronizes the state of the Linux bus driver with the actual state of
* the MC by removing devices that represent MC objects that have
* been dynamically removed in the physical DPRC .
*/
2020-09-29 11:54:35 +03:00
void dprc_remove_devices ( struct fsl_mc_device * mc_bus_dev ,
struct fsl_mc_obj_desc * obj_desc_array ,
int num_child_objects_in_mc )
2015-03-06 04:29:11 +03:00
{
if ( num_child_objects_in_mc ! = 0 ) {
/*
* Remove child objects that are in the DPRC in Linux ,
* but not in the MC :
*/
2017-06-27 17:41:24 +03:00
struct fsl_mc_child_objs objs ;
2015-03-06 04:29:11 +03:00
objs . child_count = num_child_objects_in_mc ;
objs . child_array = obj_desc_array ;
device_for_each_child ( & mc_bus_dev - > dev , & objs ,
__fsl_mc_device_remove_if_not_in_mc ) ;
} else {
/*
* There are no child objects for this DPRC in the MC .
* So , remove all the child devices from Linux :
*/
device_for_each_child ( & mc_bus_dev - > dev , NULL ,
__fsl_mc_device_remove ) ;
}
}
2020-09-29 11:54:35 +03:00
EXPORT_SYMBOL_GPL ( dprc_remove_devices ) ;
2015-03-06 04:29:11 +03:00
static int __fsl_mc_device_match ( struct device * dev , void * data )
{
2017-06-27 17:41:24 +03:00
struct fsl_mc_obj_desc * obj_desc = data ;
2015-03-06 04:29:11 +03:00
struct fsl_mc_device * mc_dev = to_fsl_mc_device ( dev ) ;
2017-06-22 16:35:47 +03:00
return fsl_mc_device_match ( mc_dev , obj_desc ) ;
2015-03-06 04:29:11 +03:00
}
2019-10-31 02:18:29 +03:00
struct fsl_mc_device * fsl_mc_device_lookup ( struct fsl_mc_obj_desc * obj_desc ,
struct fsl_mc_device * mc_bus_dev )
2015-03-06 04:29:11 +03:00
{
struct device * dev ;
dev = device_find_child ( & mc_bus_dev - > dev , obj_desc ,
__fsl_mc_device_match ) ;
return dev ? to_fsl_mc_device ( dev ) : NULL ;
}
2015-03-28 00:01:06 +03:00
/**
* check_plugged_state_change - Check change in an MC object ' s plugged state
*
* @ mc_dev : pointer to the fsl - mc device for a given MC object
* @ obj_desc : pointer to the MC object ' s descriptor in the MC
*
* If the plugged state has changed from unplugged to plugged , the fsl - mc
* device is bound to the corresponding device driver .
* If the plugged state has changed from plugged to unplugged , the fsl - mc
* device is unbound from the corresponding device driver .
*/
static void check_plugged_state_change ( struct fsl_mc_device * mc_dev ,
2017-06-27 17:41:24 +03:00
struct fsl_mc_obj_desc * obj_desc )
2015-03-28 00:01:06 +03:00
{
int error ;
2015-09-25 19:21:01 +03:00
u32 plugged_flag_at_mc =
2017-06-27 17:41:24 +03:00
obj_desc - > state & FSL_MC_OBJ_STATE_PLUGGED ;
2015-03-28 00:01:06 +03:00
if ( plugged_flag_at_mc ! =
2017-06-27 17:41:24 +03:00
( mc_dev - > obj_desc . state & FSL_MC_OBJ_STATE_PLUGGED ) ) {
2015-03-28 00:01:06 +03:00
if ( plugged_flag_at_mc ) {
2017-06-27 17:41:24 +03:00
mc_dev - > obj_desc . state | = FSL_MC_OBJ_STATE_PLUGGED ;
2015-03-28 00:01:06 +03:00
error = device_attach ( & mc_dev - > dev ) ;
if ( error < 0 ) {
dev_err ( & mc_dev - > dev ,
" device_attach() failed: %d \n " ,
error ) ;
}
} else {
2017-06-27 17:41:24 +03:00
mc_dev - > obj_desc . state & = ~ FSL_MC_OBJ_STATE_PLUGGED ;
2015-03-28 00:01:06 +03:00
device_release_driver ( & mc_dev - > dev ) ;
}
}
}
2020-07-17 18:48:00 +03:00
static void fsl_mc_obj_device_add ( struct fsl_mc_device * mc_bus_dev ,
struct fsl_mc_obj_desc * obj_desc )
{
int error ;
struct fsl_mc_device * child_dev ;
/*
* Check if device is already known to Linux :
*/
child_dev = fsl_mc_device_lookup ( obj_desc , mc_bus_dev ) ;
if ( child_dev ) {
check_plugged_state_change ( child_dev , obj_desc ) ;
put_device ( & child_dev - > dev ) ;
} else {
error = fsl_mc_device_add ( obj_desc , NULL , & mc_bus_dev - > dev ,
& child_dev ) ;
if ( error < 0 )
return ;
}
}
2015-03-06 04:29:11 +03:00
/**
* dprc_add_new_devices - Adds devices to the logical bus for a DPRC
*
* @ mc_bus_dev : pointer to the fsl - mc device that represents a DPRC object
* @ obj_desc_array : array of device descriptors for child devices currently
* present in the physical DPRC .
* @ num_child_objects_in_mc : number of entries in obj_desc_array
*
* Synchronizes the state of the Linux bus driver with the actual
* state of the MC by adding objects that have been newly discovered
* in the physical DPRC .
*/
static void dprc_add_new_devices ( struct fsl_mc_device * mc_bus_dev ,
2017-06-27 17:41:24 +03:00
struct fsl_mc_obj_desc * obj_desc_array ,
2015-03-06 04:29:11 +03:00
int num_child_objects_in_mc )
{
int i ;
2020-07-17 18:48:00 +03:00
/* probe the allocable objects first */
2015-03-06 04:29:11 +03:00
for ( i = 0 ; i < num_child_objects_in_mc ; i + + ) {
2017-06-27 17:41:24 +03:00
struct fsl_mc_obj_desc * obj_desc = & obj_desc_array [ i ] ;
2015-03-06 04:29:11 +03:00
2020-07-17 18:48:00 +03:00
if ( strlen ( obj_desc - > type ) > 0 & &
fsl_mc_obj_desc_is_allocatable ( obj_desc ) )
fsl_mc_obj_device_add ( mc_bus_dev , obj_desc ) ;
}
2015-03-06 04:29:11 +03:00
2020-07-17 18:48:00 +03:00
for ( i = 0 ; i < num_child_objects_in_mc ; i + + ) {
struct fsl_mc_obj_desc * obj_desc = & obj_desc_array [ i ] ;
2015-03-06 04:29:11 +03:00
2020-07-17 18:48:00 +03:00
if ( strlen ( obj_desc - > type ) > 0 & &
! fsl_mc_obj_desc_is_allocatable ( obj_desc ) )
fsl_mc_obj_device_add ( mc_bus_dev , obj_desc ) ;
2015-03-06 04:29:11 +03:00
}
}
/**
* dprc_scan_objects - Discover objects in a DPRC
*
* @ mc_bus_dev : pointer to the fsl - mc device that represents a DPRC object
2020-09-29 11:54:30 +03:00
* @ alloc_interrupts : if true the function allocates the interrupt pool ,
* otherwise the interrupt allocation is delayed
2015-03-06 04:29:11 +03:00
*
* Detects objects added and removed from a DPRC and synchronizes the
* state of the Linux bus driver , MC by adding and removing
* devices accordingly .
2015-03-06 04:35:25 +03:00
* Two types of devices can be found in a DPRC : allocatable objects ( e . g . ,
* dpbp , dpmcp ) and non - allocatable devices ( e . g . , dprc , dpni ) .
* All allocatable devices needed to be probed before all non - allocatable
* devices , to ensure that device drivers for non - allocatable
* devices can allocate any type of allocatable devices .
* That is , we need to ensure that the corresponding resource pools are
* populated before they can get allocation requests from probe callbacks
* of the device drivers for the non - allocatable devices .
2015-03-06 04:29:11 +03:00
*/
2021-01-14 20:07:51 +03:00
int dprc_scan_objects ( struct fsl_mc_device * mc_bus_dev ,
bool alloc_interrupts )
2015-03-06 04:29:11 +03:00
{
int num_child_objects ;
int dprc_get_obj_failures ;
int error ;
2016-01-07 01:03:25 +03:00
unsigned int irq_count = mc_bus_dev - > obj_desc . irq_count ;
2017-06-27 17:41:24 +03:00
struct fsl_mc_obj_desc * child_obj_desc_array = NULL ;
2017-12-11 18:45:37 +03:00
struct fsl_mc_bus * mc_bus = to_fsl_mc_bus ( mc_bus_dev ) ;
2015-03-06 04:29:11 +03:00
error = dprc_get_obj_count ( mc_bus_dev - > mc_io ,
2015-09-24 00:11:03 +03:00
0 ,
2015-03-06 04:29:11 +03:00
mc_bus_dev - > mc_handle ,
& num_child_objects ) ;
if ( error < 0 ) {
dev_err ( & mc_bus_dev - > dev , " dprc_get_obj_count() failed: %d \n " ,
error ) ;
return error ;
}
if ( num_child_objects ! = 0 ) {
int i ;
child_obj_desc_array =
devm_kmalloc_array ( & mc_bus_dev - > dev , num_child_objects ,
sizeof ( * child_obj_desc_array ) ,
GFP_KERNEL ) ;
if ( ! child_obj_desc_array )
return - ENOMEM ;
/*
* Discover objects currently present in the physical DPRC :
*/
dprc_get_obj_failures = 0 ;
for ( i = 0 ; i < num_child_objects ; i + + ) {
2017-06-27 17:41:24 +03:00
struct fsl_mc_obj_desc * obj_desc =
2015-03-06 04:29:11 +03:00
& child_obj_desc_array [ i ] ;
error = dprc_get_obj ( mc_bus_dev - > mc_io ,
2015-09-24 00:11:03 +03:00
0 ,
2015-03-06 04:29:11 +03:00
mc_bus_dev - > mc_handle ,
i , obj_desc ) ;
if ( error < 0 ) {
dev_err ( & mc_bus_dev - > dev ,
" dprc_get_obj(i=%d) failed: %d \n " ,
i , error ) ;
/*
* Mark the obj entry as " invalid " , by using the
* empty string as obj type :
*/
obj_desc - > type [ 0 ] = ' \0 ' ;
obj_desc - > id = error ;
dprc_get_obj_failures + + ;
continue ;
}
2016-04-11 19:50:26 +03:00
/*
* add a quirk for all versions of dpsec < 4.0 . . . none
* are coherent regardless of what the MC reports .
*/
if ( ( strcmp ( obj_desc - > type , " dpseci " ) = = 0 ) & &
( obj_desc - > ver_major < 4 ) )
obj_desc - > flags | =
2017-06-27 17:41:24 +03:00
FSL_MC_OBJ_FLAG_NO_MEM_SHAREABILITY ;
2016-04-11 19:50:26 +03:00
2016-01-07 01:03:25 +03:00
irq_count + = obj_desc - > irq_count ;
2015-03-06 04:29:11 +03:00
dev_dbg ( & mc_bus_dev - > dev ,
" Discovered object: type %s, id %d \n " ,
obj_desc - > type , obj_desc - > id ) ;
}
if ( dprc_get_obj_failures ! = 0 ) {
dev_err ( & mc_bus_dev - > dev ,
" %d out of %d devices could not be retrieved \n " ,
dprc_get_obj_failures , num_child_objects ) ;
}
}
2017-12-11 18:45:37 +03:00
/*
* Allocate IRQ ' s before binding the scanned devices with their
* respective drivers .
*/
2020-09-29 11:54:29 +03:00
if ( dev_get_msi_domain ( & mc_bus_dev - > dev ) ) {
2017-12-11 18:45:37 +03:00
if ( irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS ) {
dev_warn ( & mc_bus_dev - > dev ,
" IRQs needed (%u) exceed IRQs preallocated (%u) \n " ,
irq_count , FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS ) ;
}
2020-09-29 11:54:30 +03:00
if ( alloc_interrupts & & ! mc_bus - > irq_resources ) {
2020-09-29 11:54:39 +03:00
error = fsl_mc_populate_irq_pool ( mc_bus_dev ,
FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS ) ;
2020-09-29 11:54:29 +03:00
if ( error < 0 )
return error ;
}
2017-12-11 18:45:37 +03:00
}
2015-03-06 04:29:11 +03:00
dprc_remove_devices ( mc_bus_dev , child_obj_desc_array ,
num_child_objects ) ;
dprc_add_new_devices ( mc_bus_dev , child_obj_desc_array ,
num_child_objects ) ;
if ( child_obj_desc_array )
devm_kfree ( & mc_bus_dev - > dev , child_obj_desc_array ) ;
return 0 ;
}
/**
* dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state
*
* @ mc_bus_dev : pointer to the fsl - mc device that represents a DPRC object
2021-06-17 14:04:57 +03:00
* @ alloc_interrupts : if true the function allocates the interrupt pool ,
* otherwise the interrupt allocation is delayed
2015-03-06 04:29:11 +03:00
* Scans the physical DPRC and synchronizes the state of the Linux
* bus driver with the actual state of the MC by adding and removing
* devices as appropriate .
*/
2020-09-29 11:54:35 +03:00
int dprc_scan_container ( struct fsl_mc_device * mc_bus_dev ,
bool alloc_interrupts )
2015-03-06 04:29:11 +03:00
{
2020-09-29 11:54:35 +03:00
int error = 0 ;
2015-03-06 04:29:11 +03:00
struct fsl_mc_bus * mc_bus = to_fsl_mc_bus ( mc_bus_dev ) ;
2016-08-24 01:13:24 +03:00
fsl_mc_init_all_resource_pools ( mc_bus_dev ) ;
2015-03-06 04:35:25 +03:00
2015-03-06 04:29:11 +03:00
/*
* Discover objects in the DPRC :
*/
mutex_lock ( & mc_bus - > scan_mutex ) ;
2020-09-29 11:54:35 +03:00
error = dprc_scan_objects ( mc_bus_dev , alloc_interrupts ) ;
2015-03-06 04:29:11 +03:00
mutex_unlock ( & mc_bus - > scan_mutex ) ;
2016-01-07 01:03:25 +03:00
2020-09-29 11:54:35 +03:00
return error ;
2015-03-06 04:29:11 +03:00
}
2020-09-29 11:54:35 +03:00
EXPORT_SYMBOL_GPL ( dprc_scan_container ) ;
2021-06-17 14:04:57 +03:00
2016-01-07 01:03:28 +03:00
/**
* dprc_irq0_handler - Regular ISR for DPRC interrupt 0
*
2021-06-17 14:04:57 +03:00
* @ irq_num : IRQ number of the interrupt being handled
2016-01-07 01:03:28 +03:00
* @ arg : Pointer to device structure
*/
static irqreturn_t dprc_irq0_handler ( int irq_num , void * arg )
{
return IRQ_WAKE_THREAD ;
}
/**
* dprc_irq0_handler_thread - Handler thread function for DPRC interrupt 0
*
2021-06-17 14:04:57 +03:00
* @ irq_num : IRQ number of the interrupt being handled
2016-01-07 01:03:28 +03:00
* @ arg : Pointer to device structure
*/
static irqreturn_t dprc_irq0_handler_thread ( int irq_num , void * arg )
{
int error ;
u32 status ;
2016-02-25 22:46:11 +03:00
struct device * dev = arg ;
2016-01-07 01:03:28 +03:00
struct fsl_mc_device * mc_dev = to_fsl_mc_device ( dev ) ;
struct fsl_mc_bus * mc_bus = to_fsl_mc_bus ( mc_dev ) ;
struct fsl_mc_io * mc_io = mc_dev - > mc_io ;
2021-12-11 01:19:34 +03:00
int irq = mc_dev - > irqs [ 0 ] - > virq ;
2016-01-07 01:03:28 +03:00
dev_dbg ( dev , " DPRC IRQ %d triggered on CPU %u \n " ,
irq_num , smp_processor_id ( ) ) ;
2017-11-17 16:38:32 +03:00
if ( ! ( mc_dev - > flags & FSL_MC_IS_DPRC ) )
2016-01-07 01:03:28 +03:00
return IRQ_HANDLED ;
mutex_lock ( & mc_bus - > scan_mutex ) ;
2021-12-11 01:19:34 +03:00
if ( irq ! = ( u32 ) irq_num )
2016-01-07 01:03:28 +03:00
goto out ;
2016-04-11 19:48:59 +03:00
status = 0 ;
2016-01-07 01:03:28 +03:00
error = dprc_get_irq_status ( mc_io , 0 , mc_dev - > mc_handle , 0 ,
& status ) ;
if ( error < 0 ) {
dev_err ( dev ,
" dprc_get_irq_status() failed: %d \n " , error ) ;
goto out ;
}
error = dprc_clear_irq_status ( mc_io , 0 , mc_dev - > mc_handle , 0 ,
status ) ;
if ( error < 0 ) {
dev_err ( dev ,
" dprc_clear_irq_status() failed: %d \n " , error ) ;
goto out ;
}
if ( status & ( DPRC_IRQ_EVENT_OBJ_ADDED |
DPRC_IRQ_EVENT_OBJ_REMOVED |
DPRC_IRQ_EVENT_CONTAINER_DESTROYED |
DPRC_IRQ_EVENT_OBJ_DESTROYED |
DPRC_IRQ_EVENT_OBJ_CREATED ) ) {
2020-09-29 11:54:30 +03:00
error = dprc_scan_objects ( mc_dev , true ) ;
2016-01-07 01:03:28 +03:00
if ( error < 0 ) {
/*
* If the error is - ENXIO , we ignore it , as it indicates
* that the object scan was aborted , as we detected that
* an object was removed from the DPRC in the MC , while
* we were scanning the DPRC .
*/
if ( error ! = - ENXIO ) {
dev_err ( dev , " dprc_scan_objects() failed: %d \n " ,
error ) ;
}
goto out ;
}
}
out :
mutex_unlock ( & mc_bus - > scan_mutex ) ;
return IRQ_HANDLED ;
}
/*
* Disable and clear interrupt for a given DPRC object
*/
2021-01-14 20:07:52 +03:00
int disable_dprc_irq ( struct fsl_mc_device * mc_dev )
2016-01-07 01:03:28 +03:00
{
2021-01-14 20:07:52 +03:00
struct fsl_mc_bus * mc_bus = to_fsl_mc_bus ( mc_dev ) ;
2016-01-07 01:03:28 +03:00
int error ;
struct fsl_mc_io * mc_io = mc_dev - > mc_io ;
/*
* Disable generation of interrupt , while we configure it :
*/
error = dprc_set_irq_enable ( mc_io , 0 , mc_dev - > mc_handle , 0 , 0 ) ;
if ( error < 0 ) {
dev_err ( & mc_dev - > dev ,
" Disabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d \n " ,
error ) ;
return error ;
}
/*
* Disable all interrupt causes for the interrupt :
*/
error = dprc_set_irq_mask ( mc_io , 0 , mc_dev - > mc_handle , 0 , 0x0 ) ;
if ( error < 0 ) {
dev_err ( & mc_dev - > dev ,
" Disabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d \n " ,
error ) ;
return error ;
}
/*
* Clear any leftover interrupts :
*/
error = dprc_clear_irq_status ( mc_io , 0 , mc_dev - > mc_handle , 0 , ~ 0x0U ) ;
if ( error < 0 ) {
dev_err ( & mc_dev - > dev ,
" Disabling DPRC IRQ failed: dprc_clear_irq_status() failed: %d \n " ,
error ) ;
return error ;
}
2021-01-14 20:07:52 +03:00
mc_bus - > irq_enabled = 0 ;
2016-01-07 01:03:28 +03:00
return 0 ;
}
2021-01-14 20:07:52 +03:00
int get_dprc_irq_state ( struct fsl_mc_device * mc_dev )
{
struct fsl_mc_bus * mc_bus = to_fsl_mc_bus ( mc_dev ) ;
return mc_bus - > irq_enabled ;
}
2016-01-07 01:03:28 +03:00
static int register_dprc_irq_handler ( struct fsl_mc_device * mc_dev )
{
int error ;
struct fsl_mc_device_irq * irq = mc_dev - > irqs [ 0 ] ;
/*
* NOTE : devm_request_threaded_irq ( ) invokes the device - specific
* function that programs the MSI physically in the device
*/
error = devm_request_threaded_irq ( & mc_dev - > dev ,
2021-12-11 01:19:34 +03:00
irq - > virq ,
2016-01-07 01:03:28 +03:00
dprc_irq0_handler ,
dprc_irq0_handler_thread ,
IRQF_NO_SUSPEND | IRQF_ONESHOT ,
2016-10-17 21:43:01 +03:00
dev_name ( & mc_dev - > dev ) ,
2016-01-07 01:03:28 +03:00
& mc_dev - > dev ) ;
if ( error < 0 ) {
dev_err ( & mc_dev - > dev ,
" devm_request_threaded_irq() failed: %d \n " ,
error ) ;
return error ;
}
return 0 ;
}
2021-01-14 20:07:52 +03:00
int enable_dprc_irq ( struct fsl_mc_device * mc_dev )
2016-01-07 01:03:28 +03:00
{
2021-01-14 20:07:52 +03:00
struct fsl_mc_bus * mc_bus = to_fsl_mc_bus ( mc_dev ) ;
2016-01-07 01:03:28 +03:00
int error ;
/*
* Enable all interrupt causes for the interrupt :
*/
error = dprc_set_irq_mask ( mc_dev - > mc_io , 0 , mc_dev - > mc_handle , 0 ,
~ 0x0u ) ;
if ( error < 0 ) {
dev_err ( & mc_dev - > dev ,
" Enabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d \n " ,
error ) ;
return error ;
}
/*
* Enable generation of the interrupt :
*/
error = dprc_set_irq_enable ( mc_dev - > mc_io , 0 , mc_dev - > mc_handle , 0 , 1 ) ;
if ( error < 0 ) {
dev_err ( & mc_dev - > dev ,
" Enabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d \n " ,
error ) ;
return error ;
}
2021-01-14 20:07:52 +03:00
mc_bus - > irq_enabled = 1 ;
2016-01-07 01:03:28 +03:00
return 0 ;
}
/*
* Setup interrupt for a given DPRC device
*/
static int dprc_setup_irq ( struct fsl_mc_device * mc_dev )
{
int error ;
error = fsl_mc_allocate_irqs ( mc_dev ) ;
if ( error < 0 )
return error ;
error = disable_dprc_irq ( mc_dev ) ;
if ( error < 0 )
goto error_free_irqs ;
error = register_dprc_irq_handler ( mc_dev ) ;
if ( error < 0 )
goto error_free_irqs ;
error = enable_dprc_irq ( mc_dev ) ;
if ( error < 0 )
goto error_free_irqs ;
return 0 ;
error_free_irqs :
fsl_mc_free_irqs ( mc_dev ) ;
return error ;
}
2015-03-06 04:29:11 +03:00
/**
2020-09-29 11:54:37 +03:00
* dprc_setup - opens and creates a mc_io for DPRC
2015-03-06 04:29:11 +03:00
*
* @ mc_dev : Pointer to fsl - mc device representing a DPRC
*
* It opens the physical DPRC in the MC .
2020-09-29 11:54:37 +03:00
* It configures the DPRC portal used to communicate with MC
2015-03-06 04:29:11 +03:00
*/
2020-09-29 11:54:37 +03:00
int dprc_setup ( struct fsl_mc_device * mc_dev )
2015-03-06 04:29:11 +03:00
{
2016-01-07 01:03:26 +03:00
struct device * parent_dev = mc_dev - > dev . parent ;
2015-03-06 04:29:11 +03:00
struct fsl_mc_bus * mc_bus = to_fsl_mc_bus ( mc_dev ) ;
2020-09-29 11:54:37 +03:00
struct irq_domain * mc_msi_domain ;
2016-01-07 01:03:27 +03:00
bool mc_io_created = false ;
2016-01-07 01:03:26 +03:00
bool msi_domain_set = false ;
2021-01-14 20:07:50 +03:00
bool uapi_created = false ;
2016-10-26 19:20:34 +03:00
u16 major_ver , minor_ver ;
2020-09-29 11:54:37 +03:00
size_t region_size ;
int error ;
2015-03-06 04:29:11 +03:00
2017-11-17 16:38:34 +03:00
if ( ! is_fsl_mc_bus_dprc ( mc_dev ) )
2015-03-06 04:29:11 +03:00
return - EINVAL ;
2017-11-17 16:38:32 +03:00
if ( dev_get_msi_domain ( & mc_dev - > dev ) )
2016-01-07 01:03:26 +03:00
return - EINVAL ;
2015-03-06 04:29:11 +03:00
if ( ! mc_dev - > mc_io ) {
/*
* This is a child DPRC :
*/
2017-11-17 16:38:32 +03:00
if ( ! dev_is_fsl_mc ( parent_dev ) )
2016-01-07 01:03:27 +03:00
return - EINVAL ;
2017-11-17 16:38:32 +03:00
if ( mc_dev - > obj_desc . region_count = = 0 )
2015-03-06 04:29:11 +03:00
return - EINVAL ;
2017-08-02 11:18:07 +03:00
region_size = resource_size ( mc_dev - > regions ) ;
2015-03-06 04:29:11 +03:00
error = fsl_create_mc_io ( & mc_dev - > dev ,
mc_dev - > regions [ 0 ] . start ,
region_size ,
2016-01-07 01:03:24 +03:00
NULL ,
FSL_MC_IO_ATOMIC_CONTEXT_PORTAL ,
& mc_dev - > mc_io ) ;
2015-03-06 04:29:11 +03:00
if ( error < 0 )
return error ;
2016-01-07 01:03:27 +03:00
mc_io_created = true ;
2021-01-14 20:07:50 +03:00
} else {
error = fsl_mc_uapi_create_device_file ( mc_bus ) ;
if ( error < 0 )
return - EPROBE_DEFER ;
uapi_created = true ;
2020-06-19 11:20:12 +03:00
}
2016-01-07 01:03:27 +03:00
2020-06-19 11:20:12 +03:00
mc_msi_domain = fsl_mc_find_msi_domain ( & mc_dev - > dev ) ;
if ( ! mc_msi_domain ) {
dev_warn ( & mc_dev - > dev ,
" WARNING: MC bus without interrupt support \n " ) ;
2016-01-07 01:03:26 +03:00
} else {
2020-06-19 11:20:12 +03:00
dev_set_msi_domain ( & mc_dev - > dev , mc_msi_domain ) ;
msi_domain_set = true ;
2015-03-06 04:29:11 +03:00
}
2015-09-24 00:11:03 +03:00
error = dprc_open ( mc_dev - > mc_io , 0 , mc_dev - > obj_desc . id ,
2015-03-06 04:29:11 +03:00
& mc_dev - > mc_handle ) ;
if ( error < 0 ) {
dev_err ( & mc_dev - > dev , " dprc_open() failed: %d \n " , error ) ;
2016-01-07 01:03:27 +03:00
goto error_cleanup_msi_domain ;
2015-03-06 04:29:11 +03:00
}
2016-04-11 19:56:05 +03:00
error = dprc_get_attributes ( mc_dev - > mc_io , 0 , mc_dev - > mc_handle ,
& mc_bus - > dprc_attr ) ;
if ( error < 0 ) {
dev_err ( & mc_dev - > dev , " dprc_get_attributes() failed: %d \n " ,
error ) ;
goto error_cleanup_open ;
}
2016-10-26 19:20:34 +03:00
error = dprc_get_api_version ( mc_dev - > mc_io , 0 ,
& major_ver ,
& minor_ver ) ;
if ( error < 0 ) {
dev_err ( & mc_dev - > dev , " dprc_get_api_version() failed: %d \n " ,
error ) ;
goto error_cleanup_open ;
}
2020-11-23 19:48:39 +03:00
if ( major_ver < DPRC_MIN_VER_MAJOR ) {
2016-04-11 19:56:05 +03:00
dev_err ( & mc_dev - > dev ,
" ERROR: DPRC version %d.%d not supported \n " ,
2016-10-26 19:20:34 +03:00
major_ver , minor_ver ) ;
2016-04-11 19:56:05 +03:00
error = - ENOTSUPP ;
goto error_cleanup_open ;
}
2020-09-29 11:54:37 +03:00
return 0 ;
error_cleanup_open :
( void ) dprc_close ( mc_dev - > mc_io , 0 , mc_dev - > mc_handle ) ;
error_cleanup_msi_domain :
if ( msi_domain_set )
dev_set_msi_domain ( & mc_dev - > dev , NULL ) ;
if ( mc_io_created ) {
fsl_destroy_mc_io ( mc_dev - > mc_io ) ;
mc_dev - > mc_io = NULL ;
}
2021-01-14 20:07:50 +03:00
if ( uapi_created )
fsl_mc_uapi_remove_device_file ( mc_bus ) ;
2020-09-29 11:54:37 +03:00
return error ;
}
EXPORT_SYMBOL_GPL ( dprc_setup ) ;
/**
* dprc_probe - callback invoked when a DPRC is being bound to this driver
*
* @ mc_dev : Pointer to fsl - mc device representing a DPRC
*
* It opens the physical DPRC in the MC .
* It scans the DPRC to discover the MC objects contained in it .
* It creates the interrupt pool for the MC bus associated with the DPRC .
* It configures the interrupts for the DPRC device itself .
*/
static int dprc_probe ( struct fsl_mc_device * mc_dev )
{
int error ;
error = dprc_setup ( mc_dev ) ;
if ( error < 0 )
return error ;
2015-03-06 04:29:11 +03:00
/*
* Discover MC objects in DPRC object :
*/
2020-09-29 11:54:35 +03:00
error = dprc_scan_container ( mc_dev , true ) ;
2015-03-06 04:29:11 +03:00
if ( error < 0 )
2020-09-29 11:54:37 +03:00
goto dprc_cleanup ;
2015-03-06 04:29:11 +03:00
2016-01-07 01:03:28 +03:00
/*
* Configure interrupt for the DPRC object associated with this MC bus :
*/
error = dprc_setup_irq ( mc_dev ) ;
if ( error < 0 )
2020-09-29 11:54:37 +03:00
goto scan_cleanup ;
2016-01-07 01:03:28 +03:00
2015-03-06 04:29:11 +03:00
dev_info ( & mc_dev - > dev , " DPRC device bound to driver " ) ;
return 0 ;
2020-09-29 11:54:37 +03:00
scan_cleanup :
device_for_each_child ( & mc_dev - > dev , NULL , __fsl_mc_device_remove ) ;
dprc_cleanup :
dprc_cleanup ( mc_dev ) ;
2015-03-06 04:29:11 +03:00
return error ;
}
2016-01-07 01:03:28 +03:00
/*
* Tear down interrupt for a given DPRC object
*/
static void dprc_teardown_irq ( struct fsl_mc_device * mc_dev )
{
2016-06-23 00:40:49 +03:00
struct fsl_mc_device_irq * irq = mc_dev - > irqs [ 0 ] ;
2016-01-07 01:03:28 +03:00
( void ) disable_dprc_irq ( mc_dev ) ;
2016-06-23 00:40:49 +03:00
2021-12-11 01:19:34 +03:00
devm_free_irq ( & mc_dev - > dev , irq - > virq , & mc_dev - > dev ) ;
2016-06-23 00:40:49 +03:00
2016-01-07 01:03:28 +03:00
fsl_mc_free_irqs ( mc_dev ) ;
}
2015-03-06 04:29:11 +03:00
/**
2020-09-29 11:54:36 +03:00
* dprc_cleanup - function that cleanups a DPRC
2015-03-06 04:29:11 +03:00
*
* @ mc_dev : Pointer to fsl - mc device representing the DPRC
*
2020-09-29 11:54:36 +03:00
* It closes the DPRC device in the MC .
2015-03-06 04:35:25 +03:00
* It destroys the interrupt pool associated with this MC bus .
2015-03-06 04:29:11 +03:00
*/
2020-09-29 11:54:36 +03:00
int dprc_cleanup ( struct fsl_mc_device * mc_dev )
2015-03-06 04:29:11 +03:00
{
2021-01-14 20:07:50 +03:00
struct fsl_mc_bus * mc_bus = to_fsl_mc_bus ( mc_dev ) ;
2015-03-06 04:29:11 +03:00
int error ;
2020-09-29 11:54:36 +03:00
/* this function should be called only for DPRCs, it
* is an error to call it for regular objects
*/
if ( ! is_fsl_mc_bus_dprc ( mc_dev ) )
2016-01-07 01:03:28 +03:00
return - EINVAL ;
2016-01-07 01:03:26 +03:00
if ( dev_get_msi_domain ( & mc_dev - > dev ) ) {
2020-09-29 11:54:39 +03:00
fsl_mc_cleanup_irq_pool ( mc_dev ) ;
2016-01-07 01:03:26 +03:00
dev_set_msi_domain ( & mc_dev - > dev , NULL ) ;
}
2016-08-24 01:13:24 +03:00
fsl_mc_cleanup_all_resource_pools ( mc_dev ) ;
2016-06-23 00:40:50 +03:00
2020-09-29 11:54:36 +03:00
/* if this step fails we cannot go further with cleanup as there is no way of
* communicating with the firmware
*/
if ( ! mc_dev - > mc_io ) {
dev_err ( & mc_dev - > dev , " mc_io is NULL, tear down cannot be performed in firmware \n " ) ;
return - EINVAL ;
}
2016-06-23 00:40:50 +03:00
error = dprc_close ( mc_dev - > mc_io , 0 , mc_dev - > mc_handle ) ;
if ( error < 0 )
dev_err ( & mc_dev - > dev , " dprc_close() failed: %d \n " , error ) ;
2016-06-23 00:40:48 +03:00
if ( ! fsl_mc_is_root_dprc ( & mc_dev - > dev ) ) {
fsl_destroy_mc_io ( mc_dev - > mc_io ) ;
mc_dev - > mc_io = NULL ;
2021-01-14 20:07:50 +03:00
} else {
fsl_mc_uapi_remove_device_file ( mc_bus ) ;
2016-06-23 00:40:48 +03:00
}
2020-09-29 11:54:36 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( dprc_cleanup ) ;
/**
* dprc_remove - callback invoked when a DPRC is being unbound from this driver
*
* @ mc_dev : Pointer to fsl - mc device representing the DPRC
*
* It removes the DPRC ' s child objects from Linux ( not from the MC ) and
* closes the DPRC device in the MC .
* It tears down the interrupts that were configured for the DPRC device .
* It destroys the interrupt pool associated with this MC bus .
*/
2023-03-11 01:41:28 +03:00
static void dprc_remove ( struct fsl_mc_device * mc_dev )
2020-09-29 11:54:36 +03:00
{
struct fsl_mc_bus * mc_bus = to_fsl_mc_bus ( mc_dev ) ;
2023-03-11 01:41:24 +03:00
if ( ! mc_bus - > irq_resources ) {
dev_err ( & mc_dev - > dev , " No irq resources, so unbinding the device failed \n " ) ;
2023-03-11 01:41:28 +03:00
return ;
2023-03-11 01:41:24 +03:00
}
2020-09-29 11:54:36 +03:00
if ( dev_get_msi_domain ( & mc_dev - > dev ) )
dprc_teardown_irq ( mc_dev ) ;
device_for_each_child ( & mc_dev - > dev , NULL , __fsl_mc_device_remove ) ;
dprc_cleanup ( mc_dev ) ;
2015-03-06 04:29:11 +03:00
dev_info ( & mc_dev - > dev , " DPRC device unbound from driver " ) ;
}
2016-06-23 00:40:44 +03:00
static const struct fsl_mc_device_id match_id_table [ ] = {
2015-03-06 04:29:11 +03:00
{
. vendor = FSL_MC_VENDOR_FREESCALE ,
2016-04-11 19:55:40 +03:00
. obj_type = " dprc " } ,
2015-03-06 04:29:11 +03:00
{ . vendor = 0x0 } ,
} ;
static struct fsl_mc_driver dprc_driver = {
. driver = {
. name = FSL_MC_DPRC_DRIVER_NAME ,
. owner = THIS_MODULE ,
. pm = NULL ,
} ,
. match_id_table = match_id_table ,
. probe = dprc_probe ,
. remove = dprc_remove ,
} ;
int __init dprc_driver_init ( void )
{
return fsl_mc_driver_register ( & dprc_driver ) ;
}
2015-10-26 01:41:19 +03:00
void dprc_driver_exit ( void )
2015-03-06 04:29:11 +03:00
{
fsl_mc_driver_unregister ( & dprc_driver ) ;
}