2019-05-27 08:55:21 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2017-01-27 02:28:32 -08:00
/*
* Qualcomm Peripheral Image Loader helpers
*
* Copyright ( C ) 2016 Linaro Ltd
* Copyright ( C ) 2015 Sony Mobile Communications Inc
* Copyright ( c ) 2012 - 2013 , The Linux Foundation . All rights reserved .
*/
# include <linux/firmware.h>
# include <linux/kernel.h>
# include <linux/module.h>
2017-07-24 22:56:43 -07:00
# include <linux/notifier.h>
2017-01-27 02:28:32 -08:00
# include <linux/remoteproc.h>
2020-06-23 19:23:27 -07:00
# include <linux/remoteproc/qcom_rproc.h>
2017-08-29 16:13:35 -07:00
# include <linux/rpmsg/qcom_glink.h>
2017-01-27 07:04:54 -08:00
# include <linux/rpmsg/qcom_smd.h>
2020-07-13 10:00:03 +08:00
# include <linux/slab.h>
2018-01-05 16:04:20 -08:00
# include <linux/soc/qcom/mdt_loader.h>
2017-01-27 02:28:32 -08:00
# include "remoteproc_internal.h"
# include "qcom_common.h"
2017-08-29 16:13:35 -07:00
# define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev)
2017-01-27 07:04:54 -08:00
# define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev)
2017-07-24 22:56:43 -07:00
# define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev)
2020-06-23 19:23:27 -07:00
struct qcom_ssr_subsystem {
const char * name ;
struct srcu_notifier_head notifier_list ;
struct list_head list ;
} ;
static LIST_HEAD ( qcom_ssr_subsystem_list ) ;
static DEFINE_MUTEX ( qcom_ssr_subsys_lock ) ;
2017-01-27 07:04:54 -08:00
2018-06-26 07:11:58 -05:00
static int glink_subdev_start ( struct rproc_subdev * subdev )
2017-08-29 16:13:35 -07:00
{
struct qcom_rproc_glink * glink = to_glink_subdev ( subdev ) ;
glink - > edge = qcom_glink_smem_register ( glink - > dev , glink - > node ) ;
2017-11-28 23:32:40 +01:00
return PTR_ERR_OR_ZERO ( glink - > edge ) ;
2017-08-29 16:13:35 -07:00
}
2018-06-26 07:11:58 -05:00
static void glink_subdev_stop ( struct rproc_subdev * subdev , bool crashed )
2017-08-29 16:13:35 -07:00
{
struct qcom_rproc_glink * glink = to_glink_subdev ( subdev ) ;
qcom_glink_smem_unregister ( glink - > edge ) ;
glink - > edge = NULL ;
}
2020-04-22 17:37:34 -07:00
static void glink_subdev_unprepare ( struct rproc_subdev * subdev )
{
struct qcom_rproc_glink * glink = to_glink_subdev ( subdev ) ;
qcom_glink_ssr_notify ( glink - > ssr_name ) ;
}
2017-08-29 16:13:35 -07:00
/**
* qcom_add_glink_subdev ( ) - try to add a GLINK subdevice to rproc
* @ rproc : rproc handle to parent the subdevice
* @ glink : reference to a GLINK subdev context
2020-04-22 17:37:33 -07:00
* @ ssr_name : identifier of the associated remoteproc for ssr notifications
2017-08-29 16:13:35 -07:00
*/
2020-04-22 17:37:33 -07:00
void qcom_add_glink_subdev ( struct rproc * rproc , struct qcom_rproc_glink * glink ,
const char * ssr_name )
2017-08-29 16:13:35 -07:00
{
struct device * dev = & rproc - > dev ;
glink - > node = of_get_child_by_name ( dev - > parent - > of_node , " glink-edge " ) ;
if ( ! glink - > node )
return ;
2020-04-22 17:37:33 -07:00
glink - > ssr_name = kstrdup_const ( ssr_name , GFP_KERNEL ) ;
if ( ! glink - > ssr_name )
return ;
2017-08-29 16:13:35 -07:00
glink - > dev = dev ;
2018-06-26 07:11:58 -05:00
glink - > subdev . start = glink_subdev_start ;
glink - > subdev . stop = glink_subdev_stop ;
2020-04-22 17:37:34 -07:00
glink - > subdev . unprepare = glink_subdev_unprepare ;
2018-06-26 07:11:57 -05:00
rproc_add_subdev ( rproc , & glink - > subdev ) ;
2017-08-29 16:13:35 -07:00
}
EXPORT_SYMBOL_GPL ( qcom_add_glink_subdev ) ;
/**
* qcom_remove_glink_subdev ( ) - remove a GLINK subdevice from rproc
* @ rproc : rproc handle
* @ glink : reference to a GLINK subdev context
*/
void qcom_remove_glink_subdev ( struct rproc * rproc , struct qcom_rproc_glink * glink )
{
2018-04-03 23:45:15 +05:30
if ( ! glink - > node )
return ;
2017-08-29 16:13:35 -07:00
rproc_remove_subdev ( rproc , & glink - > subdev ) ;
2020-04-22 17:37:33 -07:00
kfree_const ( glink - > ssr_name ) ;
2017-08-29 16:13:35 -07:00
of_node_put ( glink - > node ) ;
}
EXPORT_SYMBOL_GPL ( qcom_remove_glink_subdev ) ;
2018-01-05 16:04:20 -08:00
/**
* qcom_register_dump_segments ( ) - register segments for coredump
* @ rproc : remoteproc handle
* @ fw : firmware header
*
* Register all segments of the ELF in the remoteproc coredump segment list
*
* Return : 0 on success , negative errno on failure .
*/
int qcom_register_dump_segments ( struct rproc * rproc ,
const struct firmware * fw )
{
const struct elf32_phdr * phdrs ;
const struct elf32_phdr * phdr ;
const struct elf32_hdr * ehdr ;
int ret ;
int i ;
ehdr = ( struct elf32_hdr * ) fw - > data ;
phdrs = ( struct elf32_phdr * ) ( ehdr + 1 ) ;
for ( i = 0 ; i < ehdr - > e_phnum ; i + + ) {
phdr = & phdrs [ i ] ;
if ( phdr - > p_type ! = PT_LOAD )
continue ;
if ( ( phdr - > p_flags & QCOM_MDT_TYPE_MASK ) = = QCOM_MDT_TYPE_HASH )
continue ;
if ( ! phdr - > p_memsz )
continue ;
ret = rproc_coredump_add_segment ( rproc , phdr - > p_paddr ,
phdr - > p_memsz ) ;
if ( ret )
return ret ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( qcom_register_dump_segments ) ;
2018-06-26 07:11:58 -05:00
static int smd_subdev_start ( struct rproc_subdev * subdev )
2017-01-27 07:04:54 -08:00
{
struct qcom_rproc_subdev * smd = to_smd_subdev ( subdev ) ;
smd - > edge = qcom_smd_register_edge ( smd - > dev , smd - > node ) ;
2017-08-29 19:13:18 +05:30
return PTR_ERR_OR_ZERO ( smd - > edge ) ;
2017-01-27 07:04:54 -08:00
}
2018-06-26 07:11:58 -05:00
static void smd_subdev_stop ( struct rproc_subdev * subdev , bool crashed )
2017-01-27 07:04:54 -08:00
{
struct qcom_rproc_subdev * smd = to_smd_subdev ( subdev ) ;
qcom_smd_unregister_edge ( smd - > edge ) ;
smd - > edge = NULL ;
}
/**
* qcom_add_smd_subdev ( ) - try to add a SMD subdevice to rproc
* @ rproc : rproc handle to parent the subdevice
* @ smd : reference to a Qualcomm subdev context
*/
void qcom_add_smd_subdev ( struct rproc * rproc , struct qcom_rproc_subdev * smd )
{
struct device * dev = & rproc - > dev ;
smd - > node = of_get_child_by_name ( dev - > parent - > of_node , " smd-edge " ) ;
if ( ! smd - > node )
return ;
smd - > dev = dev ;
2018-06-26 07:11:58 -05:00
smd - > subdev . start = smd_subdev_start ;
smd - > subdev . stop = smd_subdev_stop ;
2018-06-26 07:11:57 -05:00
rproc_add_subdev ( rproc , & smd - > subdev ) ;
2017-01-27 07:04:54 -08:00
}
EXPORT_SYMBOL_GPL ( qcom_add_smd_subdev ) ;
/**
* qcom_remove_smd_subdev ( ) - remove the smd subdevice from rproc
* @ rproc : rproc handle
* @ smd : the SMD subdevice to remove
*/
void qcom_remove_smd_subdev ( struct rproc * rproc , struct qcom_rproc_subdev * smd )
{
2018-04-03 23:45:15 +05:30
if ( ! smd - > node )
return ;
2017-01-27 07:04:54 -08:00
rproc_remove_subdev ( rproc , & smd - > subdev ) ;
of_node_put ( smd - > node ) ;
}
EXPORT_SYMBOL_GPL ( qcom_remove_smd_subdev ) ;
2020-06-23 19:23:27 -07:00
static struct qcom_ssr_subsystem * qcom_ssr_get_subsys ( const char * name )
{
struct qcom_ssr_subsystem * info ;
mutex_lock ( & qcom_ssr_subsys_lock ) ;
/* Match in the global qcom_ssr_subsystem_list with name */
list_for_each_entry ( info , & qcom_ssr_subsystem_list , list )
if ( ! strcmp ( info - > name , name ) )
goto out ;
info = kzalloc ( sizeof ( * info ) , GFP_KERNEL ) ;
if ( ! info ) {
info = ERR_PTR ( - ENOMEM ) ;
goto out ;
}
info - > name = kstrdup_const ( name , GFP_KERNEL ) ;
srcu_init_notifier_head ( & info - > notifier_list ) ;
/* Add to global notification list */
list_add_tail ( & info - > list , & qcom_ssr_subsystem_list ) ;
out :
mutex_unlock ( & qcom_ssr_subsys_lock ) ;
return info ;
}
2017-07-24 22:56:43 -07:00
/**
* qcom_register_ssr_notifier ( ) - register SSR notification handler
2020-06-23 19:23:27 -07:00
* @ name : Subsystem ' s SSR name
* @ nb : notifier_block to be invoked upon subsystem ' s state change
2017-07-24 22:56:43 -07:00
*
2020-06-23 19:23:27 -07:00
* This registers the @ nb notifier block as part the notifier chain for a
* remoteproc associated with @ name . The notifier block ' s callback
* will be invoked when the remote processor ' s SSR events occur
* ( pre / post startup and pre / post shutdown ) .
2017-07-24 22:56:43 -07:00
*
2020-06-23 19:23:27 -07:00
* Return : a subsystem cookie on success , ERR_PTR on failure .
2017-07-24 22:56:43 -07:00
*/
2020-06-23 19:23:27 -07:00
void * qcom_register_ssr_notifier ( const char * name , struct notifier_block * nb )
2017-07-24 22:56:43 -07:00
{
2020-06-23 19:23:27 -07:00
struct qcom_ssr_subsystem * info ;
info = qcom_ssr_get_subsys ( name ) ;
if ( IS_ERR ( info ) )
return info ;
srcu_notifier_chain_register ( & info - > notifier_list , nb ) ;
return & info - > notifier_list ;
2017-07-24 22:56:43 -07:00
}
EXPORT_SYMBOL_GPL ( qcom_register_ssr_notifier ) ;
/**
* qcom_unregister_ssr_notifier ( ) - unregister SSR notification handler
2020-06-23 19:23:27 -07:00
* @ notify : subsystem cookie returned from qcom_register_ssr_notifier
2017-07-24 22:56:43 -07:00
* @ nb : notifier_block to unregister
2020-06-23 19:23:27 -07:00
*
* This function will unregister the notifier from the particular notifier
* chain .
*
* Return : 0 on success , % ENOENT otherwise .
2017-07-24 22:56:43 -07:00
*/
2020-06-23 19:23:27 -07:00
int qcom_unregister_ssr_notifier ( void * notify , struct notifier_block * nb )
2017-07-24 22:56:43 -07:00
{
2020-06-23 19:23:27 -07:00
return srcu_notifier_chain_unregister ( notify , nb ) ;
2017-07-24 22:56:43 -07:00
}
EXPORT_SYMBOL_GPL ( qcom_unregister_ssr_notifier ) ;
2020-06-23 19:23:28 -07:00
static int ssr_notify_prepare ( struct rproc_subdev * subdev )
{
struct qcom_rproc_ssr * ssr = to_ssr_subdev ( subdev ) ;
struct qcom_ssr_notify_data data = {
. name = ssr - > info - > name ,
. crashed = false ,
} ;
srcu_notifier_call_chain ( & ssr - > info - > notifier_list ,
QCOM_SSR_BEFORE_POWERUP , & data ) ;
return 0 ;
}
static int ssr_notify_start ( struct rproc_subdev * subdev )
{
struct qcom_rproc_ssr * ssr = to_ssr_subdev ( subdev ) ;
struct qcom_ssr_notify_data data = {
. name = ssr - > info - > name ,
. crashed = false ,
} ;
srcu_notifier_call_chain ( & ssr - > info - > notifier_list ,
QCOM_SSR_AFTER_POWERUP , & data ) ;
return 0 ;
}
static void ssr_notify_stop ( struct rproc_subdev * subdev , bool crashed )
{
struct qcom_rproc_ssr * ssr = to_ssr_subdev ( subdev ) ;
struct qcom_ssr_notify_data data = {
. name = ssr - > info - > name ,
. crashed = crashed ,
} ;
srcu_notifier_call_chain ( & ssr - > info - > notifier_list ,
QCOM_SSR_BEFORE_SHUTDOWN , & data ) ;
}
2019-07-15 22:03:27 -07:00
static void ssr_notify_unprepare ( struct rproc_subdev * subdev )
2017-07-24 22:56:43 -07:00
{
struct qcom_rproc_ssr * ssr = to_ssr_subdev ( subdev ) ;
2020-06-23 19:23:27 -07:00
struct qcom_ssr_notify_data data = {
. name = ssr - > info - > name ,
. crashed = false ,
} ;
2017-07-24 22:56:43 -07:00
2020-06-23 19:23:28 -07:00
srcu_notifier_call_chain ( & ssr - > info - > notifier_list ,
QCOM_SSR_AFTER_SHUTDOWN , & data ) ;
2017-07-24 22:56:43 -07:00
}
/**
* qcom_add_ssr_subdev ( ) - register subdevice as restart notification source
* @ rproc : rproc handle
* @ ssr : SSR subdevice handle
* @ ssr_name : identifier to use for notifications originating from @ rproc
*
* As the @ ssr is registered with the @ rproc SSR events will be sent to all
2020-06-23 19:23:27 -07:00
* registered listeners for the remoteproc when it ' s SSR events occur
* ( pre / post startup and pre / post shutdown ) .
2017-07-24 22:56:43 -07:00
*/
void qcom_add_ssr_subdev ( struct rproc * rproc , struct qcom_rproc_ssr * ssr ,
const char * ssr_name )
{
2020-06-23 19:23:27 -07:00
struct qcom_ssr_subsystem * info ;
info = qcom_ssr_get_subsys ( ssr_name ) ;
if ( IS_ERR ( info ) ) {
dev_err ( & rproc - > dev , " Failed to add ssr subdevice \n " ) ;
return ;
}
ssr - > info = info ;
2020-06-23 19:23:28 -07:00
ssr - > subdev . prepare = ssr_notify_prepare ;
ssr - > subdev . start = ssr_notify_start ;
ssr - > subdev . stop = ssr_notify_stop ;
2019-07-15 22:03:27 -07:00
ssr - > subdev . unprepare = ssr_notify_unprepare ;
2017-07-24 22:56:43 -07:00
2018-06-26 07:11:57 -05:00
rproc_add_subdev ( rproc , & ssr - > subdev ) ;
2017-07-24 22:56:43 -07:00
}
EXPORT_SYMBOL_GPL ( qcom_add_ssr_subdev ) ;
/**
* qcom_remove_ssr_subdev ( ) - remove subdevice as restart notification source
* @ rproc : rproc handle
* @ ssr : SSR subdevice handle
*/
void qcom_remove_ssr_subdev ( struct rproc * rproc , struct qcom_rproc_ssr * ssr )
{
rproc_remove_subdev ( rproc , & ssr - > subdev ) ;
2020-06-23 19:23:27 -07:00
ssr - > info = NULL ;
2017-07-24 22:56:43 -07:00
}
EXPORT_SYMBOL_GPL ( qcom_remove_ssr_subdev ) ;
2017-01-27 02:28:32 -08:00
MODULE_DESCRIPTION ( " Qualcomm Remoteproc helper driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;