Good morning,

On Thu, Mar 18, 2021 at 04:58:42PM -0500, Suman Anna wrote:
> The K3 AM64x SoC family has a revised R5F sub-system and contains a
> subset of the R5F clusters present on J721E SoCs. The K3 AM64x SoCs
> only have two dual-core Arm R5F clusters/subsystems with 2 R5F cores
> each present within the MAIN voltage domain (MAIN_R5FSS0 & MAIN_R5FSS1).
> 
> The revised IP has the following distinct features:
>  1. The R5FSS IP supports a new "Single-CPU" mode instead of the LockStep
>     mode on existing SoCs (AM65x, J721E or J7200). This mode is similar
>     to LockStep-mode on J7200 SoCs in terms of TCM usage without the
>     fault-tolerant safety feature provided by the LockStep mode.
> 
>     The Core1 TCMs are combined with the Core0 TCMs effectively doubling
>     the amount of TCMs available in Single-CPU mode. The LockStep-mode
>     on previous AM65x and J721E SoCs could only use the Core0 TCMs. These
>     combined TCMs appear contiguous at the respective Core0 TCM addresses.
>     The code though is executed only on a single CPU (on Core0), and as
>     such, requires the halt signal to be programmed only for Core0, while
>     the resets need to be managed for both the cores.
> 
>  2. TCMs are auto-initialized during module power-up, and the behavior
>     is programmable through a MMR bit. This feature is the same as on
>     the recent J7200 SoCs.
> 
> Extend the support to these clusters in the K3 R5F remoteproc driver
> using AM64x specific compatibles. New TI-SCI flags and a unique cluster
> mode are also needed for the cluster mode detection on these SoCs. The
> reset assert and deassert sequence of both the cores in Single-CPU mode
> is agnostic of the order, so the same LockStep reset and release sequences
> are re-used.
> 
> The integration of these clusters is very much similar to existing SoCs
> otherwise.
> 
> Signed-off-by: Suman Anna <s-a...@ti.com>
> ---
>  drivers/remoteproc/ti_k3_r5_remoteproc.c | 155 ++++++++++++++++++-----
>  1 file changed, 126 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c 
> b/drivers/remoteproc/ti_k3_r5_remoteproc.c
> index 5cf8d030a1f0..497f0d05b887 100644
> --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c
> +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c
> @@ -40,6 +40,8 @@
>  #define PROC_BOOT_CFG_FLAG_R5_ATCM_EN                        0x00002000
>  /* Available from J7200 SoCs onwards */
>  #define PROC_BOOT_CFG_FLAG_R5_MEM_INIT_DIS           0x00004000
> +/* Applicable to only AM64x SoCs */
> +#define PROC_BOOT_CFG_FLAG_R5_SINGLE_CORE            0x00008000
>  
>  /* R5 TI-SCI Processor Control Flags */
>  #define PROC_BOOT_CTRL_FLAG_R5_CORE_HALT             0x00000001
> @@ -49,6 +51,8 @@
>  #define PROC_BOOT_STATUS_FLAG_R5_WFI                 0x00000002
>  #define PROC_BOOT_STATUS_FLAG_R5_CLK_GATED           0x00000004
>  #define PROC_BOOT_STATUS_FLAG_R5_LOCKSTEP_PERMITTED  0x00000100
> +/* Applicable to only AM64x SoCs */
> +#define PROC_BOOT_STATUS_FLAG_R5_SINGLECORE_ONLY     0x00000200
>  
>  /**
>   * struct k3_r5_mem - internal memory structure
> @@ -64,19 +68,29 @@ struct k3_r5_mem {
>       size_t size;
>  };
>  
> +/*
> + * All cluster mode values are not applicable on all SoCs. The following
> + * are the modes supported on various SoCs:
> + *   Split mode      : AM65x, J721E, J7200 and AM64x SoCs
> + *   LockStep mode   : AM65x, J721E and J7200 SoCs
> + *   Single-CPU mode : AM64x SoCs only
> + */
>  enum cluster_mode {
>       CLUSTER_MODE_SPLIT = 0,
>       CLUSTER_MODE_LOCKSTEP,
> +     CLUSTER_MODE_SINGLECPU,
>  };
>  
>  /**
>   * struct k3_r5_soc_data - match data to handle SoC variations
>   * @tcm_is_double: flag to denote the larger unified TCMs in certain modes
>   * @tcm_ecc_autoinit: flag to denote the auto-initialization of TCMs for ECC
> + * @single_cpu_mode: flag to denote if SoC/IP supports Single-CPU mode
>   */
>  struct k3_r5_soc_data {
>       bool tcm_is_double;
>       bool tcm_ecc_autoinit;
> +     bool single_cpu_mode;
>  };
>  
>  /**
> @@ -369,6 +383,13 @@ static inline int k3_r5_core_run(struct k3_r5_core *core)
>   * applicable cores to allow loading into the TCMs. The .prepare() ops is
>   * invoked by remoteproc core before any firmware loading, and is followed
>   * by the .start() ops after loading to actually let the R5 cores run.
> + *
> + * The Single-CPU mode on applicable SoCs (eg: AM64x) only uses Core0 to
> + * execute code, but combines the TCMs from both cores. The resets for both
> + * cores need to be released to make this possible, as the TCMs are in 
> general
> + * private to each core. Only Core0 needs to be unhalted for running the
> + * cluster in this mode. The function uses the same reset logic as LockStep
> + * mode for this (though the behavior is agnostic of the reset release 
> order).
>   */
>  static int k3_r5_rproc_prepare(struct rproc *rproc)
>  {
> @@ -386,7 +407,9 @@ static int k3_r5_rproc_prepare(struct rproc *rproc)
>               return ret;
>       mem_init_dis = !!(cfg & PROC_BOOT_CFG_FLAG_R5_MEM_INIT_DIS);
>  
> -     ret = (cluster->mode == CLUSTER_MODE_LOCKSTEP) ?
> +     /* Re-use LockStep-mode reset logic for Single-CPU mode */
> +     ret = (cluster->mode == CLUSTER_MODE_LOCKSTEP ||
> +            cluster->mode == CLUSTER_MODE_SINGLECPU) ?
>               k3_r5_lockstep_release(cluster) : k3_r5_split_release(core);
>       if (ret) {
>               dev_err(dev, "unable to enable cores for TCM loading, ret = 
> %d\n",
> @@ -427,6 +450,12 @@ static int k3_r5_rproc_prepare(struct rproc *rproc)
>   * cores. The cores themselves are only halted in the .stop() ops, and the
>   * .unprepare() ops is invoked by the remoteproc core after the remoteproc is
>   * stopped.
> + *
> + * The Single-CPU mode on applicable SoCs (eg: AM64x) combines the TCMs from
> + * both cores. The access is made possible only with releasing the resets for
> + * both cores, but with only Core0 unhalted. This function re-uses the same
> + * reset assert logic as LockStep mode for this mode (though the behavior is
> + * agnostic of the reset assert order).
>   */
>  static int k3_r5_rproc_unprepare(struct rproc *rproc)
>  {
> @@ -436,7 +465,9 @@ static int k3_r5_rproc_unprepare(struct rproc *rproc)
>       struct device *dev = kproc->dev;
>       int ret;
>  
> -     ret = (cluster->mode == CLUSTER_MODE_LOCKSTEP) ?
> +     /* Re-use LockStep-mode reset logic for Single-CPU mode */
> +     ret = (cluster->mode == CLUSTER_MODE_LOCKSTEP ||
> +            cluster->mode == CLUSTER_MODE_SINGLECPU) ?
>               k3_r5_lockstep_reset(cluster) : k3_r5_split_reset(core);
>       if (ret)
>               dev_err(dev, "unable to disable cores, ret = %d\n", ret);
> @@ -455,6 +486,10 @@ static int k3_r5_rproc_unprepare(struct rproc *rproc)
>   * first followed by Core0. The Split-mode requires that Core0 to be 
> maintained
>   * always in a higher power state that Core1 (implying Core1 needs to be 
> started
>   * always only after Core0 is started).
> + *
> + * The Single-CPU mode on applicable SoCs (eg: AM64x) only uses Core0 to 
> execute
> + * code, so only Core0 needs to be unhalted. The function uses the same logic
> + * flow as Split-mode for this.
>   */
>  static int k3_r5_rproc_start(struct rproc *rproc)
>  {
> @@ -539,6 +574,10 @@ static int k3_r5_rproc_start(struct rproc *rproc)
>   * Core0 to be maintained always in a higher power state that Core1 (implying
>   * Core1 needs to be stopped first before Core0).
>   *
> + * The Single-CPU mode on applicable SoCs (eg: AM64x) only uses Core0 to 
> execute
> + * code, so only Core0 needs to be halted. The function uses the same logic
> + * flow as Split-mode for this.
> + *
>   * Note that the R5F halt operation in general is not effective when the R5F
>   * core is running, but is needed to make sure the core won't run after
>   * deasserting the reset the subsequent time. The asserting of reset can
> @@ -665,7 +704,9 @@ static const struct rproc_ops k3_r5_rproc_ops = {
>   *
>   * Each R5FSS has a cluster-level setting for configuring the processor
>   * subsystem either in a safety/fault-tolerant LockStep mode or a performance
> - * oriented Split mode. Each R5F core has a number of settings to either
> + * oriented Split mode on most SoCs. A fewer SoCs support a non-safety mode
> + * as an alternate for LockStep mode that exercises only a single R5F core
> + * called Single-CPU mode. Each R5F core has a number of settings to either
>   * enable/disable each of the TCMs, control which TCM appears at the R5F 
> core's
>   * address 0x0. These settings need to be configured before the resets for 
> the
>   * corresponding core are released. These settings are all protected and 
> managed
> @@ -677,11 +718,13 @@ static const struct rproc_ops k3_r5_rproc_ops = {
>   * the cores are halted before the .prepare() step.
>   *
>   * The function is called from k3_r5_cluster_rproc_init() and is invoked 
> either
> - * once (in LockStep mode) or twice (in Split mode). Support for 
> LockStep-mode
> - * is dictated by an eFUSE register bit, and the config settings retrieved 
> from
> - * DT are adjusted accordingly as per the permitted cluster mode. All cluster
> - * level settings like Cluster mode and TEINIT (exception handling state
> - * dictating ARM or Thumb mode) can only be set and retrieved using Core0.
> + * once (in LockStep mode or Single-CPU modes) or twice (in Split mode). 
> Support
> + * for LockStep-mode is dictated by an eFUSE register bit, and the config
> + * settings retrieved from DT are adjusted accordingly as per the permitted
> + * cluster mode. Another eFUSE register bit dictates if the R5F cluster only
> + * supports a Single-CPU mode. All cluster level settings like Cluster mode 
> and
> + * TEINIT (exception handling state dictating ARM or Thumb mode) can only be 
> set
> + * and retrieved using Core0.
>   *
>   * The function behavior is different based on the cluster mode. The R5F 
> cores
>   * are configured independently as per their individual settings in Split 
> mode.
> @@ -700,10 +743,16 @@ static int k3_r5_rproc_configure(struct k3_r5_rproc 
> *kproc)
>       u32 set_cfg = 0, clr_cfg = 0;
>       u64 boot_vec = 0;
>       bool lockstep_en;
> +     bool single_cpu;
>       int ret;
>  
>       core0 = list_first_entry(&cluster->cores, struct k3_r5_core, elem);
> -     core = (cluster->mode == CLUSTER_MODE_LOCKSTEP) ? core0 : kproc->core;
> +     if (cluster->mode == CLUSTER_MODE_LOCKSTEP ||
> +         cluster->mode == CLUSTER_MODE_SINGLECPU) {
> +             core = core0;
> +     } else {
> +             core = kproc->core;
> +     }
>  
>       ret = ti_sci_proc_get_status(core->tsp, &boot_vec, &cfg, &ctrl,
>                                    &stat);
> @@ -713,23 +762,48 @@ static int k3_r5_rproc_configure(struct k3_r5_rproc 
> *kproc)
>       dev_dbg(dev, "boot_vector = 0x%llx, cfg = 0x%x ctrl = 0x%x stat = 
> 0x%x\n",
>               boot_vec, cfg, ctrl, stat);
>  
> +     /* check if only Single-CPU mode is supported on applicable SoCs */
> +     if (cluster->soc_data->single_cpu_mode) {
> +             single_cpu =
> +                     !!(stat & PROC_BOOT_STATUS_FLAG_R5_SINGLECORE_ONLY);
> +             if (single_cpu && cluster->mode == CLUSTER_MODE_SPLIT) {
> +                     dev_err(cluster->dev, "split-mode not permitted, force 
> configuring for single-cpu mode\n");
> +                     cluster->mode = CLUSTER_MODE_SINGLECPU;
> +             }
> +             goto config;
> +     }
> +
> +     /* check conventional LockStep vs Split mode configuration */
>       lockstep_en = !!(stat & PROC_BOOT_STATUS_FLAG_R5_LOCKSTEP_PERMITTED);
>       if (!lockstep_en && cluster->mode == CLUSTER_MODE_LOCKSTEP) {
>               dev_err(cluster->dev, "lockstep mode not permitted, force 
> configuring for split-mode\n");
>               cluster->mode = CLUSTER_MODE_SPLIT;
>       }
>  
> +config:
>       /* always enable ARM mode and set boot vector to 0 */
>       boot_vec = 0x0;
>       if (core == core0) {
>               clr_cfg = PROC_BOOT_CFG_FLAG_R5_TEINIT;
> -             /*
> -              * LockStep configuration bit is Read-only on Split-mode _only_
> -              * devices and system firmware will NACK any requests with the
> -              * bit configured, so program it only on permitted devices
> -              */
> -             if (lockstep_en)
> -                     clr_cfg |= PROC_BOOT_CFG_FLAG_R5_LOCKSTEP;
> +             if (cluster->soc_data->single_cpu_mode) {
> +                     /*
> +                      * Single-CPU configuration bit can only be configured
> +                      * on Core0 and system firmware will NACK any requests
> +                      * with the bit configured, so program it only on
> +                      * permitted cores
> +                      */
> +                     if (cluster->mode == CLUSTER_MODE_SINGLECPU)
> +                             set_cfg = PROC_BOOT_CFG_FLAG_R5_SINGLE_CORE;
> +             } else {
> +                     /*
> +                      * LockStep configuration bit is Read-only on Split-mode
> +                      * _only_ devices and system firmware will NACK any
> +                      * requests with the bit configured, so program it only
> +                      * on permitted devices
> +                      */
> +                     if (lockstep_en)
> +                             clr_cfg |= PROC_BOOT_CFG_FLAG_R5_LOCKSTEP;
> +             }
>       }
>  
>       if (core->atcm_enable)
> @@ -894,12 +968,12 @@ static void k3_r5_reserved_mem_exit(struct k3_r5_rproc 
> *kproc)
>   * cores are usable in Split-mode, but only the Core0 TCMs can be used in
>   * LockStep-mode. The newer revisions of the R5FSS IP maximizes these TCMs by
>   * leveraging the Core1 TCMs as well in certain modes where they would have
> - * otherwise been unusable (Eg: LockStep-mode on J7200 SoCs). This is done by
> - * making a Core1 TCM visible immediately after the corresponding Core0 TCM.
> - * The SoC memory map uses the larger 64 KB sizes for the Core0 TCMs, and the
> - * dts representation reflects this increased size on supported SoCs. The 
> Core0
> - * TCM sizes therefore have to be adjusted to only half the original size in
> - * Split mode.
> + * otherwise been unusable (Eg: LockStep-mode on J7200 SoCs, Single-CPU mode 
> on
> + * AM64x SoCs). This is done by making a Core1 TCM visible immediately after 
> the
> + * corresponding Core0 TCM. The SoC memory map uses the larger 64 KB sizes 
> for
> + * the Core0 TCMs, and the dts representation reflects this increased size on
> + * supported SoCs. The Core0 TCM sizes therefore have to be adjusted to only
> + * half the original size in Split mode.
>   */
>  static void k3_r5_adjust_tcm_sizes(struct k3_r5_rproc *kproc)
>  {
> @@ -909,6 +983,7 @@ static void k3_r5_adjust_tcm_sizes(struct k3_r5_rproc 
> *kproc)
>       struct k3_r5_core *core0;
>  
>       if (cluster->mode == CLUSTER_MODE_LOCKSTEP ||
> +         cluster->mode == CLUSTER_MODE_SINGLECPU ||
>           !cluster->soc_data->tcm_is_double)
>               return;
>  
> @@ -987,8 +1062,9 @@ static int k3_r5_cluster_rproc_init(struct 
> platform_device *pdev)
>                       goto err_add;
>               }
>  
> -             /* create only one rproc in lockstep mode */
> -             if (cluster->mode == CLUSTER_MODE_LOCKSTEP)
> +             /* create only one rproc in lockstep mode or single-cpu mode */
> +             if (cluster->mode == CLUSTER_MODE_LOCKSTEP ||
> +                 cluster->mode == CLUSTER_MODE_SINGLECPU)
>                       break;
>       }
>  
> @@ -1020,11 +1096,12 @@ static void k3_r5_cluster_rproc_exit(void *data)
>       struct rproc *rproc;
>  
>       /*
> -      * lockstep mode has only one rproc associated with first core, whereas
> -      * split-mode has two rprocs associated with each core, and requires
> -      * that core1 be powered down first
> +      * lockstep mode and single-cpu modes have only one rproc associated
> +      * with first core, whereas split-mode has two rprocs associated with
> +      * each core, and requires that core1 be powered down first
>        */
> -     core = (cluster->mode == CLUSTER_MODE_LOCKSTEP) ?
> +     core = (cluster->mode == CLUSTER_MODE_LOCKSTEP ||
> +             cluster->mode == CLUSTER_MODE_SINGLECPU) ?
>               list_first_entry(&cluster->cores, struct k3_r5_core, elem) :
>               list_last_entry(&cluster->cores, struct k3_r5_core, elem);
>  
> @@ -1396,7 +1473,12 @@ static int k3_r5_probe(struct platform_device *pdev)
>               return -ENOMEM;
>  
>       cluster->dev = dev;
> -     cluster->mode = CLUSTER_MODE_LOCKSTEP;
> +     /*
> +      * default to most common efuse configurations - Split-mode on AM64x
> +      * and LockStep-mode on all others
> +      */
> +     cluster->mode = data->single_cpu_mode ?
> +                             CLUSTER_MODE_SPLIT : CLUSTER_MODE_LOCKSTEP;

I had another look after reading your comment yesterday and I
still think this patch is introducing two variables to keep track of a single
state.

In the above hunk we want to set a default value.  I suggested
of_device_is_compatible() but you could also compare @data with @am64_soc_data.
If the values are equal then we have an AM64 platform.


>       cluster->soc_data = data;
>       INIT_LIST_HEAD(&cluster->cores);
>  
> @@ -1406,6 +1488,12 @@ static int k3_r5_probe(struct platform_device *pdev)
>                       ret);
>               return ret;
>       }
> +     /*
> +      * Translate SoC-specific dts value of 1 or 2 into appropriate
> +      * driver-specific mode. Valid values are dictated by YAML binding
> +      */
> +     if (cluster->mode && data->single_cpu_mode)
> +             cluster->mode = CLUSTER_MODE_SINGLECPU;

And here I don't see why we have to set cluster->mode again.  The default was
set before calling of_property_read_u32().  To me the above two hunks could be
replace with something like:

        u32 mode = UINT_MAX;

        ret = of_property_read_u32(np, "ti,cluster-mode", &mode);
        if (ret < 0 && ret != -EINVAL) {
                dev_err(dev, "invalid format for ti,cluster-mode, ret = %d\n",
                        ret); 
                return ret;
        }

        /* No mode specificied in the DT, use default */
        if (mode == UINT_MAX) {
                if (cluster->soc_data == &am64_soc_data)
                        cluster->mode = CLUSTER_MODE_SPLIT;
                else
                        cluster->mode = CLUSTER_MODE_LOCKSTEP;

        } else {
                /* A mode was specified in the DT */
                if (mode > CLUSTER_MODE_SINGLECPU)
                        return -EINVAL;

                cluster->mode = mode;
        }

If that doesn't work then I'm missing something very subtle.

Thanks,
Mathieu

>  
>       num_cores = of_get_available_child_count(np);
>       if (num_cores != 2) {
> @@ -1450,17 +1538,26 @@ static int k3_r5_probe(struct platform_device *pdev)
>  static const struct k3_r5_soc_data am65_j721e_soc_data = {
>       .tcm_is_double = false,
>       .tcm_ecc_autoinit = false,
> +     .single_cpu_mode = false,
>  };
>  
>  static const struct k3_r5_soc_data j7200_soc_data = {
>       .tcm_is_double = true,
>       .tcm_ecc_autoinit = true,
> +     .single_cpu_mode = false,
> +};
> +
> +static const struct k3_r5_soc_data am64_soc_data = {
> +     .tcm_is_double = true,
> +     .tcm_ecc_autoinit = true,
> +     .single_cpu_mode = true,
>  };
>  
>  static const struct of_device_id k3_r5_of_match[] = {
>       { .compatible = "ti,am654-r5fss", .data = &am65_j721e_soc_data, },
>       { .compatible = "ti,j721e-r5fss", .data = &am65_j721e_soc_data, },
>       { .compatible = "ti,j7200-r5fss", .data = &j7200_soc_data, },
> +     { .compatible = "ti,am64-r5fss",  .data = &am64_soc_data, },
>       { /* sentinel */ },
>  };
>  MODULE_DEVICE_TABLE(of, k3_r5_of_match);
> -- 
> 2.30.1
> 

Reply via email to