162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bitfield.h> 762306a36Sopenharmony_ci#include <linux/bitops.h> 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/genalloc.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/property.h> 1662306a36Sopenharmony_ci#include <linux/regmap.h> 1762306a36Sopenharmony_ci#include <linux/remoteproc.h> 1862306a36Sopenharmony_ci#include <linux/reset.h> 1962306a36Sopenharmony_ci#include <linux/sizes.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "remoteproc_internal.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define AO_REMAP_REG0 0x0 2462306a36Sopenharmony_ci#define AO_REMAP_REG0_REMAP_AHB_SRAM_BITS_17_14_FOR_ARM_CPU GENMASK(3, 0) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define AO_REMAP_REG1 0x4 2762306a36Sopenharmony_ci#define AO_REMAP_REG1_MOVE_AHB_SRAM_TO_0X0_INSTEAD_OF_DDR BIT(4) 2862306a36Sopenharmony_ci#define AO_REMAP_REG1_REMAP_AHB_SRAM_BITS_17_14_FOR_MEDIA_CPU GENMASK(3, 0) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define AO_CPU_CNTL 0x0 3162306a36Sopenharmony_ci#define AO_CPU_CNTL_AHB_SRAM_BITS_31_20 GENMASK(28, 16) 3262306a36Sopenharmony_ci#define AO_CPU_CNTL_HALT BIT(9) 3362306a36Sopenharmony_ci#define AO_CPU_CNTL_UNKNONWN BIT(8) 3462306a36Sopenharmony_ci#define AO_CPU_CNTL_RUN BIT(0) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define AO_CPU_STAT 0x4 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define AO_SECURE_REG0 0x0 3962306a36Sopenharmony_ci#define AO_SECURE_REG0_AHB_SRAM_BITS_19_12 GENMASK(15, 8) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Only bits [31:20] and [17:14] are usable, all other bits must be zero */ 4262306a36Sopenharmony_ci#define MESON_AO_RPROC_SRAM_USABLE_BITS 0xfff3c000ULL 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define MESON_AO_RPROC_MEMORY_OFFSET 0x10000000 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistruct meson_mx_ao_arc_rproc_priv { 4762306a36Sopenharmony_ci void __iomem *remap_base; 4862306a36Sopenharmony_ci void __iomem *cpu_base; 4962306a36Sopenharmony_ci unsigned long sram_va; 5062306a36Sopenharmony_ci phys_addr_t sram_pa; 5162306a36Sopenharmony_ci size_t sram_size; 5262306a36Sopenharmony_ci struct gen_pool *sram_pool; 5362306a36Sopenharmony_ci struct reset_control *arc_reset; 5462306a36Sopenharmony_ci struct clk *arc_pclk; 5562306a36Sopenharmony_ci struct regmap *secbus2_regmap; 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int meson_mx_ao_arc_rproc_start(struct rproc *rproc) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct meson_mx_ao_arc_rproc_priv *priv = rproc->priv; 6162306a36Sopenharmony_ci phys_addr_t translated_sram_addr; 6262306a36Sopenharmony_ci u32 tmp; 6362306a36Sopenharmony_ci int ret; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci ret = clk_prepare_enable(priv->arc_pclk); 6662306a36Sopenharmony_ci if (ret) 6762306a36Sopenharmony_ci return ret; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci tmp = FIELD_PREP(AO_REMAP_REG0_REMAP_AHB_SRAM_BITS_17_14_FOR_ARM_CPU, 7062306a36Sopenharmony_ci priv->sram_pa >> 14); 7162306a36Sopenharmony_ci writel(tmp, priv->remap_base + AO_REMAP_REG0); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* 7462306a36Sopenharmony_ci * The SRAM content as seen by the ARC core always starts at 0x0 7562306a36Sopenharmony_ci * regardless of the value given here (this was discovered by trial and 7662306a36Sopenharmony_ci * error). For SoCs older than Meson6 we probably have to set 7762306a36Sopenharmony_ci * AO_REMAP_REG1_MOVE_AHB_SRAM_TO_0X0_INSTEAD_OF_DDR to achieve the 7862306a36Sopenharmony_ci * same. (At least) For Meson8 and newer that bit must not be set. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci writel(0x0, priv->remap_base + AO_REMAP_REG1); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci regmap_update_bits(priv->secbus2_regmap, AO_SECURE_REG0, 8362306a36Sopenharmony_ci AO_SECURE_REG0_AHB_SRAM_BITS_19_12, 8462306a36Sopenharmony_ci FIELD_PREP(AO_SECURE_REG0_AHB_SRAM_BITS_19_12, 8562306a36Sopenharmony_ci priv->sram_pa >> 12)); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci ret = reset_control_reset(priv->arc_reset); 8862306a36Sopenharmony_ci if (ret) { 8962306a36Sopenharmony_ci clk_disable_unprepare(priv->arc_pclk); 9062306a36Sopenharmony_ci return ret; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci usleep_range(10, 100); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* 9662306a36Sopenharmony_ci * Convert from 0xd9000000 to 0xc9000000 as the vendor driver does. 9762306a36Sopenharmony_ci * This only seems to be relevant for the AO_CPU_CNTL register. It is 9862306a36Sopenharmony_ci * unknown why this is needed. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci translated_sram_addr = priv->sram_pa - MESON_AO_RPROC_MEMORY_OFFSET; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci tmp = FIELD_PREP(AO_CPU_CNTL_AHB_SRAM_BITS_31_20, 10362306a36Sopenharmony_ci translated_sram_addr >> 20); 10462306a36Sopenharmony_ci tmp |= AO_CPU_CNTL_UNKNONWN | AO_CPU_CNTL_RUN; 10562306a36Sopenharmony_ci writel(tmp, priv->cpu_base + AO_CPU_CNTL); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci usleep_range(20, 200); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int meson_mx_ao_arc_rproc_stop(struct rproc *rproc) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct meson_mx_ao_arc_rproc_priv *priv = rproc->priv; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci writel(AO_CPU_CNTL_HALT, priv->cpu_base + AO_CPU_CNTL); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci clk_disable_unprepare(priv->arc_pclk); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic void *meson_mx_ao_arc_rproc_da_to_va(struct rproc *rproc, u64 da, 12462306a36Sopenharmony_ci size_t len, bool *is_iomem) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct meson_mx_ao_arc_rproc_priv *priv = rproc->priv; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* The memory from the ARC core's perspective always starts at 0x0. */ 12962306a36Sopenharmony_ci if ((da + len) > priv->sram_size) 13062306a36Sopenharmony_ci return NULL; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return (void *)priv->sram_va + da; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic struct rproc_ops meson_mx_ao_arc_rproc_ops = { 13662306a36Sopenharmony_ci .start = meson_mx_ao_arc_rproc_start, 13762306a36Sopenharmony_ci .stop = meson_mx_ao_arc_rproc_stop, 13862306a36Sopenharmony_ci .da_to_va = meson_mx_ao_arc_rproc_da_to_va, 13962306a36Sopenharmony_ci .get_boot_addr = rproc_elf_get_boot_addr, 14062306a36Sopenharmony_ci .load = rproc_elf_load_segments, 14162306a36Sopenharmony_ci .sanity_check = rproc_elf_sanity_check, 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int meson_mx_ao_arc_rproc_probe(struct platform_device *pdev) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct meson_mx_ao_arc_rproc_priv *priv; 14762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 14862306a36Sopenharmony_ci const char *fw_name = NULL; 14962306a36Sopenharmony_ci struct rproc *rproc; 15062306a36Sopenharmony_ci int ret; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci device_property_read_string(dev, "firmware-name", &fw_name); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci rproc = devm_rproc_alloc(dev, "meson-mx-ao-arc", 15562306a36Sopenharmony_ci &meson_mx_ao_arc_rproc_ops, fw_name, 15662306a36Sopenharmony_ci sizeof(*priv)); 15762306a36Sopenharmony_ci if (!rproc) 15862306a36Sopenharmony_ci return -ENOMEM; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci rproc->has_iommu = false; 16162306a36Sopenharmony_ci priv = rproc->priv; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci priv->sram_pool = of_gen_pool_get(dev->of_node, "sram", 0); 16462306a36Sopenharmony_ci if (!priv->sram_pool) { 16562306a36Sopenharmony_ci dev_err(dev, "Could not get SRAM pool\n"); 16662306a36Sopenharmony_ci return -ENODEV; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci priv->sram_size = gen_pool_avail(priv->sram_pool); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci priv->sram_va = gen_pool_alloc(priv->sram_pool, priv->sram_size); 17262306a36Sopenharmony_ci if (!priv->sram_va) { 17362306a36Sopenharmony_ci dev_err(dev, "Could not alloc memory in SRAM pool\n"); 17462306a36Sopenharmony_ci return -ENOMEM; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci priv->sram_pa = gen_pool_virt_to_phys(priv->sram_pool, priv->sram_va); 17862306a36Sopenharmony_ci if (priv->sram_pa & ~MESON_AO_RPROC_SRAM_USABLE_BITS) { 17962306a36Sopenharmony_ci dev_err(dev, "SRAM address contains unusable bits\n"); 18062306a36Sopenharmony_ci ret = -EINVAL; 18162306a36Sopenharmony_ci goto err_free_genpool; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci priv->secbus2_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, 18562306a36Sopenharmony_ci "amlogic,secbus2"); 18662306a36Sopenharmony_ci if (IS_ERR(priv->secbus2_regmap)) { 18762306a36Sopenharmony_ci dev_err(dev, "Failed to find SECBUS2 regmap\n"); 18862306a36Sopenharmony_ci ret = PTR_ERR(priv->secbus2_regmap); 18962306a36Sopenharmony_ci goto err_free_genpool; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci priv->remap_base = devm_platform_ioremap_resource_byname(pdev, "remap"); 19362306a36Sopenharmony_ci if (IS_ERR(priv->remap_base)) { 19462306a36Sopenharmony_ci ret = PTR_ERR(priv->remap_base); 19562306a36Sopenharmony_ci goto err_free_genpool; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci priv->cpu_base = devm_platform_ioremap_resource_byname(pdev, "cpu"); 19962306a36Sopenharmony_ci if (IS_ERR(priv->cpu_base)) { 20062306a36Sopenharmony_ci ret = PTR_ERR(priv->cpu_base); 20162306a36Sopenharmony_ci goto err_free_genpool; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci priv->arc_reset = devm_reset_control_get_exclusive(dev, NULL); 20562306a36Sopenharmony_ci if (IS_ERR(priv->arc_reset)) { 20662306a36Sopenharmony_ci dev_err(dev, "Failed to get ARC reset\n"); 20762306a36Sopenharmony_ci ret = PTR_ERR(priv->arc_reset); 20862306a36Sopenharmony_ci goto err_free_genpool; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci priv->arc_pclk = devm_clk_get(dev, NULL); 21262306a36Sopenharmony_ci if (IS_ERR(priv->arc_pclk)) { 21362306a36Sopenharmony_ci dev_err(dev, "Failed to get the ARC PCLK\n"); 21462306a36Sopenharmony_ci ret = PTR_ERR(priv->arc_pclk); 21562306a36Sopenharmony_ci goto err_free_genpool; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci platform_set_drvdata(pdev, rproc); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ret = rproc_add(rproc); 22162306a36Sopenharmony_ci if (ret) 22262306a36Sopenharmony_ci goto err_free_genpool; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cierr_free_genpool: 22762306a36Sopenharmony_ci gen_pool_free(priv->sram_pool, priv->sram_va, priv->sram_size); 22862306a36Sopenharmony_ci return ret; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic void meson_mx_ao_arc_rproc_remove(struct platform_device *pdev) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct rproc *rproc = platform_get_drvdata(pdev); 23462306a36Sopenharmony_ci struct meson_mx_ao_arc_rproc_priv *priv = rproc->priv; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci rproc_del(rproc); 23762306a36Sopenharmony_ci gen_pool_free(priv->sram_pool, priv->sram_va, priv->sram_size); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic const struct of_device_id meson_mx_ao_arc_rproc_match[] = { 24162306a36Sopenharmony_ci { .compatible = "amlogic,meson8-ao-arc" }, 24262306a36Sopenharmony_ci { .compatible = "amlogic,meson8b-ao-arc" }, 24362306a36Sopenharmony_ci { /* sentinel */ } 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, meson_mx_ao_arc_rproc_match); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic struct platform_driver meson_mx_ao_arc_rproc_driver = { 24862306a36Sopenharmony_ci .probe = meson_mx_ao_arc_rproc_probe, 24962306a36Sopenharmony_ci .remove_new = meson_mx_ao_arc_rproc_remove, 25062306a36Sopenharmony_ci .driver = { 25162306a36Sopenharmony_ci .name = "meson-mx-ao-arc-rproc", 25262306a36Sopenharmony_ci .of_match_table = meson_mx_ao_arc_rproc_match, 25362306a36Sopenharmony_ci }, 25462306a36Sopenharmony_ci}; 25562306a36Sopenharmony_cimodule_platform_driver(meson_mx_ao_arc_rproc_driver); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ciMODULE_DESCRIPTION("Amlogic Meson6/8/8b/8m2 AO ARC remote processor driver"); 25862306a36Sopenharmony_ciMODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); 25962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 260