2017-10-30 18:33:30 +00:00
// SPDX-License-Identifier: GPL-2.0
/*
* System Control and Management Interface ( SCMI ) Message Protocol bus layer
*
2021-03-16 12:48:26 +00:00
* Copyright ( C ) 2018 - 2021 ARM Ltd .
2017-10-30 18:33:30 +00:00
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/types.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/device.h>
# include "common.h"
static DEFINE_IDA ( scmi_bus_id ) ;
static DEFINE_IDR ( scmi_protocols ) ;
static DEFINE_SPINLOCK ( protocol_lock ) ;
static const struct scmi_device_id *
scmi_dev_match_id ( struct scmi_device * scmi_dev , struct scmi_driver * scmi_drv )
{
const struct scmi_device_id * id = scmi_drv - > id_table ;
if ( ! id )
return NULL ;
for ( ; id - > protocol_id ; id + + )
2018-12-21 18:08:08 +00:00
if ( id - > protocol_id = = scmi_dev - > protocol_id ) {
if ( ! id - > name )
return id ;
else if ( ! strcmp ( id - > name , scmi_dev - > name ) )
return id ;
}
2017-10-30 18:33:30 +00:00
return NULL ;
}
static int scmi_dev_match ( struct device * dev , struct device_driver * drv )
{
struct scmi_driver * scmi_drv = to_scmi_driver ( drv ) ;
struct scmi_device * scmi_dev = to_scmi_dev ( dev ) ;
const struct scmi_device_id * id ;
id = scmi_dev_match_id ( scmi_dev , scmi_drv ) ;
if ( id )
return 1 ;
return 0 ;
}
2021-03-16 12:48:26 +00:00
const struct scmi_protocol * scmi_protocol_get ( int protocol_id )
{
const struct scmi_protocol * proto ;
proto = idr_find ( & scmi_protocols , protocol_id ) ;
if ( ! proto ) {
pr_warn ( " SCMI Protocol 0x%x not found! \n " , protocol_id ) ;
return NULL ;
}
pr_debug ( " Found SCMI Protocol 0x%x \n " , protocol_id ) ;
return proto ;
}
2017-10-30 18:33:30 +00:00
static int scmi_protocol_init ( int protocol_id , struct scmi_handle * handle )
{
2021-03-16 12:48:26 +00:00
const struct scmi_protocol * proto ;
2017-10-30 18:33:30 +00:00
2021-03-16 12:48:26 +00:00
proto = scmi_protocol_get ( protocol_id ) ;
if ( ! proto )
2017-10-30 18:33:30 +00:00
return - EINVAL ;
2021-03-16 12:48:26 +00:00
return proto - > init ( handle ) ;
2017-10-30 18:33:30 +00:00
}
2019-11-07 11:39:45 +00:00
static int scmi_protocol_dummy_init ( struct scmi_handle * handle )
{
return 0 ;
}
2017-10-30 18:33:30 +00:00
static int scmi_dev_probe ( struct device * dev )
{
struct scmi_driver * scmi_drv = to_scmi_driver ( dev - > driver ) ;
struct scmi_device * scmi_dev = to_scmi_dev ( dev ) ;
const struct scmi_device_id * id ;
int ret ;
id = scmi_dev_match_id ( scmi_dev , scmi_drv ) ;
if ( ! id )
return - ENODEV ;
if ( ! scmi_dev - > handle )
return - EPROBE_DEFER ;
ret = scmi_protocol_init ( scmi_dev - > protocol_id , scmi_dev - > handle ) ;
if ( ret )
return ret ;
2019-11-07 11:39:45 +00:00
/* Skip protocol initialisation for additional devices */
idr_replace ( & scmi_protocols , & scmi_protocol_dummy_init ,
scmi_dev - > protocol_id ) ;
2017-10-30 18:33:30 +00:00
return scmi_drv - > probe ( scmi_dev ) ;
}
static int scmi_dev_remove ( struct device * dev )
{
struct scmi_driver * scmi_drv = to_scmi_driver ( dev - > driver ) ;
struct scmi_device * scmi_dev = to_scmi_dev ( dev ) ;
if ( scmi_drv - > remove )
scmi_drv - > remove ( scmi_dev ) ;
return 0 ;
}
static struct bus_type scmi_bus_type = {
. name = " scmi_protocol " ,
. match = scmi_dev_match ,
. probe = scmi_dev_probe ,
. remove = scmi_dev_remove ,
} ;
int scmi_driver_register ( struct scmi_driver * driver , struct module * owner ,
const char * mod_name )
{
int retval ;
driver - > driver . bus = & scmi_bus_type ;
driver - > driver . name = driver - > name ;
driver - > driver . owner = owner ;
driver - > driver . mod_name = mod_name ;
retval = driver_register ( & driver - > driver ) ;
if ( ! retval )
pr_debug ( " registered new scmi driver %s \n " , driver - > name ) ;
return retval ;
}
EXPORT_SYMBOL_GPL ( scmi_driver_register ) ;
void scmi_driver_unregister ( struct scmi_driver * driver )
{
driver_unregister ( & driver - > driver ) ;
}
EXPORT_SYMBOL_GPL ( scmi_driver_unregister ) ;
2019-01-22 11:35:25 +00:00
static void scmi_device_release ( struct device * dev )
{
kfree ( to_scmi_dev ( dev ) ) ;
}
2017-10-30 18:33:30 +00:00
struct scmi_device *
2018-12-21 18:08:08 +00:00
scmi_device_create ( struct device_node * np , struct device * parent , int protocol ,
const char * name )
2017-10-30 18:33:30 +00:00
{
int id , retval ;
struct scmi_device * scmi_dev ;
scmi_dev = kzalloc ( sizeof ( * scmi_dev ) , GFP_KERNEL ) ;
if ( ! scmi_dev )
2018-05-09 17:52:06 +01:00
return NULL ;
2018-12-21 18:08:08 +00:00
scmi_dev - > name = kstrdup_const ( name ? : " unknown " , GFP_KERNEL ) ;
if ( ! scmi_dev - > name ) {
kfree ( scmi_dev ) ;
return NULL ;
}
2018-05-09 17:52:06 +01:00
id = ida_simple_get ( & scmi_bus_id , 1 , 0 , GFP_KERNEL ) ;
2019-11-25 23:54:09 +08:00
if ( id < 0 ) {
2018-12-21 18:08:08 +00:00
kfree_const ( scmi_dev - > name ) ;
2019-11-25 23:54:09 +08:00
kfree ( scmi_dev ) ;
return NULL ;
}
2017-10-30 18:33:30 +00:00
scmi_dev - > id = id ;
scmi_dev - > protocol_id = protocol ;
scmi_dev - > dev . parent = parent ;
scmi_dev - > dev . of_node = np ;
scmi_dev - > dev . bus = & scmi_bus_type ;
2019-01-22 11:35:25 +00:00
scmi_dev - > dev . release = scmi_device_release ;
2017-10-30 18:33:30 +00:00
dev_set_name ( & scmi_dev - > dev , " scmi_dev.%d " , id ) ;
retval = device_register ( & scmi_dev - > dev ) ;
2018-05-09 17:52:06 +01:00
if ( retval )
goto put_dev ;
2017-10-30 18:33:30 +00:00
2018-05-09 17:52:06 +01:00
return scmi_dev ;
put_dev :
2018-12-21 18:08:08 +00:00
kfree_const ( scmi_dev - > name ) ;
2017-10-30 18:33:30 +00:00
put_device ( & scmi_dev - > dev ) ;
ida_simple_remove ( & scmi_bus_id , id ) ;
return NULL ;
}
void scmi_device_destroy ( struct scmi_device * scmi_dev )
{
2018-12-21 18:08:08 +00:00
kfree_const ( scmi_dev - > name ) ;
2017-10-30 18:33:30 +00:00
scmi_handle_put ( scmi_dev - > handle ) ;
ida_simple_remove ( & scmi_bus_id , scmi_dev - > id ) ;
2019-01-22 11:35:25 +00:00
device_unregister ( & scmi_dev - > dev ) ;
2017-10-30 18:33:30 +00:00
}
void scmi_set_handle ( struct scmi_device * scmi_dev )
{
scmi_dev - > handle = scmi_handle_get ( & scmi_dev - > dev ) ;
}
2021-03-16 12:48:26 +00:00
int scmi_protocol_register ( const struct scmi_protocol * proto )
2017-10-30 18:33:30 +00:00
{
int ret ;
2021-03-16 12:48:26 +00:00
if ( ! proto ) {
pr_err ( " invalid protocol \n " ) ;
return - EINVAL ;
}
if ( ! proto - > init & & ! proto - > instance_init ) {
pr_err ( " missing init for protocol 0x%x \n " , proto - > id ) ;
return - EINVAL ;
}
2017-10-30 18:33:30 +00:00
spin_lock ( & protocol_lock ) ;
2021-03-16 12:48:26 +00:00
ret = idr_alloc ( & scmi_protocols , ( void * ) proto ,
proto - > id , proto - > id + 1 , GFP_ATOMIC ) ;
2018-05-09 17:52:06 +01:00
spin_unlock ( & protocol_lock ) ;
2021-03-16 12:48:26 +00:00
if ( ret ! = proto - > id ) {
pr_err ( " unable to allocate SCMI idr slot for 0x%x - err %d \n " ,
proto - > id , ret ) ;
return ret ;
}
pr_debug ( " Registered SCMI Protocol 0x%x \n " , proto - > id ) ;
2017-10-30 18:33:30 +00:00
2021-03-16 12:48:26 +00:00
return 0 ;
2017-10-30 18:33:30 +00:00
}
EXPORT_SYMBOL_GPL ( scmi_protocol_register ) ;
2021-03-16 12:48:26 +00:00
void scmi_protocol_unregister ( const struct scmi_protocol * proto )
2017-10-30 18:33:30 +00:00
{
spin_lock ( & protocol_lock ) ;
2021-03-16 12:48:26 +00:00
idr_remove ( & scmi_protocols , proto - > id ) ;
2017-10-30 18:33:30 +00:00
spin_unlock ( & protocol_lock ) ;
2021-03-16 12:48:26 +00:00
pr_debug ( " Unregistered SCMI Protocol 0x%x \n " , proto - > id ) ;
return ;
2017-10-30 18:33:30 +00:00
}
EXPORT_SYMBOL_GPL ( scmi_protocol_unregister ) ;
static int __scmi_devices_unregister ( struct device * dev , void * data )
{
struct scmi_device * scmi_dev = to_scmi_dev ( dev ) ;
scmi_device_destroy ( scmi_dev ) ;
return 0 ;
}
static void scmi_devices_unregister ( void )
{
bus_for_each_dev ( & scmi_bus_type , NULL , NULL , __scmi_devices_unregister ) ;
}
2020-09-07 12:00:04 +01:00
int __init scmi_bus_init ( void )
2017-10-30 18:33:30 +00:00
{
int retval ;
retval = bus_register ( & scmi_bus_type ) ;
if ( retval )
pr_err ( " scmi protocol bus register failed (%d) \n " , retval ) ;
return retval ;
}
2020-09-07 12:00:04 +01:00
void __exit scmi_bus_exit ( void )
2017-10-30 18:33:30 +00:00
{
scmi_devices_unregister ( ) ;
bus_unregister ( & scmi_bus_type ) ;
ida_destroy ( & scmi_bus_id ) ;
}