2018-06-04 23:30:36 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Qualcomm Peripheral Image Loader for Q6V5
*
* Copyright ( C ) 2016 - 2018 Linaro Ltd .
* Copyright ( C ) 2014 Sony Mobile Communications AB
* Copyright ( c ) 2012 - 2013 , The Linux Foundation . All rights reserved .
*/
# include <linux/kernel.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
2018-07-06 15:57:53 +03:00
# include <linux/module.h>
2018-06-04 23:30:36 +03:00
# include <linux/soc/qcom/smem.h>
# include <linux/soc/qcom/smem_state.h>
# include <linux/remoteproc.h>
# include "qcom_q6v5.h"
2020-03-24 08:29:03 +03:00
# define Q6V5_PANIC_DELAY_MS 200
2018-06-04 23:30:36 +03:00
/**
* qcom_q6v5_prepare ( ) - reinitialize the qcom_q6v5 context before start
* @ q6v5 : reference to qcom_q6v5 context to be reinitialized
*
* Return : 0 on success , negative errno on failure
*/
int qcom_q6v5_prepare ( struct qcom_q6v5 * q6v5 )
{
reinit_completion ( & q6v5 - > start_done ) ;
reinit_completion ( & q6v5 - > stop_done ) ;
q6v5 - > running = true ;
q6v5 - > handover_issued = false ;
enable_irq ( q6v5 - > handover_irq ) ;
return 0 ;
}
2018-07-06 15:57:53 +03:00
EXPORT_SYMBOL_GPL ( qcom_q6v5_prepare ) ;
2018-06-04 23:30:36 +03:00
/**
* qcom_q6v5_unprepare ( ) - unprepare the qcom_q6v5 context after stop
* @ q6v5 : reference to qcom_q6v5 context to be unprepared
*
* Return : 0 on success , 1 if handover hasn ' t yet been called
*/
int qcom_q6v5_unprepare ( struct qcom_q6v5 * q6v5 )
{
disable_irq ( q6v5 - > handover_irq ) ;
return ! q6v5 - > handover_issued ;
}
2018-07-06 15:57:53 +03:00
EXPORT_SYMBOL_GPL ( qcom_q6v5_unprepare ) ;
2018-06-04 23:30:36 +03:00
static irqreturn_t q6v5_wdog_interrupt ( int irq , void * data )
{
struct qcom_q6v5 * q6v5 = data ;
size_t len ;
char * msg ;
/* Sometimes the stop triggers a watchdog rather than a stop-ack */
if ( ! q6v5 - > running ) {
complete ( & q6v5 - > stop_done ) ;
return IRQ_HANDLED ;
}
msg = qcom_smem_get ( QCOM_SMEM_HOST_ANY , q6v5 - > crash_reason , & len ) ;
if ( ! IS_ERR ( msg ) & & len > 0 & & msg [ 0 ] )
dev_err ( q6v5 - > dev , " watchdog received: %s \n " , msg ) ;
else
dev_err ( q6v5 - > dev , " watchdog without message \n " ) ;
rproc_report_crash ( q6v5 - > rproc , RPROC_WATCHDOG ) ;
return IRQ_HANDLED ;
}
static irqreturn_t q6v5_fatal_interrupt ( int irq , void * data )
{
struct qcom_q6v5 * q6v5 = data ;
size_t len ;
char * msg ;
msg = qcom_smem_get ( QCOM_SMEM_HOST_ANY , q6v5 - > crash_reason , & len ) ;
if ( ! IS_ERR ( msg ) & & len > 0 & & msg [ 0 ] )
dev_err ( q6v5 - > dev , " fatal error received: %s \n " , msg ) ;
else
dev_err ( q6v5 - > dev , " fatal error without message \n " ) ;
2018-10-01 17:25:00 +03:00
q6v5 - > running = false ;
2018-06-04 23:30:36 +03:00
rproc_report_crash ( q6v5 - > rproc , RPROC_FATAL_ERROR ) ;
return IRQ_HANDLED ;
}
static irqreturn_t q6v5_ready_interrupt ( int irq , void * data )
{
struct qcom_q6v5 * q6v5 = data ;
complete ( & q6v5 - > start_done ) ;
return IRQ_HANDLED ;
}
/**
* qcom_q6v5_wait_for_start ( ) - wait for remote processor start signal
* @ q6v5 : reference to qcom_q6v5 context
* @ timeout : timeout to wait for the event , in jiffies
*
* qcom_q6v5_unprepare ( ) should not be called when this function fails .
*
* Return : 0 on success , - ETIMEDOUT on timeout
*/
int qcom_q6v5_wait_for_start ( struct qcom_q6v5 * q6v5 , int timeout )
{
int ret ;
ret = wait_for_completion_timeout ( & q6v5 - > start_done , timeout ) ;
if ( ! ret )
disable_irq ( q6v5 - > handover_irq ) ;
return ! ret ? - ETIMEDOUT : 0 ;
}
2018-07-06 15:57:53 +03:00
EXPORT_SYMBOL_GPL ( qcom_q6v5_wait_for_start ) ;
2018-06-04 23:30:36 +03:00
static irqreturn_t q6v5_handover_interrupt ( int irq , void * data )
{
struct qcom_q6v5 * q6v5 = data ;
if ( q6v5 - > handover )
q6v5 - > handover ( q6v5 ) ;
q6v5 - > handover_issued = true ;
return IRQ_HANDLED ;
}
static irqreturn_t q6v5_stop_interrupt ( int irq , void * data )
{
struct qcom_q6v5 * q6v5 = data ;
complete ( & q6v5 - > stop_done ) ;
return IRQ_HANDLED ;
}
/**
* qcom_q6v5_request_stop ( ) - request the remote processor to stop
* @ q6v5 : reference to qcom_q6v5 context
*
* Return : 0 on success , negative errno on failure
*/
int qcom_q6v5_request_stop ( struct qcom_q6v5 * q6v5 )
{
int ret ;
qcom_smem_state_update_bits ( q6v5 - > state ,
BIT ( q6v5 - > stop_bit ) , BIT ( q6v5 - > stop_bit ) ) ;
ret = wait_for_completion_timeout ( & q6v5 - > stop_done , 5 * HZ ) ;
qcom_smem_state_update_bits ( q6v5 - > state , BIT ( q6v5 - > stop_bit ) , 0 ) ;
return ret = = 0 ? - ETIMEDOUT : 0 ;
}
2018-07-06 15:57:53 +03:00
EXPORT_SYMBOL_GPL ( qcom_q6v5_request_stop ) ;
2018-06-04 23:30:36 +03:00
2020-03-24 08:29:03 +03:00
/**
* qcom_q6v5_panic ( ) - panic handler to invoke a stop on the remote
* @ q6v5 : reference to qcom_q6v5 context
*
* Set the stop bit and sleep in order to allow the remote processor to flush
* its caches etc for post mortem debugging .
*
* Return : 200 ms
*/
unsigned long qcom_q6v5_panic ( struct qcom_q6v5 * q6v5 )
{
qcom_smem_state_update_bits ( q6v5 - > state ,
BIT ( q6v5 - > stop_bit ) , BIT ( q6v5 - > stop_bit ) ) ;
return Q6V5_PANIC_DELAY_MS ;
}
EXPORT_SYMBOL_GPL ( qcom_q6v5_panic ) ;
2018-06-04 23:30:36 +03:00
/**
* qcom_q6v5_init ( ) - initializer of the q6v5 common struct
* @ q6v5 : handle to be initialized
* @ pdev : platform_device reference for acquiring resources
* @ rproc : associated remoteproc instance
* @ crash_reason : SMEM id for crash reason string , or 0 if none
* @ handover : function to be called when proxy resources should be released
*
* Return : 0 on success , negative errno on failure
*/
int qcom_q6v5_init ( struct qcom_q6v5 * q6v5 , struct platform_device * pdev ,
struct rproc * rproc , int crash_reason ,
void ( * handover ) ( struct qcom_q6v5 * q6v5 ) )
{
int ret ;
q6v5 - > rproc = rproc ;
q6v5 - > dev = & pdev - > dev ;
q6v5 - > crash_reason = crash_reason ;
q6v5 - > handover = handover ;
init_completion ( & q6v5 - > start_done ) ;
init_completion ( & q6v5 - > stop_done ) ;
q6v5 - > wdog_irq = platform_get_irq_byname ( pdev , " wdog " ) ;
2019-07-30 21:15:38 +03:00
if ( q6v5 - > wdog_irq < 0 )
2018-10-10 01:25:27 +03:00
return q6v5 - > wdog_irq ;
2018-06-04 23:30:36 +03:00
ret = devm_request_threaded_irq ( & pdev - > dev , q6v5 - > wdog_irq ,
NULL , q6v5_wdog_interrupt ,
IRQF_TRIGGER_RISING | IRQF_ONESHOT ,
" q6v5 wdog " , q6v5 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to acquire wdog IRQ \n " ) ;
return ret ;
}
q6v5 - > fatal_irq = platform_get_irq_byname ( pdev , " fatal " ) ;
2019-07-30 21:15:38 +03:00
if ( q6v5 - > fatal_irq < 0 )
2018-10-10 01:25:27 +03:00
return q6v5 - > fatal_irq ;
2018-09-20 04:51:51 +03:00
2018-06-04 23:30:36 +03:00
ret = devm_request_threaded_irq ( & pdev - > dev , q6v5 - > fatal_irq ,
NULL , q6v5_fatal_interrupt ,
IRQF_TRIGGER_RISING | IRQF_ONESHOT ,
" q6v5 fatal " , q6v5 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to acquire fatal IRQ \n " ) ;
return ret ;
}
q6v5 - > ready_irq = platform_get_irq_byname ( pdev , " ready " ) ;
2019-07-30 21:15:38 +03:00
if ( q6v5 - > ready_irq < 0 )
2018-10-10 01:25:27 +03:00
return q6v5 - > ready_irq ;
2018-09-20 04:51:51 +03:00
2018-06-04 23:30:36 +03:00
ret = devm_request_threaded_irq ( & pdev - > dev , q6v5 - > ready_irq ,
NULL , q6v5_ready_interrupt ,
IRQF_TRIGGER_RISING | IRQF_ONESHOT ,
" q6v5 ready " , q6v5 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to acquire ready IRQ \n " ) ;
return ret ;
}
q6v5 - > handover_irq = platform_get_irq_byname ( pdev , " handover " ) ;
2019-07-30 21:15:38 +03:00
if ( q6v5 - > handover_irq < 0 )
2018-10-10 01:25:27 +03:00
return q6v5 - > handover_irq ;
2018-09-20 04:51:51 +03:00
2018-06-04 23:30:36 +03:00
ret = devm_request_threaded_irq ( & pdev - > dev , q6v5 - > handover_irq ,
NULL , q6v5_handover_interrupt ,
IRQF_TRIGGER_RISING | IRQF_ONESHOT ,
" q6v5 handover " , q6v5 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to acquire handover IRQ \n " ) ;
return ret ;
}
disable_irq ( q6v5 - > handover_irq ) ;
q6v5 - > stop_irq = platform_get_irq_byname ( pdev , " stop-ack " ) ;
2019-07-30 21:15:38 +03:00
if ( q6v5 - > stop_irq < 0 )
2018-10-10 01:25:27 +03:00
return q6v5 - > stop_irq ;
2018-09-20 04:51:51 +03:00
2018-06-04 23:30:36 +03:00
ret = devm_request_threaded_irq ( & pdev - > dev , q6v5 - > stop_irq ,
NULL , q6v5_stop_interrupt ,
IRQF_TRIGGER_RISING | IRQF_ONESHOT ,
" q6v5 stop " , q6v5 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to acquire stop-ack IRQ \n " ) ;
return ret ;
}
q6v5 - > state = qcom_smem_state_get ( & pdev - > dev , " stop " , & q6v5 - > stop_bit ) ;
if ( IS_ERR ( q6v5 - > state ) ) {
dev_err ( & pdev - > dev , " failed to acquire stop state \n " ) ;
return PTR_ERR ( q6v5 - > state ) ;
}
return 0 ;
}
2018-07-06 15:57:53 +03:00
EXPORT_SYMBOL_GPL ( qcom_q6v5_init ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " Qualcomm Peripheral Image Loader for Q6V5 " ) ;