2020-05-15 13:43:39 +03:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Ingenic JZ47xx remoteproc driver
* Copyright 2019 , Paul Cercueil < paul @ crapouillou . net >
*/
# include <linux/bitops.h>
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/remoteproc.h>
# include "remoteproc_internal.h"
# define REG_AUX_CTRL 0x0
# define REG_AUX_MSG_ACK 0x10
# define REG_AUX_MSG 0x14
# define REG_CORE_MSG_ACK 0x18
# define REG_CORE_MSG 0x1C
# define AUX_CTRL_SLEEP BIT(31)
# define AUX_CTRL_MSG_IRQ_EN BIT(3)
# define AUX_CTRL_NMI_RESETS BIT(2)
# define AUX_CTRL_NMI BIT(1)
# define AUX_CTRL_SW_RESET BIT(0)
2021-01-23 17:29:56 +03:00
static bool auto_boot ;
module_param ( auto_boot , bool , 0400 ) ;
MODULE_PARM_DESC ( auto_boot ,
" Auto-boot the remote processor [default=false] " ) ;
2020-05-15 13:43:39 +03:00
struct vpu_mem_map {
const char * name ;
unsigned int da ;
} ;
struct vpu_mem_info {
const struct vpu_mem_map * map ;
unsigned long len ;
void __iomem * base ;
} ;
static const struct vpu_mem_map vpu_mem_map [ ] = {
{ " tcsm0 " , 0x132b0000 } ,
{ " tcsm1 " , 0xf4000000 } ,
{ " sram " , 0x132f0000 } ,
} ;
/**
* struct vpu - Ingenic VPU remoteproc private structure
* @ irq : interrupt number
* @ clks : pointers to the VPU and AUX clocks
* @ aux_base : raw pointer to the AUX interface registers
* @ mem_info : array of struct vpu_mem_info , which contain the mapping info of
* each of the external memories
* @ dev : private pointer to the device
*/
struct vpu {
int irq ;
struct clk_bulk_data clks [ 2 ] ;
void __iomem * aux_base ;
struct vpu_mem_info mem_info [ ARRAY_SIZE ( vpu_mem_map ) ] ;
struct device * dev ;
} ;
2020-06-30 19:31:17 +03:00
static int ingenic_rproc_prepare ( struct rproc * rproc )
{
struct vpu * vpu = rproc - > priv ;
int ret ;
/* The clocks must be enabled for the firmware to be loaded in TCSM */
ret = clk_bulk_prepare_enable ( ARRAY_SIZE ( vpu - > clks ) , vpu - > clks ) ;
if ( ret )
dev_err ( vpu - > dev , " Unable to start clocks: %d \n " , ret ) ;
return ret ;
}
static int ingenic_rproc_unprepare ( struct rproc * rproc )
{
struct vpu * vpu = rproc - > priv ;
clk_bulk_disable_unprepare ( ARRAY_SIZE ( vpu - > clks ) , vpu - > clks ) ;
return 0 ;
}
2020-05-15 13:43:39 +03:00
static int ingenic_rproc_start ( struct rproc * rproc )
{
struct vpu * vpu = rproc - > priv ;
u32 ctrl ;
enable_irq ( vpu - > irq ) ;
/* Reset the AUX and enable message IRQ */
ctrl = AUX_CTRL_NMI_RESETS | AUX_CTRL_NMI | AUX_CTRL_MSG_IRQ_EN ;
writel ( ctrl , vpu - > aux_base + REG_AUX_CTRL ) ;
return 0 ;
}
static int ingenic_rproc_stop ( struct rproc * rproc )
{
struct vpu * vpu = rproc - > priv ;
disable_irq ( vpu - > irq ) ;
/* Keep AUX in reset mode */
writel ( AUX_CTRL_SW_RESET , vpu - > aux_base + REG_AUX_CTRL ) ;
return 0 ;
}
static void ingenic_rproc_kick ( struct rproc * rproc , int vqid )
{
struct vpu * vpu = rproc - > priv ;
writel ( vqid , vpu - > aux_base + REG_CORE_MSG ) ;
}
2021-03-06 14:24:19 +03:00
static void * ingenic_rproc_da_to_va ( struct rproc * rproc , u64 da , size_t len , bool * is_iomem )
2020-05-15 13:43:39 +03:00
{
struct vpu * vpu = rproc - > priv ;
void __iomem * va = NULL ;
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( vpu_mem_map ) ; i + + ) {
const struct vpu_mem_info * info = & vpu - > mem_info [ i ] ;
const struct vpu_mem_map * map = info - > map ;
if ( da > = map - > da & & ( da + len ) < ( map - > da + info - > len ) ) {
va = info - > base + ( da - map - > da ) ;
break ;
}
}
return ( __force void * ) va ;
}
2020-11-08 02:36:29 +03:00
static const struct rproc_ops ingenic_rproc_ops = {
2020-06-30 19:31:17 +03:00
. prepare = ingenic_rproc_prepare ,
. unprepare = ingenic_rproc_unprepare ,
2020-05-15 13:43:39 +03:00
. start = ingenic_rproc_start ,
. stop = ingenic_rproc_stop ,
. kick = ingenic_rproc_kick ,
. da_to_va = ingenic_rproc_da_to_va ,
} ;
static irqreturn_t vpu_interrupt ( int irq , void * data )
{
struct rproc * rproc = data ;
struct vpu * vpu = rproc - > priv ;
u32 vring ;
vring = readl ( vpu - > aux_base + REG_AUX_MSG ) ;
/* Ack the interrupt */
writel ( 0 , vpu - > aux_base + REG_AUX_MSG_ACK ) ;
return rproc_vq_interrupt ( rproc , vring ) ;
}
static int ingenic_rproc_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct resource * mem ;
struct rproc * rproc ;
struct vpu * vpu ;
unsigned int i ;
int ret ;
rproc = devm_rproc_alloc ( dev , " ingenic-vpu " ,
& ingenic_rproc_ops , NULL , sizeof ( * vpu ) ) ;
if ( ! rproc )
return - ENOMEM ;
2021-01-23 17:29:56 +03:00
rproc - > auto_boot = auto_boot ;
2020-05-15 13:43:39 +03:00
vpu = rproc - > priv ;
vpu - > dev = & pdev - > dev ;
platform_set_drvdata ( pdev , vpu ) ;
mem = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " aux " ) ;
vpu - > aux_base = devm_ioremap_resource ( dev , mem ) ;
if ( IS_ERR ( vpu - > aux_base ) ) {
dev_err ( dev , " Failed to ioremap \n " ) ;
return PTR_ERR ( vpu - > aux_base ) ;
}
for ( i = 0 ; i < ARRAY_SIZE ( vpu_mem_map ) ; i + + ) {
mem = platform_get_resource_byname ( pdev , IORESOURCE_MEM ,
vpu_mem_map [ i ] . name ) ;
vpu - > mem_info [ i ] . base = devm_ioremap_resource ( dev , mem ) ;
if ( IS_ERR ( vpu - > mem_info [ i ] . base ) ) {
ret = PTR_ERR ( vpu - > mem_info [ i ] . base ) ;
dev_err ( dev , " Failed to ioremap \n " ) ;
return ret ;
}
vpu - > mem_info [ i ] . len = resource_size ( mem ) ;
vpu - > mem_info [ i ] . map = & vpu_mem_map [ i ] ;
}
vpu - > clks [ 0 ] . id = " vpu " ;
vpu - > clks [ 1 ] . id = " aux " ;
ret = devm_clk_bulk_get ( dev , ARRAY_SIZE ( vpu - > clks ) , vpu - > clks ) ;
if ( ret ) {
dev_err ( dev , " Failed to get clocks \n " ) ;
return ret ;
}
vpu - > irq = platform_get_irq ( pdev , 0 ) ;
if ( vpu - > irq < 0 )
return vpu - > irq ;
2021-12-05 14:13:49 +03:00
ret = devm_request_irq ( dev , vpu - > irq , vpu_interrupt , IRQF_NO_AUTOEN ,
" VPU " , rproc ) ;
2020-05-15 13:43:39 +03:00
if ( ret < 0 ) {
dev_err ( dev , " Failed to request IRQ \n " ) ;
return ret ;
}
ret = devm_rproc_add ( dev , rproc ) ;
if ( ret ) {
dev_err ( dev , " Failed to register remote processor \n " ) ;
2020-06-30 19:31:17 +03:00
return ret ;
2020-05-15 13:43:39 +03:00
}
2020-06-30 19:31:17 +03:00
return 0 ;
2020-05-15 13:43:39 +03:00
}
static const struct of_device_id ingenic_rproc_of_matches [ ] = {
{ . compatible = " ingenic,jz4770-vpu-rproc " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , ingenic_rproc_of_matches ) ;
static struct platform_driver ingenic_rproc_driver = {
. probe = ingenic_rproc_probe ,
. driver = {
. name = " ingenic-vpu " ,
. of_match_table = ingenic_rproc_of_matches ,
} ,
} ;
module_platform_driver ( ingenic_rproc_driver ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Paul Cercueil <paul@crapouillou.net> " ) ;
MODULE_DESCRIPTION ( " Ingenic JZ47xx Remote Processor control driver " ) ;