2016-08-23 08:57:44 +03:00
/*
2017-01-30 18:03:08 +03:00
* Qualcomm ADSP / SLPI Peripheral Image Loader for MSM8974 and MSM8996
2016-08-23 08:57:44 +03:00
*
* Copyright ( C ) 2016 Linaro Ltd
* Copyright ( C ) 2014 Sony Mobile Communications AB
* Copyright ( c ) 2012 - 2013 , The Linux Foundation . All rights reserved .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
2016-10-25 23:57:26 +03:00
# include <linux/clk.h>
2016-08-23 08:57:44 +03:00
# include <linux/firmware.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of_address.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <linux/qcom_scm.h>
# include <linux/regulator/consumer.h>
# include <linux/remoteproc.h>
2017-01-27 14:12:57 +03:00
# include <linux/soc/qcom/mdt_loader.h>
2016-08-23 08:57:44 +03:00
# include <linux/soc/qcom/smem.h>
# include <linux/soc/qcom/smem_state.h>
2017-01-27 13:28:32 +03:00
# include "qcom_common.h"
2016-08-23 08:57:44 +03:00
# include "remoteproc_internal.h"
2017-01-30 18:03:06 +03:00
struct adsp_data {
int crash_reason_smem ;
const char * firmware_name ;
int pas_id ;
2017-01-30 18:03:07 +03:00
bool has_aggre2_clk ;
2017-01-30 18:03:06 +03:00
} ;
2016-08-23 08:57:44 +03:00
struct qcom_adsp {
struct device * dev ;
struct rproc * rproc ;
int wdog_irq ;
int fatal_irq ;
int ready_irq ;
int handover_irq ;
int stop_ack_irq ;
struct qcom_smem_state * state ;
unsigned stop_bit ;
2016-10-25 23:57:26 +03:00
struct clk * xo ;
2017-01-30 18:03:07 +03:00
struct clk * aggre2_clk ;
2016-10-25 23:57:26 +03:00
2016-08-23 08:57:44 +03:00
struct regulator * cx_supply ;
2017-01-30 18:03:07 +03:00
struct regulator * px_supply ;
2016-08-23 08:57:44 +03:00
2017-01-30 18:03:06 +03:00
int pas_id ;
int crash_reason_smem ;
2017-01-30 18:03:07 +03:00
bool has_aggre2_clk ;
2017-01-30 18:03:06 +03:00
2016-08-23 08:57:44 +03:00
struct completion start_done ;
struct completion stop_done ;
phys_addr_t mem_phys ;
phys_addr_t mem_reloc ;
void * mem_region ;
size_t mem_size ;
2017-01-30 01:05:50 +03:00
struct qcom_rproc_subdev smd_subdev ;
2016-08-23 08:57:44 +03:00
} ;
static int adsp_load ( struct rproc * rproc , const struct firmware * fw )
{
struct qcom_adsp * adsp = ( struct qcom_adsp * ) rproc - > priv ;
2017-01-27 13:17:23 +03:00
return qcom_mdt_load ( adsp - > dev , fw , rproc - > firmware , adsp - > pas_id ,
adsp - > mem_region , adsp - > mem_phys , adsp - > mem_size ) ;
2016-08-23 08:57:44 +03:00
}
static const struct rproc_fw_ops adsp_fw_ops = {
. find_rsc_table = qcom_mdt_find_rsc_table ,
. load = adsp_load ,
} ;
static int adsp_start ( struct rproc * rproc )
{
struct qcom_adsp * adsp = ( struct qcom_adsp * ) rproc - > priv ;
int ret ;
2016-10-25 23:57:26 +03:00
ret = clk_prepare_enable ( adsp - > xo ) ;
2016-08-23 08:57:44 +03:00
if ( ret )
return ret ;
2017-01-30 18:03:07 +03:00
ret = clk_prepare_enable ( adsp - > aggre2_clk ) ;
if ( ret )
goto disable_xo_clk ;
2016-10-25 23:57:26 +03:00
ret = regulator_enable ( adsp - > cx_supply ) ;
if ( ret )
2017-01-30 18:03:07 +03:00
goto disable_aggre2_clk ;
ret = regulator_enable ( adsp - > px_supply ) ;
if ( ret )
goto disable_cx_supply ;
2016-10-25 23:57:26 +03:00
2017-01-30 18:03:06 +03:00
ret = qcom_scm_pas_auth_and_reset ( adsp - > pas_id ) ;
2016-08-23 08:57:44 +03:00
if ( ret ) {
dev_err ( adsp - > dev ,
" failed to authenticate image and release reset \n " ) ;
2017-01-30 18:03:07 +03:00
goto disable_px_supply ;
2016-08-23 08:57:44 +03:00
}
ret = wait_for_completion_timeout ( & adsp - > start_done ,
msecs_to_jiffies ( 5000 ) ) ;
if ( ! ret ) {
dev_err ( adsp - > dev , " start timed out \n " ) ;
2017-01-30 18:03:06 +03:00
qcom_scm_pas_shutdown ( adsp - > pas_id ) ;
2016-08-23 08:57:44 +03:00
ret = - ETIMEDOUT ;
2017-01-30 18:03:07 +03:00
goto disable_px_supply ;
2016-08-23 08:57:44 +03:00
}
ret = 0 ;
2017-01-30 18:03:07 +03:00
disable_px_supply :
regulator_disable ( adsp - > px_supply ) ;
disable_cx_supply :
2016-08-23 08:57:44 +03:00
regulator_disable ( adsp - > cx_supply ) ;
2017-01-30 18:03:07 +03:00
disable_aggre2_clk :
clk_disable_unprepare ( adsp - > aggre2_clk ) ;
disable_xo_clk :
2016-10-25 23:57:26 +03:00
clk_disable_unprepare ( adsp - > xo ) ;
2016-08-23 08:57:44 +03:00
return ret ;
}
static int adsp_stop ( struct rproc * rproc )
{
struct qcom_adsp * adsp = ( struct qcom_adsp * ) rproc - > priv ;
int ret ;
qcom_smem_state_update_bits ( adsp - > state ,
BIT ( adsp - > stop_bit ) ,
BIT ( adsp - > stop_bit ) ) ;
ret = wait_for_completion_timeout ( & adsp - > stop_done ,
msecs_to_jiffies ( 5000 ) ) ;
if ( ret = = 0 )
dev_err ( adsp - > dev , " timed out on wait \n " ) ;
qcom_smem_state_update_bits ( adsp - > state ,
BIT ( adsp - > stop_bit ) ,
0 ) ;
2017-01-30 18:03:06 +03:00
ret = qcom_scm_pas_shutdown ( adsp - > pas_id ) ;
2016-08-23 08:57:44 +03:00
if ( ret )
dev_err ( adsp - > dev , " failed to shutdown: %d \n " , ret ) ;
return ret ;
}
static void * adsp_da_to_va ( struct rproc * rproc , u64 da , int len )
{
struct qcom_adsp * adsp = ( struct qcom_adsp * ) rproc - > priv ;
int offset ;
offset = da - adsp - > mem_reloc ;
if ( offset < 0 | | offset + len > adsp - > mem_size )
return NULL ;
return adsp - > mem_region + offset ;
}
static const struct rproc_ops adsp_ops = {
. start = adsp_start ,
. stop = adsp_stop ,
. da_to_va = adsp_da_to_va ,
} ;
static irqreturn_t adsp_wdog_interrupt ( int irq , void * dev )
{
struct qcom_adsp * adsp = dev ;
rproc_report_crash ( adsp - > rproc , RPROC_WATCHDOG ) ;
return IRQ_HANDLED ;
}
static irqreturn_t adsp_fatal_interrupt ( int irq , void * dev )
{
struct qcom_adsp * adsp = dev ;
size_t len ;
char * msg ;
2017-01-30 18:03:06 +03:00
msg = qcom_smem_get ( QCOM_SMEM_HOST_ANY , adsp - > crash_reason_smem , & len ) ;
2016-08-23 08:57:44 +03:00
if ( ! IS_ERR ( msg ) & & len > 0 & & msg [ 0 ] )
dev_err ( adsp - > dev , " fatal error received: %s \n " , msg ) ;
rproc_report_crash ( adsp - > rproc , RPROC_FATAL_ERROR ) ;
if ( ! IS_ERR ( msg ) )
msg [ 0 ] = ' \0 ' ;
return IRQ_HANDLED ;
}
static irqreturn_t adsp_ready_interrupt ( int irq , void * dev )
{
return IRQ_HANDLED ;
}
static irqreturn_t adsp_handover_interrupt ( int irq , void * dev )
{
struct qcom_adsp * adsp = dev ;
complete ( & adsp - > start_done ) ;
return IRQ_HANDLED ;
}
static irqreturn_t adsp_stop_ack_interrupt ( int irq , void * dev )
{
struct qcom_adsp * adsp = dev ;
complete ( & adsp - > stop_done ) ;
return IRQ_HANDLED ;
}
2016-10-25 23:57:26 +03:00
static int adsp_init_clock ( struct qcom_adsp * adsp )
{
int ret ;
adsp - > xo = devm_clk_get ( adsp - > dev , " xo " ) ;
if ( IS_ERR ( adsp - > xo ) ) {
ret = PTR_ERR ( adsp - > xo ) ;
if ( ret ! = - EPROBE_DEFER )
dev_err ( adsp - > dev , " failed to get xo clock " ) ;
return ret ;
}
2017-01-30 18:03:07 +03:00
if ( adsp - > has_aggre2_clk ) {
adsp - > aggre2_clk = devm_clk_get ( adsp - > dev , " aggre2 " ) ;
if ( IS_ERR ( adsp - > aggre2_clk ) ) {
ret = PTR_ERR ( adsp - > aggre2_clk ) ;
if ( ret ! = - EPROBE_DEFER )
dev_err ( adsp - > dev ,
" failed to get aggre2 clock " ) ;
return ret ;
}
}
2016-10-25 23:57:26 +03:00
return 0 ;
}
2016-08-23 08:57:44 +03:00
static int adsp_init_regulator ( struct qcom_adsp * adsp )
{
adsp - > cx_supply = devm_regulator_get ( adsp - > dev , " cx " ) ;
if ( IS_ERR ( adsp - > cx_supply ) )
return PTR_ERR ( adsp - > cx_supply ) ;
regulator_set_load ( adsp - > cx_supply , 100000 ) ;
2017-01-30 18:03:07 +03:00
adsp - > px_supply = devm_regulator_get ( adsp - > dev , " px " ) ;
if ( IS_ERR ( adsp - > px_supply ) )
return PTR_ERR ( adsp - > px_supply ) ;
2016-08-23 08:57:44 +03:00
return 0 ;
}
static int adsp_request_irq ( struct qcom_adsp * adsp ,
struct platform_device * pdev ,
const char * name ,
irq_handler_t thread_fn )
{
int ret ;
ret = platform_get_irq_byname ( pdev , name ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " no %s IRQ defined \n " , name ) ;
return ret ;
}
ret = devm_request_threaded_irq ( & pdev - > dev , ret ,
NULL , thread_fn ,
IRQF_ONESHOT ,
" adsp " , adsp ) ;
if ( ret )
dev_err ( & pdev - > dev , " request %s IRQ failed \n " , name ) ;
return ret ;
}
static int adsp_alloc_memory_region ( struct qcom_adsp * adsp )
{
struct device_node * node ;
struct resource r ;
int ret ;
node = of_parse_phandle ( adsp - > dev - > of_node , " memory-region " , 0 ) ;
if ( ! node ) {
dev_err ( adsp - > dev , " no memory-region specified \n " ) ;
return - EINVAL ;
}
ret = of_address_to_resource ( node , 0 , & r ) ;
if ( ret )
return ret ;
adsp - > mem_phys = adsp - > mem_reloc = r . start ;
adsp - > mem_size = resource_size ( & r ) ;
adsp - > mem_region = devm_ioremap_wc ( adsp - > dev , adsp - > mem_phys , adsp - > mem_size ) ;
if ( ! adsp - > mem_region ) {
dev_err ( adsp - > dev , " unable to map memory region: %pa+%zx \n " ,
& r . start , adsp - > mem_size ) ;
return - EBUSY ;
}
return 0 ;
}
static int adsp_probe ( struct platform_device * pdev )
{
2017-01-30 18:03:06 +03:00
const struct adsp_data * desc ;
2016-08-23 08:57:44 +03:00
struct qcom_adsp * adsp ;
struct rproc * rproc ;
int ret ;
2017-01-30 18:03:06 +03:00
desc = of_device_get_match_data ( & pdev - > dev ) ;
if ( ! desc )
return - EINVAL ;
2016-08-23 08:57:44 +03:00
if ( ! qcom_scm_is_available ( ) )
return - EPROBE_DEFER ;
rproc = rproc_alloc ( & pdev - > dev , pdev - > name , & adsp_ops ,
2017-01-30 18:03:06 +03:00
desc - > firmware_name , sizeof ( * adsp ) ) ;
2016-08-23 08:57:44 +03:00
if ( ! rproc ) {
dev_err ( & pdev - > dev , " unable to allocate remoteproc \n " ) ;
return - ENOMEM ;
}
rproc - > fw_ops = & adsp_fw_ops ;
adsp = ( struct qcom_adsp * ) rproc - > priv ;
adsp - > dev = & pdev - > dev ;
adsp - > rproc = rproc ;
2017-01-30 18:03:06 +03:00
adsp - > pas_id = desc - > pas_id ;
adsp - > crash_reason_smem = desc - > crash_reason_smem ;
2017-01-30 18:03:07 +03:00
adsp - > has_aggre2_clk = desc - > has_aggre2_clk ;
2016-08-23 08:57:44 +03:00
platform_set_drvdata ( pdev , adsp ) ;
init_completion ( & adsp - > start_done ) ;
init_completion ( & adsp - > stop_done ) ;
ret = adsp_alloc_memory_region ( adsp ) ;
if ( ret )
goto free_rproc ;
2016-10-25 23:57:26 +03:00
ret = adsp_init_clock ( adsp ) ;
if ( ret )
goto free_rproc ;
2016-08-23 08:57:44 +03:00
ret = adsp_init_regulator ( adsp ) ;
if ( ret )
goto free_rproc ;
ret = adsp_request_irq ( adsp , pdev , " wdog " , adsp_wdog_interrupt ) ;
if ( ret < 0 )
goto free_rproc ;
adsp - > wdog_irq = ret ;
ret = adsp_request_irq ( adsp , pdev , " fatal " , adsp_fatal_interrupt ) ;
if ( ret < 0 )
goto free_rproc ;
adsp - > fatal_irq = ret ;
ret = adsp_request_irq ( adsp , pdev , " ready " , adsp_ready_interrupt ) ;
if ( ret < 0 )
goto free_rproc ;
adsp - > ready_irq = ret ;
ret = adsp_request_irq ( adsp , pdev , " handover " , adsp_handover_interrupt ) ;
if ( ret < 0 )
goto free_rproc ;
adsp - > handover_irq = ret ;
ret = adsp_request_irq ( adsp , pdev , " stop-ack " , adsp_stop_ack_interrupt ) ;
if ( ret < 0 )
goto free_rproc ;
adsp - > stop_ack_irq = ret ;
adsp - > state = qcom_smem_state_get ( & pdev - > dev , " stop " ,
& adsp - > stop_bit ) ;
if ( IS_ERR ( adsp - > state ) ) {
ret = PTR_ERR ( adsp - > state ) ;
goto free_rproc ;
}
2017-01-30 01:05:50 +03:00
qcom_add_smd_subdev ( rproc , & adsp - > smd_subdev ) ;
2016-08-23 08:57:44 +03:00
ret = rproc_add ( rproc ) ;
if ( ret )
goto free_rproc ;
return 0 ;
free_rproc :
2016-11-20 09:42:55 +03:00
rproc_free ( rproc ) ;
2016-08-23 08:57:44 +03:00
return ret ;
}
static int adsp_remove ( struct platform_device * pdev )
{
struct qcom_adsp * adsp = platform_get_drvdata ( pdev ) ;
qcom_smem_state_put ( adsp - > state ) ;
rproc_del ( adsp - > rproc ) ;
2017-01-30 01:05:50 +03:00
qcom_remove_smd_subdev ( adsp - > rproc , & adsp - > smd_subdev ) ;
2016-11-20 09:42:55 +03:00
rproc_free ( adsp - > rproc ) ;
2016-08-23 08:57:44 +03:00
return 0 ;
}
2017-01-30 18:03:06 +03:00
static const struct adsp_data adsp_resource_init = {
. crash_reason_smem = 423 ,
. firmware_name = " adsp.mdt " ,
. pas_id = 1 ,
2017-01-30 18:03:07 +03:00
. has_aggre2_clk = false ,
2017-01-30 18:03:06 +03:00
} ;
2017-01-30 18:03:08 +03:00
static const struct adsp_data slpi_resource_init = {
. crash_reason_smem = 424 ,
. firmware_name = " slpi.mdt " ,
. pas_id = 12 ,
. has_aggre2_clk = true ,
} ;
2016-08-23 08:57:44 +03:00
static const struct of_device_id adsp_of_match [ ] = {
2017-01-30 18:03:06 +03:00
{ . compatible = " qcom,msm8974-adsp-pil " , . data = & adsp_resource_init } ,
{ . compatible = " qcom,msm8996-adsp-pil " , . data = & adsp_resource_init } ,
2017-01-30 18:03:08 +03:00
{ . compatible = " qcom,msm8996-slpi-pil " , . data = & slpi_resource_init } ,
2016-08-23 08:57:44 +03:00
{ } ,
} ;
2016-11-20 09:41:56 +03:00
MODULE_DEVICE_TABLE ( of , adsp_of_match ) ;
2016-08-23 08:57:44 +03:00
static struct platform_driver adsp_driver = {
. probe = adsp_probe ,
. remove = adsp_remove ,
. driver = {
. name = " qcom_adsp_pil " ,
. of_match_table = adsp_of_match ,
} ,
} ;
module_platform_driver ( adsp_driver ) ;
MODULE_DESCRIPTION ( " Qualcomm MSM8974/MSM8996 ADSP Peripherial Image Loader " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;