2021-09-21 22:25:57 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright ( C ) 2020 Martin Blumenstingl < martin . blumenstingl @ googlemail . com >
*/
# include <linux/bitfield.h>
# include <linux/bitops.h>
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/genalloc.h>
# include <linux/io.h>
# include <linux/mfd/syscon.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/property.h>
# include <linux/regmap.h>
# include <linux/remoteproc.h>
# include <linux/reset.h>
# include <linux/sizes.h>
# include "remoteproc_internal.h"
# define AO_REMAP_REG0 0x0
# define AO_REMAP_REG0_REMAP_AHB_SRAM_BITS_17_14_FOR_ARM_CPU GENMASK(3, 0)
# define AO_REMAP_REG1 0x4
# define AO_REMAP_REG1_MOVE_AHB_SRAM_TO_0X0_INSTEAD_OF_DDR BIT(4)
# define AO_REMAP_REG1_REMAP_AHB_SRAM_BITS_17_14_FOR_MEDIA_CPU GENMASK(3, 0)
# define AO_CPU_CNTL 0x0
# define AO_CPU_CNTL_AHB_SRAM_BITS_31_20 GENMASK(28, 16)
# define AO_CPU_CNTL_HALT BIT(9)
# define AO_CPU_CNTL_UNKNONWN BIT(8)
# define AO_CPU_CNTL_RUN BIT(0)
# define AO_CPU_STAT 0x4
# define AO_SECURE_REG0 0x0
# define AO_SECURE_REG0_AHB_SRAM_BITS_19_12 GENMASK(15, 8)
/* Only bits [31:20] and [17:14] are usable, all other bits must be zero */
2021-10-04 13:52:57 +03:00
# define MESON_AO_RPROC_SRAM_USABLE_BITS 0xfff3c000ULL
2021-09-21 22:25:57 +03:00
# define MESON_AO_RPROC_MEMORY_OFFSET 0x10000000
struct meson_mx_ao_arc_rproc_priv {
void __iomem * remap_base ;
void __iomem * cpu_base ;
unsigned long sram_va ;
phys_addr_t sram_pa ;
size_t sram_size ;
struct gen_pool * sram_pool ;
struct reset_control * arc_reset ;
struct clk * arc_pclk ;
struct regmap * secbus2_regmap ;
} ;
static int meson_mx_ao_arc_rproc_start ( struct rproc * rproc )
{
struct meson_mx_ao_arc_rproc_priv * priv = rproc - > priv ;
phys_addr_t translated_sram_addr ;
u32 tmp ;
int ret ;
ret = clk_prepare_enable ( priv - > arc_pclk ) ;
if ( ret )
return ret ;
tmp = FIELD_PREP ( AO_REMAP_REG0_REMAP_AHB_SRAM_BITS_17_14_FOR_ARM_CPU ,
priv - > sram_pa > > 14 ) ;
writel ( tmp , priv - > remap_base + AO_REMAP_REG0 ) ;
/*
* The SRAM content as seen by the ARC core always starts at 0x0
* regardless of the value given here ( this was discovered by trial and
* error ) . For SoCs older than Meson6 we probably have to set
* AO_REMAP_REG1_MOVE_AHB_SRAM_TO_0X0_INSTEAD_OF_DDR to achieve the
* same . ( At least ) For Meson8 and newer that bit must not be set .
*/
writel ( 0x0 , priv - > remap_base + AO_REMAP_REG1 ) ;
regmap_update_bits ( priv - > secbus2_regmap , AO_SECURE_REG0 ,
AO_SECURE_REG0_AHB_SRAM_BITS_19_12 ,
FIELD_PREP ( AO_SECURE_REG0_AHB_SRAM_BITS_19_12 ,
priv - > sram_pa > > 12 ) ) ;
ret = reset_control_reset ( priv - > arc_reset ) ;
if ( ret ) {
clk_disable_unprepare ( priv - > arc_pclk ) ;
return ret ;
}
usleep_range ( 10 , 100 ) ;
/*
* Convert from 0xd9000000 to 0xc9000000 as the vendor driver does .
* This only seems to be relevant for the AO_CPU_CNTL register . It is
* unknown why this is needed .
*/
translated_sram_addr = priv - > sram_pa - MESON_AO_RPROC_MEMORY_OFFSET ;
tmp = FIELD_PREP ( AO_CPU_CNTL_AHB_SRAM_BITS_31_20 ,
translated_sram_addr > > 20 ) ;
tmp | = AO_CPU_CNTL_UNKNONWN | AO_CPU_CNTL_RUN ;
writel ( tmp , priv - > cpu_base + AO_CPU_CNTL ) ;
usleep_range ( 20 , 200 ) ;
return 0 ;
}
static int meson_mx_ao_arc_rproc_stop ( struct rproc * rproc )
{
struct meson_mx_ao_arc_rproc_priv * priv = rproc - > priv ;
writel ( AO_CPU_CNTL_HALT , priv - > cpu_base + AO_CPU_CNTL ) ;
clk_disable_unprepare ( priv - > arc_pclk ) ;
return 0 ;
}
static void * meson_mx_ao_arc_rproc_da_to_va ( struct rproc * rproc , u64 da ,
size_t len , bool * is_iomem )
{
struct meson_mx_ao_arc_rproc_priv * priv = rproc - > priv ;
/* The memory from the ARC core's perspective always starts at 0x0. */
if ( ( da + len ) > priv - > sram_size )
return NULL ;
return ( void * ) priv - > sram_va + da ;
}
static struct rproc_ops meson_mx_ao_arc_rproc_ops = {
. start = meson_mx_ao_arc_rproc_start ,
. stop = meson_mx_ao_arc_rproc_stop ,
. da_to_va = meson_mx_ao_arc_rproc_da_to_va ,
. get_boot_addr = rproc_elf_get_boot_addr ,
. load = rproc_elf_load_segments ,
. sanity_check = rproc_elf_sanity_check ,
} ;
static int meson_mx_ao_arc_rproc_probe ( struct platform_device * pdev )
{
struct meson_mx_ao_arc_rproc_priv * priv ;
struct device * dev = & pdev - > dev ;
const char * fw_name = NULL ;
struct rproc * rproc ;
int ret ;
device_property_read_string ( dev , " firmware-name " , & fw_name ) ;
rproc = devm_rproc_alloc ( dev , " meson-mx-ao-arc " ,
& meson_mx_ao_arc_rproc_ops , fw_name ,
sizeof ( * priv ) ) ;
if ( ! rproc )
return - ENOMEM ;
rproc - > has_iommu = false ;
priv = rproc - > priv ;
priv - > sram_pool = of_gen_pool_get ( dev - > of_node , " sram " , 0 ) ;
if ( ! priv - > sram_pool ) {
dev_err ( dev , " Could not get SRAM pool \n " ) ;
return - ENODEV ;
}
priv - > sram_size = gen_pool_avail ( priv - > sram_pool ) ;
priv - > sram_va = gen_pool_alloc ( priv - > sram_pool , priv - > sram_size ) ;
if ( ! priv - > sram_va ) {
dev_err ( dev , " Could not alloc memory in SRAM pool \n " ) ;
return - ENOMEM ;
}
priv - > sram_pa = gen_pool_virt_to_phys ( priv - > sram_pool , priv - > sram_va ) ;
if ( priv - > sram_pa & ~ MESON_AO_RPROC_SRAM_USABLE_BITS ) {
dev_err ( dev , " SRAM address contains unusable bits \n " ) ;
ret = - EINVAL ;
goto err_free_genpool ;
}
priv - > secbus2_regmap = syscon_regmap_lookup_by_phandle ( dev - > of_node ,
" amlogic,secbus2 " ) ;
if ( IS_ERR ( priv - > secbus2_regmap ) ) {
dev_err ( dev , " Failed to find SECBUS2 regmap \n " ) ;
ret = PTR_ERR ( priv - > secbus2_regmap ) ;
goto err_free_genpool ;
}
priv - > remap_base = devm_platform_ioremap_resource_byname ( pdev , " remap " ) ;
if ( IS_ERR ( priv - > remap_base ) ) {
ret = PTR_ERR ( priv - > remap_base ) ;
goto err_free_genpool ;
}
priv - > cpu_base = devm_platform_ioremap_resource_byname ( pdev , " cpu " ) ;
if ( IS_ERR ( priv - > cpu_base ) ) {
ret = PTR_ERR ( priv - > cpu_base ) ;
goto err_free_genpool ;
}
priv - > arc_reset = devm_reset_control_get_exclusive ( dev , NULL ) ;
if ( IS_ERR ( priv - > arc_reset ) ) {
dev_err ( dev , " Failed to get ARC reset \n " ) ;
ret = PTR_ERR ( priv - > arc_reset ) ;
goto err_free_genpool ;
}
priv - > arc_pclk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( priv - > arc_pclk ) ) {
dev_err ( dev , " Failed to get the ARC PCLK \n " ) ;
ret = PTR_ERR ( priv - > arc_pclk ) ;
goto err_free_genpool ;
}
platform_set_drvdata ( pdev , rproc ) ;
ret = rproc_add ( rproc ) ;
if ( ret )
goto err_free_genpool ;
return 0 ;
err_free_genpool :
gen_pool_free ( priv - > sram_pool , priv - > sram_va , priv - > sram_size ) ;
return ret ;
}
static int meson_mx_ao_arc_rproc_remove ( struct platform_device * pdev )
{
struct rproc * rproc = platform_get_drvdata ( pdev ) ;
struct meson_mx_ao_arc_rproc_priv * priv = rproc - > priv ;
rproc_del ( rproc ) ;
gen_pool_free ( priv - > sram_pool , priv - > sram_va , priv - > sram_size ) ;
return 0 ;
}
static const struct of_device_id meson_mx_ao_arc_rproc_match [ ] = {
{ . compatible = " amlogic,meson8-ao-arc " } ,
{ . compatible = " amlogic,meson8b-ao-arc " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , meson_mx_ao_arc_rproc_match ) ;
static struct platform_driver meson_mx_ao_arc_rproc_driver = {
. probe = meson_mx_ao_arc_rproc_probe ,
. remove = meson_mx_ao_arc_rproc_remove ,
. driver = {
. name = " meson-mx-ao-arc-rproc " ,
. of_match_table = meson_mx_ao_arc_rproc_match ,
} ,
} ;
module_platform_driver ( meson_mx_ao_arc_rproc_driver ) ;
MODULE_DESCRIPTION ( " Amlogic Meson6/8/8b/8m2 AO ARC remote processor driver " ) ;
MODULE_AUTHOR ( " Martin Blumenstingl <martin.blumenstingl@googlemail.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;