@ -56,6 +56,7 @@
# include <linux/irq.h>
# include <linux/atomic.h>
# include <linux/pm_runtime.h>
# include <linux/msi.h>
# include "../dmaengine.h"
# include "hidma.h"
@ -70,6 +71,7 @@
# define HIDMA_ERR_INFO_SW 0xFF
# define HIDMA_ERR_CODE_UNEXPECTED_TERMINATE 0x0
# define HIDMA_NR_DEFAULT_DESC 10
# define HIDMA_MSI_INTS 11
static inline struct hidma_dev * to_hidma_dev ( struct dma_device * dmadev )
{
@ -553,6 +555,15 @@ static irqreturn_t hidma_chirq_handler(int chirq, void *arg)
return hidma_ll_inthandler ( chirq , lldev ) ;
}
static irqreturn_t hidma_chirq_handler_msi ( int chirq , void * arg )
{
struct hidma_lldev * * lldevp = arg ;
struct hidma_dev * dmadev = to_hidma_dev_from_lldev ( lldevp ) ;
return hidma_ll_inthandler_msi ( chirq , * lldevp ,
1 < < ( chirq - dmadev - > msi_virqbase ) ) ;
}
static ssize_t hidma_show_values ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
@ -590,6 +601,104 @@ static int hidma_create_sysfs_entry(struct hidma_dev *dev, char *name,
return device_create_file ( dev - > ddev . dev , attrs ) ;
}
# ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
static void hidma_write_msi_msg ( struct msi_desc * desc , struct msi_msg * msg )
{
struct device * dev = msi_desc_to_dev ( desc ) ;
struct hidma_dev * dmadev = dev_get_drvdata ( dev ) ;
if ( ! desc - > platform . msi_index ) {
writel ( msg - > address_lo , dmadev - > dev_evca + 0x118 ) ;
writel ( msg - > address_hi , dmadev - > dev_evca + 0x11C ) ;
writel ( msg - > data , dmadev - > dev_evca + 0x120 ) ;
}
}
# endif
static void hidma_free_msis ( struct hidma_dev * dmadev )
{
# ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
struct device * dev = dmadev - > ddev . dev ;
struct msi_desc * desc ;
/* free allocated MSI interrupts above */
for_each_msi_entry ( desc , dev )
devm_free_irq ( dev , desc - > irq , & dmadev - > lldev ) ;
platform_msi_domain_free_irqs ( dev ) ;
# endif
}
static int hidma_request_msi ( struct hidma_dev * dmadev ,
struct platform_device * pdev )
{
# ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
int rc ;
struct msi_desc * desc ;
struct msi_desc * failed_desc = NULL ;
rc = platform_msi_domain_alloc_irqs ( & pdev - > dev , HIDMA_MSI_INTS ,
hidma_write_msi_msg ) ;
if ( rc )
return rc ;
for_each_msi_entry ( desc , & pdev - > dev ) {
if ( ! desc - > platform . msi_index )
dmadev - > msi_virqbase = desc - > irq ;
rc = devm_request_irq ( & pdev - > dev , desc - > irq ,
hidma_chirq_handler_msi ,
0 , " qcom-hidma-msi " ,
& dmadev - > lldev ) ;
if ( rc ) {
failed_desc = desc ;
break ;
}
}
if ( rc ) {
/* free allocated MSI interrupts above */
for_each_msi_entry ( desc , & pdev - > dev ) {
if ( desc = = failed_desc )
break ;
devm_free_irq ( & pdev - > dev , desc - > irq ,
& dmadev - > lldev ) ;
}
} else {
/* Add callback to free MSIs on teardown */
hidma_ll_setup_irq ( dmadev - > lldev , true ) ;
}
if ( rc )
dev_warn ( & pdev - > dev ,
" failed to request MSI irq, falling back to wired IRQ \n " ) ;
return rc ;
# else
return - EINVAL ;
# endif
}
static bool hidma_msi_capable ( struct device * dev )
{
struct acpi_device * adev = ACPI_COMPANION ( dev ) ;
const char * of_compat ;
int ret = - EINVAL ;
if ( ! adev | | acpi_disabled ) {
ret = device_property_read_string ( dev , " compatible " ,
& of_compat ) ;
if ( ret )
return false ;
ret = strcmp ( of_compat , " qcom,hidma-1.1 " ) ;
} else {
# ifdef CONFIG_ACPI
ret = strcmp ( acpi_device_hid ( adev ) , " QCOM8062 " ) ;
# endif
}
return ret = = 0 ;
}
static int hidma_probe ( struct platform_device * pdev )
{
struct hidma_dev * dmadev ;
@ -599,6 +708,7 @@ static int hidma_probe(struct platform_device *pdev)
void __iomem * evca ;
void __iomem * trca ;
int rc ;
bool msi ;
pm_runtime_set_autosuspend_delay ( & pdev - > dev , HIDMA_AUTOSUSPEND_TIMEOUT ) ;
pm_runtime_use_autosuspend ( & pdev - > dev ) ;
@ -660,6 +770,12 @@ static int hidma_probe(struct platform_device *pdev)
dmadev - > ddev . device_terminate_all = hidma_terminate_all ;
dmadev - > ddev . copy_align = 8 ;
/*
* Determine the MSI capability of the platform . Old HW doesn ' t
* support MSI .
*/
msi = hidma_msi_capable ( & pdev - > dev ) ;
device_property_read_u32 ( & pdev - > dev , " desc-count " ,
& dmadev - > nr_descriptors ) ;
@ -688,10 +804,17 @@ static int hidma_probe(struct platform_device *pdev)
goto dmafree ;
}
rc = devm_request_irq ( & pdev - > dev , chirq , hidma_chirq_handler , 0 ,
" qcom-hidma " , dmadev - > lldev ) ;
platform_set_drvdata ( pdev , dmadev ) ;
if ( msi )
rc = hidma_request_msi ( dmadev , pdev ) ;
if ( ! msi | | rc ) {
hidma_ll_setup_irq ( dmadev - > lldev , false ) ;
rc = devm_request_irq ( & pdev - > dev , chirq , hidma_chirq_handler ,
0 , " qcom-hidma " , dmadev - > lldev ) ;
if ( rc )
goto uninit ;
}
INIT_LIST_HEAD ( & dmadev - > ddev . channels ) ;
rc = hidma_chan_init ( dmadev , 0 ) ;
@ -707,12 +830,14 @@ static int hidma_probe(struct platform_device *pdev)
hidma_debug_init ( dmadev ) ;
hidma_create_sysfs_entry ( dmadev , " chid " , S_IRUGO ) ;
dev_info ( & pdev - > dev , " HI-DMA engine driver registration complete \n " ) ;
platform_set_drvdata ( pdev , dmadev ) ;
pm_runtime_mark_last_busy ( dmadev - > ddev . dev ) ;
pm_runtime_put_autosuspend ( dmadev - > ddev . dev ) ;
return 0 ;
uninit :
if ( msi )
hidma_free_msis ( dmadev ) ;
hidma_debug_uninit ( dmadev ) ;
hidma_ll_uninit ( dmadev - > lldev ) ;
dmafree :
@ -730,7 +855,11 @@ static int hidma_remove(struct platform_device *pdev)
pm_runtime_get_sync ( dmadev - > ddev . dev ) ;
dma_async_device_unregister ( & dmadev - > ddev ) ;
if ( ! dmadev - > lldev - > msi_support )
devm_free_irq ( dmadev - > ddev . dev , dmadev - > irq , dmadev - > lldev ) ;
else
hidma_free_msis ( dmadev ) ;
tasklet_kill ( & dmadev - > task ) ;
hidma_debug_uninit ( dmadev ) ;
hidma_ll_uninit ( dmadev - > lldev ) ;
@ -746,12 +875,14 @@ static int hidma_remove(struct platform_device *pdev)
# if IS_ENABLED(CONFIG_ACPI)
static const struct acpi_device_id hidma_acpi_ids [ ] = {
{ " QCOM8061 " } ,
{ " QCOM8062 " } ,
{ } ,
} ;
# endif
static const struct of_device_id hidma_match [ ] = {
{ . compatible = " qcom,hidma-1.0 " , } ,
{ . compatible = " qcom,hidma-1.1 " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , hidma_match ) ;