All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dmitry Osipenko <digetx@gmail.com>
To: Thierry Reding <thierry.reding@gmail.com>,
	Jonathan Hunter <jonathanh@nvidia.com>,
	Peter De Schrijver <pdeschrijver@nvidia.com>,
	"Rafael J. Wysocki" <rjw@rjwysocki.net>,
	Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: linux-pm@vger.kernel.org, linux-tegra@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2 3/6] cpuidle: Add unified driver for NVIDIA Tegra SoCs
Date: Fri, 12 Jul 2019 19:41:01 +0300	[thread overview]
Message-ID: <01ec2515-8c54-4809-b79a-363db12c453f@gmail.com> (raw)
In-Reply-To: <c54c43fd-9b11-4aa8-3acf-d2260d0fca4a@gmail.com>

11.07.2019 21:35, Dmitry Osipenko пишет:
> 11.07.2019 6:13, Dmitry Osipenko пишет:
>> The new driver is based on the old CPU Idle drivers that are removed now
>> from arm/arch/mach-tegra/ directory. Those removed drivers were reworked
>> and squashed into a single unified driver that covers multiple hardware
>> generations, starting from Tegra20 and ending with Tegra124.
>>
>> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
>> ---
>>  arch/arm/mach-tegra/tegra.c     |   4 +
>>  drivers/cpuidle/Kconfig.arm     |   8 +
>>  drivers/cpuidle/Makefile        |   1 +
>>  drivers/cpuidle/cpuidle-tegra.c | 304 ++++++++++++++++++++++++++++++++
>>  include/soc/tegra/cpuidle.h     |   4 +
>>  5 files changed, 321 insertions(+)
>>  create mode 100644 drivers/cpuidle/cpuidle-tegra.c
>>
>> diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c
>> index d9237769a37c..f1ce2857a251 100644
>> --- a/arch/arm/mach-tegra/tegra.c
>> +++ b/arch/arm/mach-tegra/tegra.c
>> @@ -36,6 +36,7 @@
>>  #include <asm/mach/arch.h>
>>  #include <asm/mach/time.h>
>>  #include <asm/mach-types.h>
>> +#include <asm/psci.h>
>>  #include <asm/setup.h>
>>  
>>  #include "board.h"
>> @@ -92,6 +93,9 @@ static void __init tegra_dt_init_late(void)
>>  	if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) &&
>>  	    of_machine_is_compatible("nvidia,tegra20"))
>>  		platform_device_register_simple("tegra20-cpufreq", -1, NULL, 0);
>> +
>> +	if (IS_ENABLED(CONFIG_ARM_TEGRA_CPUIDLE) && !psci_smp_available())
>> +		platform_device_register_simple("tegra-cpuidle", -1, NULL, 0);
>>  }
>>  
>>  static const char * const tegra_dt_board_compat[] = {
>> diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
>> index 48cb3d4bb7d1..d90861361f1d 100644
>> --- a/drivers/cpuidle/Kconfig.arm
>> +++ b/drivers/cpuidle/Kconfig.arm
>> @@ -76,3 +76,11 @@ config ARM_MVEBU_V7_CPUIDLE
>>  	depends on ARCH_MVEBU && !ARM64
>>  	help
>>  	  Select this to enable cpuidle on Armada 370, 38x and XP processors.
>> +
>> +config ARM_TEGRA_CPUIDLE
>> +	bool "CPU Idle Driver for NVIDIA Tegra SoCs"
>> +	depends on ARCH_TEGRA && !ARM64
>> +	select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP
>> +	select ARM_CPU_SUSPEND
>> +	help
>> +	  Select this to enable cpuidle for NVIDIA Tegra20/30/114/124 SoCs.
>> diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
>> index 9d7176cee3d3..470d17fa8746 100644
>> --- a/drivers/cpuidle/Makefile
>> +++ b/drivers/cpuidle/Makefile
>> @@ -20,6 +20,7 @@ obj-$(CONFIG_ARM_U8500_CPUIDLE)         += cpuidle-ux500.o
>>  obj-$(CONFIG_ARM_AT91_CPUIDLE)          += cpuidle-at91.o
>>  obj-$(CONFIG_ARM_EXYNOS_CPUIDLE)        += cpuidle-exynos.o
>>  obj-$(CONFIG_ARM_CPUIDLE)		+= cpuidle-arm.o
>> +obj-$(CONFIG_ARM_TEGRA_CPUIDLE)		+= cpuidle-tegra.o
>>  
>>  ###############################################################################
>>  # MIPS drivers
>> diff --git a/drivers/cpuidle/cpuidle-tegra.c b/drivers/cpuidle/cpuidle-tegra.c
>> new file mode 100644
>> index 000000000000..54974cd2255f
>> --- /dev/null
>> +++ b/drivers/cpuidle/cpuidle-tegra.c
>> @@ -0,0 +1,304 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * CPU idle driver for Tegra CPUs
>> + *
>> + * Copyright (c) 2010-2013, NVIDIA Corporation.
>> + * Copyright (c) 2011 Google, Inc.
>> + * Author: Colin Cross <ccross@android.com>
>> + *         Gary King <gking@nvidia.com>
>> + *
>> + * Rework for 3.3 by Peter De Schrijver <pdeschrijver@nvidia.com>
>> + *
>> + * Tegra20/124 driver unification by Dmitry Osipenko <digetx@gmail.com>
>> + */
>> +
>> +#include <linux/cpuidle.h>
>> +#include <linux/cpumask.h>
>> +#include <linux/cpu_pm.h>
>> +#include <linux/errno.h>
>> +#include <linux/ktime.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/types.h>
>> +
>> +#include <linux/clk/tegra.h>
>> +#include <linux/firmware/trusted_foundations.h>
>> +
>> +#include <soc/tegra/cpuidle.h>
>> +#include <soc/tegra/flowctrl.h>
>> +#include <soc/tegra/fuse.h>
>> +#include <soc/tegra/pm.h>
>> +
>> +#include <asm/cpuidle.h>
>> +#include <asm/firmware.h>
>> +#include <asm/smp_plat.h>
>> +#include <asm/suspend.h>
>> +
>> +#define TEGRA_C1		0
>> +#define TEGRA_C7		1
>> +#define TEGRA_CC6		2
>> +
>> +static atomic_t tegra_idle_barrier;
>> +
>> +static inline bool tegra_cpuidle_using_firmware(void)
>> +{
>> +	return firmware_ops->prepare_idle && firmware_ops->do_idle;
>> +}
>> +
>> +static int tegra_shutdown_secondary_cpu(unsigned long cpu)
>> +{
>> +	tegra_cpu_die(cpu);
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static int tegra_await_secondary_cpus_shutdown(void)
>> +{
>> +	ktime_t timeout = ktime_add_ms(ktime_get(), 1);
>> +
>> +	/*
>> +	 * The boot CPU shall await for the secondaries shutdown in order
>> +	 * to power-off CPU's cluster safely.
>> +	 */
>> +	do {
>> +		if (tegra_cpu_rail_off_ready())
>> +			return 0;
>> +
>> +	} while (ktime_compare(ktime_get(), timeout) < 0);
>> +
>> +	return -ETIMEDOUT;
>> +}
>> +
>> +static void tegra_wake_up_secondary_cpus(void)
>> +{
>> +	unsigned int cpu, lcpu;
>> +
>> +	for_each_cpu(lcpu, cpu_online_mask) {
>> +		cpu = cpu_logical_map(lcpu);
>> +
>> +		if (cpu > 0) {
>> +			tegra_enable_cpu_clock(cpu);
>> +			tegra_cpu_out_of_reset(cpu);
>> +			flowctrl_write_cpu_halt(cpu, 0);
>> +		}
>> +	}
>> +}
>> +
>> +static int tegra_cpuidle_cc6_enter(void)
>> +{
>> +	int err;
>> +
>> +	err = tegra_await_secondary_cpus_shutdown();
>> +	if (err)
>> +		return err;
>> +
>> +	err = tegra_idle_lp2_last();
>> +
>> +	tegra_wake_up_secondary_cpus();
>> +
>> +	return err;
>> +}
>> +
>> +static int tegra_cpuidle_c7_enter(void)
>> +{
>> +	int err;
>> +
>> +	if (tegra_cpuidle_using_firmware()) {
>> +		err = call_firmware_op(prepare_idle, TF_PM_MODE_LP2_NOFLUSH_L2);
>> +		if (err)
>> +			return err;
>> +
>> +		return call_firmware_op(do_idle, 0);
>> +	}
>> +
>> +	return cpu_suspend(0, tegra30_sleep_cpu_secondary_finish);
>> +}
>> +
>> +static int tegra_cpuidle_enter(struct cpuidle_device *dev,
>> +			       int index, unsigned int cpu)
>> +{
>> +	int err;
>> +
>> +	local_fiq_disable();
>> +	tegra_set_cpu_in_lp2();
>> +	cpu_pm_enter();
>> +
>> +	switch (index) {
>> +	case TEGRA_C7:
>> +		err = tegra_cpuidle_c7_enter();
>> +		break;
>> +	case TEGRA_CC6:
>> +		cpuidle_coupled_parallel_barrier(dev, &tegra_idle_barrier);
> 
> I realized that this is not very correct. We still need to do a proper
> barrier with SGI checking in order to bail out if other CPU sent IPI
> during of the awaiting for a coupled barrier to avoid the overhead of
> unnecessary power-gating. Will correct that in the next revision.

UPDATE: Actually, turned out it's even a necessity to handle the SGI
because GIC's driver doesn't save and replay SGI across CPU cluster PM.

WARNING: multiple messages have this Message-ID (diff)
From: Dmitry Osipenko <digetx@gmail.com>
To: Thierry Reding <thierry.reding@gmail.com>,
	Jonathan Hunter <jonathanh@nvidia.com>,
	Peter De Schrijver <pdeschrijver@nvidia.com>,
	"Rafael J. Wysocki" <rjw@rjwysocki.net>,
	Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, linux-pm@vger.kernel.org
Subject: Re: [PATCH v2 3/6] cpuidle: Add unified driver for NVIDIA Tegra SoCs
Date: Fri, 12 Jul 2019 19:41:01 +0300	[thread overview]
Message-ID: <01ec2515-8c54-4809-b79a-363db12c453f@gmail.com> (raw)
In-Reply-To: <c54c43fd-9b11-4aa8-3acf-d2260d0fca4a@gmail.com>

11.07.2019 21:35, Dmitry Osipenko пишет:
> 11.07.2019 6:13, Dmitry Osipenko пишет:
>> The new driver is based on the old CPU Idle drivers that are removed now
>> from arm/arch/mach-tegra/ directory. Those removed drivers were reworked
>> and squashed into a single unified driver that covers multiple hardware
>> generations, starting from Tegra20 and ending with Tegra124.
>>
>> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
>> ---
>>  arch/arm/mach-tegra/tegra.c     |   4 +
>>  drivers/cpuidle/Kconfig.arm     |   8 +
>>  drivers/cpuidle/Makefile        |   1 +
>>  drivers/cpuidle/cpuidle-tegra.c | 304 ++++++++++++++++++++++++++++++++
>>  include/soc/tegra/cpuidle.h     |   4 +
>>  5 files changed, 321 insertions(+)
>>  create mode 100644 drivers/cpuidle/cpuidle-tegra.c
>>
>> diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c
>> index d9237769a37c..f1ce2857a251 100644
>> --- a/arch/arm/mach-tegra/tegra.c
>> +++ b/arch/arm/mach-tegra/tegra.c
>> @@ -36,6 +36,7 @@
>>  #include <asm/mach/arch.h>
>>  #include <asm/mach/time.h>
>>  #include <asm/mach-types.h>
>> +#include <asm/psci.h>
>>  #include <asm/setup.h>
>>  
>>  #include "board.h"
>> @@ -92,6 +93,9 @@ static void __init tegra_dt_init_late(void)
>>  	if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) &&
>>  	    of_machine_is_compatible("nvidia,tegra20"))
>>  		platform_device_register_simple("tegra20-cpufreq", -1, NULL, 0);
>> +
>> +	if (IS_ENABLED(CONFIG_ARM_TEGRA_CPUIDLE) && !psci_smp_available())
>> +		platform_device_register_simple("tegra-cpuidle", -1, NULL, 0);
>>  }
>>  
>>  static const char * const tegra_dt_board_compat[] = {
>> diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
>> index 48cb3d4bb7d1..d90861361f1d 100644
>> --- a/drivers/cpuidle/Kconfig.arm
>> +++ b/drivers/cpuidle/Kconfig.arm
>> @@ -76,3 +76,11 @@ config ARM_MVEBU_V7_CPUIDLE
>>  	depends on ARCH_MVEBU && !ARM64
>>  	help
>>  	  Select this to enable cpuidle on Armada 370, 38x and XP processors.
>> +
>> +config ARM_TEGRA_CPUIDLE
>> +	bool "CPU Idle Driver for NVIDIA Tegra SoCs"
>> +	depends on ARCH_TEGRA && !ARM64
>> +	select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP
>> +	select ARM_CPU_SUSPEND
>> +	help
>> +	  Select this to enable cpuidle for NVIDIA Tegra20/30/114/124 SoCs.
>> diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
>> index 9d7176cee3d3..470d17fa8746 100644
>> --- a/drivers/cpuidle/Makefile
>> +++ b/drivers/cpuidle/Makefile
>> @@ -20,6 +20,7 @@ obj-$(CONFIG_ARM_U8500_CPUIDLE)         += cpuidle-ux500.o
>>  obj-$(CONFIG_ARM_AT91_CPUIDLE)          += cpuidle-at91.o
>>  obj-$(CONFIG_ARM_EXYNOS_CPUIDLE)        += cpuidle-exynos.o
>>  obj-$(CONFIG_ARM_CPUIDLE)		+= cpuidle-arm.o
>> +obj-$(CONFIG_ARM_TEGRA_CPUIDLE)		+= cpuidle-tegra.o
>>  
>>  ###############################################################################
>>  # MIPS drivers
>> diff --git a/drivers/cpuidle/cpuidle-tegra.c b/drivers/cpuidle/cpuidle-tegra.c
>> new file mode 100644
>> index 000000000000..54974cd2255f
>> --- /dev/null
>> +++ b/drivers/cpuidle/cpuidle-tegra.c
>> @@ -0,0 +1,304 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * CPU idle driver for Tegra CPUs
>> + *
>> + * Copyright (c) 2010-2013, NVIDIA Corporation.
>> + * Copyright (c) 2011 Google, Inc.
>> + * Author: Colin Cross <ccross@android.com>
>> + *         Gary King <gking@nvidia.com>
>> + *
>> + * Rework for 3.3 by Peter De Schrijver <pdeschrijver@nvidia.com>
>> + *
>> + * Tegra20/124 driver unification by Dmitry Osipenko <digetx@gmail.com>
>> + */
>> +
>> +#include <linux/cpuidle.h>
>> +#include <linux/cpumask.h>
>> +#include <linux/cpu_pm.h>
>> +#include <linux/errno.h>
>> +#include <linux/ktime.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/types.h>
>> +
>> +#include <linux/clk/tegra.h>
>> +#include <linux/firmware/trusted_foundations.h>
>> +
>> +#include <soc/tegra/cpuidle.h>
>> +#include <soc/tegra/flowctrl.h>
>> +#include <soc/tegra/fuse.h>
>> +#include <soc/tegra/pm.h>
>> +
>> +#include <asm/cpuidle.h>
>> +#include <asm/firmware.h>
>> +#include <asm/smp_plat.h>
>> +#include <asm/suspend.h>
>> +
>> +#define TEGRA_C1		0
>> +#define TEGRA_C7		1
>> +#define TEGRA_CC6		2
>> +
>> +static atomic_t tegra_idle_barrier;
>> +
>> +static inline bool tegra_cpuidle_using_firmware(void)
>> +{
>> +	return firmware_ops->prepare_idle && firmware_ops->do_idle;
>> +}
>> +
>> +static int tegra_shutdown_secondary_cpu(unsigned long cpu)
>> +{
>> +	tegra_cpu_die(cpu);
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static int tegra_await_secondary_cpus_shutdown(void)
>> +{
>> +	ktime_t timeout = ktime_add_ms(ktime_get(), 1);
>> +
>> +	/*
>> +	 * The boot CPU shall await for the secondaries shutdown in order
>> +	 * to power-off CPU's cluster safely.
>> +	 */
>> +	do {
>> +		if (tegra_cpu_rail_off_ready())
>> +			return 0;
>> +
>> +	} while (ktime_compare(ktime_get(), timeout) < 0);
>> +
>> +	return -ETIMEDOUT;
>> +}
>> +
>> +static void tegra_wake_up_secondary_cpus(void)
>> +{
>> +	unsigned int cpu, lcpu;
>> +
>> +	for_each_cpu(lcpu, cpu_online_mask) {
>> +		cpu = cpu_logical_map(lcpu);
>> +
>> +		if (cpu > 0) {
>> +			tegra_enable_cpu_clock(cpu);
>> +			tegra_cpu_out_of_reset(cpu);
>> +			flowctrl_write_cpu_halt(cpu, 0);
>> +		}
>> +	}
>> +}
>> +
>> +static int tegra_cpuidle_cc6_enter(void)
>> +{
>> +	int err;
>> +
>> +	err = tegra_await_secondary_cpus_shutdown();
>> +	if (err)
>> +		return err;
>> +
>> +	err = tegra_idle_lp2_last();
>> +
>> +	tegra_wake_up_secondary_cpus();
>> +
>> +	return err;
>> +}
>> +
>> +static int tegra_cpuidle_c7_enter(void)
>> +{
>> +	int err;
>> +
>> +	if (tegra_cpuidle_using_firmware()) {
>> +		err = call_firmware_op(prepare_idle, TF_PM_MODE_LP2_NOFLUSH_L2);
>> +		if (err)
>> +			return err;
>> +
>> +		return call_firmware_op(do_idle, 0);
>> +	}
>> +
>> +	return cpu_suspend(0, tegra30_sleep_cpu_secondary_finish);
>> +}
>> +
>> +static int tegra_cpuidle_enter(struct cpuidle_device *dev,
>> +			       int index, unsigned int cpu)
>> +{
>> +	int err;
>> +
>> +	local_fiq_disable();
>> +	tegra_set_cpu_in_lp2();
>> +	cpu_pm_enter();
>> +
>> +	switch (index) {
>> +	case TEGRA_C7:
>> +		err = tegra_cpuidle_c7_enter();
>> +		break;
>> +	case TEGRA_CC6:
>> +		cpuidle_coupled_parallel_barrier(dev, &tegra_idle_barrier);
> 
> I realized that this is not very correct. We still need to do a proper
> barrier with SGI checking in order to bail out if other CPU sent IPI
> during of the awaiting for a coupled barrier to avoid the overhead of
> unnecessary power-gating. Will correct that in the next revision.

UPDATE: Actually, turned out it's even a necessity to handle the SGI
because GIC's driver doesn't save and replay SGI across CPU cluster PM.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  reply	other threads:[~2019-07-12 16:41 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-07-11  3:13 [PATCH v2 0/6] Consolidate and improve NVIDIA Tegra CPUIDLE driver(s) Dmitry Osipenko
2019-07-11  3:13 ` Dmitry Osipenko
2019-07-11  3:13 ` [PATCH v2 1/6] ARM: tegra: Remove cpuidle drivers Dmitry Osipenko
2019-07-11  3:13   ` Dmitry Osipenko
2019-07-11  9:26   ` Jon Hunter
2019-07-11  9:26     ` Jon Hunter
2019-07-11  9:26     ` Jon Hunter
2019-07-11 17:03     ` Dmitry Osipenko
2019-07-11 17:03       ` Dmitry Osipenko
2019-07-12  9:39       ` Jon Hunter
2019-07-12  9:39         ` Jon Hunter
2019-07-12  9:39         ` Jon Hunter
2019-07-12 16:23         ` Dmitry Osipenko
2019-07-12 16:23           ` Dmitry Osipenko
2019-07-14 22:19           ` Dmitry Osipenko
2019-07-14 22:19             ` Dmitry Osipenko
2019-07-11  3:13 ` [PATCH v2 2/6] ARM: tegra: Expose functions required for cpuidle driver Dmitry Osipenko
2019-07-11  3:13   ` Dmitry Osipenko
2019-07-11 12:42   ` Jon Hunter
2019-07-11 12:42     ` Jon Hunter
2019-07-11 12:42     ` Jon Hunter
2019-07-11 17:25     ` Dmitry Osipenko
2019-07-11 17:25       ` Dmitry Osipenko
2019-07-11  3:13 ` [PATCH v2 3/6] cpuidle: Add unified driver for NVIDIA Tegra SoCs Dmitry Osipenko
2019-07-11  3:13   ` Dmitry Osipenko
2019-07-11 18:35   ` Dmitry Osipenko
2019-07-11 18:35     ` Dmitry Osipenko
2019-07-12 16:41     ` Dmitry Osipenko [this message]
2019-07-12 16:41       ` Dmitry Osipenko
2019-07-11  3:13 ` [PATCH v2 4/6] cpuidle: tegra: Support CPU cluster power-down on Tegra30 Dmitry Osipenko
2019-07-11  3:13   ` Dmitry Osipenko
2019-07-11  3:13 ` [PATCH v2 5/6] ARM: multi_v7_defconfig: Enable Tegra cpuidle driver Dmitry Osipenko
2019-07-11  3:13   ` Dmitry Osipenko
2019-07-11  3:13 ` [PATCH v2 6/6] ARM: tegra: Enable Tegra cpuidle driver in tegra_defconfig Dmitry Osipenko
2019-07-11  3:13   ` Dmitry Osipenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=01ec2515-8c54-4809-b79a-363db12c453f@gmail.com \
    --to=digetx@gmail.com \
    --cc=daniel.lezcano@linaro.org \
    --cc=jonathanh@nvidia.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=linux-tegra@vger.kernel.org \
    --cc=pdeschrijver@nvidia.com \
    --cc=rjw@rjwysocki.net \
    --cc=thierry.reding@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.