From: "Ville Syrjälä" <ville.syrjala@linux.intel.com>
To: Chris Wilson <chris@chris-wilson.co.uk>
Cc: intel-gfx@lists.freedesktop.org
Subject: Re: [PATCH 10/10] drm/i915: Move sandybride pcode access to intel_sideband.c
Date: Tue, 23 Apr 2019 20:22:59 +0300 [thread overview]
Message-ID: <20190423172259.GE1747@intel.com> (raw)
In-Reply-To: <20190419171402.30596-11-chris@chris-wilson.co.uk>
On Fri, Apr 19, 2019 at 06:14:02PM +0100, Chris Wilson wrote:
> sandybride_pcode is another sideband, so move it to their new home.
Close enough.
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
> drivers/gpu/drm/i915/i915_drv.h | 10 --
> drivers/gpu/drm/i915/intel_hdcp.c | 1 +
> drivers/gpu/drm/i915/intel_pm.c | 195 -------------------------
> drivers/gpu/drm/i915/intel_sideband.c | 196 ++++++++++++++++++++++++++
> drivers/gpu/drm/i915/intel_sideband.h | 10 ++
> 5 files changed, 207 insertions(+), 205 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index f4879fb41aa6..45c998870b87 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -3427,16 +3427,6 @@ intel_display_capture_error_state(struct drm_i915_private *dev_priv);
> extern void intel_display_print_error_state(struct drm_i915_error_state_buf *e,
> struct intel_display_error_state *error);
>
> -int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u32 mbox, u32 *val);
> -int sandybridge_pcode_write_timeout(struct drm_i915_private *dev_priv, u32 mbox,
> - u32 val, int fast_timeout_us,
> - int slow_timeout_ms);
> -#define sandybridge_pcode_write(dev_priv, mbox, val) \
> - sandybridge_pcode_write_timeout(dev_priv, mbox, val, 500, 0)
> -
> -int skl_pcode_request(struct drm_i915_private *dev_priv, u32 mbox, u32 request,
> - u32 reply_mask, u32 reply, int timeout_base_ms);
> -
> /* intel_dpio_phy.c */
> void bxt_port_to_phy_channel(struct drm_i915_private *dev_priv, enum port port,
> enum dpio_phy *phy, enum dpio_channel *ch);
> diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
> index 2476e867981d..ca5982e45e3e 100644
> --- a/drivers/gpu/drm/i915/intel_hdcp.c
> +++ b/drivers/gpu/drm/i915/intel_hdcp.c
> @@ -16,6 +16,7 @@
> #include "i915_reg.h"
> #include "intel_drv.h"
> #include "intel_hdcp.h"
> +#include "intel_sideband.h"
>
> #define KEY_LOAD_TRIES 5
> #define ENCRYPT_STATUS_CHANGE_TIMEOUT_MS 50
> diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
> index c9a321aa5d26..0904f5ff2deb 100644
> --- a/drivers/gpu/drm/i915/intel_pm.c
> +++ b/drivers/gpu/drm/i915/intel_pm.c
> @@ -9714,201 +9714,6 @@ void intel_init_pm(struct drm_i915_private *dev_priv)
> }
> }
>
> -static inline int gen6_check_mailbox_status(struct drm_i915_private *dev_priv,
> - u32 mbox)
> -{
> - switch (mbox & GEN6_PCODE_ERROR_MASK) {
> - case GEN6_PCODE_SUCCESS:
> - return 0;
> - case GEN6_PCODE_UNIMPLEMENTED_CMD:
> - return -ENODEV;
> - case GEN6_PCODE_ILLEGAL_CMD:
> - return -ENXIO;
> - case GEN6_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE:
> - case GEN7_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE:
> - return -EOVERFLOW;
> - case GEN6_PCODE_TIMEOUT:
> - return -ETIMEDOUT;
> - default:
> - MISSING_CASE(mbox & GEN6_PCODE_ERROR_MASK);
> - return 0;
> - }
> -}
> -
> -static inline int gen7_check_mailbox_status(struct drm_i915_private *dev_priv,
> - u32 mbox)
> -{
> - switch (mbox & GEN6_PCODE_ERROR_MASK) {
> - case GEN6_PCODE_SUCCESS:
> - return 0;
> - case GEN6_PCODE_ILLEGAL_CMD:
> - return -ENXIO;
> - case GEN7_PCODE_TIMEOUT:
> - return -ETIMEDOUT;
> - case GEN7_PCODE_ILLEGAL_DATA:
> - return -EINVAL;
> - case GEN7_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE:
> - return -EOVERFLOW;
> - default:
> - MISSING_CASE(mbox & GEN6_PCODE_ERROR_MASK);
> - return 0;
> - }
> -}
> -
> -static int __sandybridge_pcode_rw(struct drm_i915_private *dev_priv,
> - u32 mbox, u32 *val,
> - int fast_timeout_us,
> - int slow_timeout_ms,
> - bool is_read)
> -{
> - lockdep_assert_held(&dev_priv->sb_lock);
> -
> - /*
> - * GEN6_PCODE_* are outside of the forcewake domain, we can
> - * use te fw I915_READ variants to reduce the amount of work
> - * required when reading/writing.
> - */
> -
> - if (I915_READ_FW(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY)
> - return -EAGAIN;
> -
> - I915_WRITE_FW(GEN6_PCODE_DATA, *val);
> - I915_WRITE_FW(GEN6_PCODE_DATA1, 0);
> - I915_WRITE_FW(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY | mbox);
> -
> - if (__intel_wait_for_register_fw(&dev_priv->uncore,
> - GEN6_PCODE_MAILBOX, GEN6_PCODE_READY, 0,
> - fast_timeout_us,
> - slow_timeout_ms,
> - &mbox))
> - return -ETIMEDOUT;
> -
> - if (is_read)
> - *val = I915_READ_FW(GEN6_PCODE_DATA);
> -
> - if (INTEL_GEN(dev_priv) > 6)
> - return gen7_check_mailbox_status(dev_priv, mbox);
> - else
> - return gen6_check_mailbox_status(dev_priv, mbox);
> -}
> -
> -int
> -sandybridge_pcode_read(struct drm_i915_private *dev_priv, u32 mbox, u32 *val)
> -{
> - int err;
> -
> - mutex_lock(&dev_priv->sb_lock);
> - err = __sandybridge_pcode_rw(dev_priv, mbox, val,
> - 500, 0,
> - true);
> - mutex_unlock(&dev_priv->sb_lock);
> -
> - if (err) {
> - DRM_DEBUG_DRIVER("warning: pcode (read from mbox %x) mailbox access failed for %ps: %d\n",
> - mbox, __builtin_return_address(0), err);
> - }
> -
> - return err;
> -}
> -
> -int sandybridge_pcode_write_timeout(struct drm_i915_private *dev_priv,
> - u32 mbox, u32 val,
> - int fast_timeout_us,
> - int slow_timeout_ms)
> -{
> - int err;
> -
> - mutex_lock(&dev_priv->sb_lock);
> - err = __sandybridge_pcode_rw(dev_priv, mbox, &val,
> - fast_timeout_us, slow_timeout_ms,
> - false);
> - mutex_unlock(&dev_priv->sb_lock);
> -
> - if (err) {
> - DRM_DEBUG_DRIVER("warning: pcode (write of 0x%08x to mbox %x) mailbox access failed for %ps: %d\n",
> - val, mbox, __builtin_return_address(0), err);
> - }
> -
> - return err;
> -}
> -
> -static bool skl_pcode_try_request(struct drm_i915_private *dev_priv, u32 mbox,
> - u32 request, u32 reply_mask, u32 reply,
> - u32 *status)
> -{
> - *status = __sandybridge_pcode_rw(dev_priv, mbox, &request,
> - 500, 0,
> - true);
> -
> - return *status || ((request & reply_mask) == reply);
> -}
> -
> -/**
> - * skl_pcode_request - send PCODE request until acknowledgment
> - * @dev_priv: device private
> - * @mbox: PCODE mailbox ID the request is targeted for
> - * @request: request ID
> - * @reply_mask: mask used to check for request acknowledgment
> - * @reply: value used to check for request acknowledgment
> - * @timeout_base_ms: timeout for polling with preemption enabled
> - *
> - * Keep resending the @request to @mbox until PCODE acknowledges it, PCODE
> - * reports an error or an overall timeout of @timeout_base_ms+50 ms expires.
> - * The request is acknowledged once the PCODE reply dword equals @reply after
> - * applying @reply_mask. Polling is first attempted with preemption enabled
> - * for @timeout_base_ms and if this times out for another 50 ms with
> - * preemption disabled.
> - *
> - * Returns 0 on success, %-ETIMEDOUT in case of a timeout, <0 in case of some
> - * other error as reported by PCODE.
> - */
> -int skl_pcode_request(struct drm_i915_private *dev_priv, u32 mbox, u32 request,
> - u32 reply_mask, u32 reply, int timeout_base_ms)
> -{
> - u32 status;
> - int ret;
> -
> - mutex_lock(&dev_priv->sb_lock);
> -
> -#define COND skl_pcode_try_request(dev_priv, mbox, request, reply_mask, reply, \
> - &status)
> -
> - /*
> - * Prime the PCODE by doing a request first. Normally it guarantees
> - * that a subsequent request, at most @timeout_base_ms later, succeeds.
> - * _wait_for() doesn't guarantee when its passed condition is evaluated
> - * first, so send the first request explicitly.
> - */
> - if (COND) {
> - ret = 0;
> - goto out;
> - }
> - ret = _wait_for(COND, timeout_base_ms * 1000, 10, 10);
> - if (!ret)
> - goto out;
> -
> - /*
> - * The above can time out if the number of requests was low (2 in the
> - * worst case) _and_ PCODE was busy for some reason even after a
> - * (queued) request and @timeout_base_ms delay. As a workaround retry
> - * the poll with preemption disabled to maximize the number of
> - * requests. Increase the timeout from @timeout_base_ms to 50ms to
> - * account for interrupts that could reduce the number of these
> - * requests, and for any quirks of the PCODE firmware that delays
> - * the request completion.
> - */
> - DRM_DEBUG_KMS("PCODE timeout, retrying with preemption disabled\n");
> - WARN_ON_ONCE(timeout_base_ms > 3);
> - preempt_disable();
> - ret = wait_for_atomic(COND, 50);
> - preempt_enable();
> -
> -out:
> - mutex_unlock(&dev_priv->sb_lock);
> - return ret ? ret : status;
> -#undef COND
> -}
> -
> static int byt_gpu_freq(struct drm_i915_private *dev_priv, int val)
> {
> struct intel_rps *rps = &dev_priv->gt_pm.rps;
> diff --git a/drivers/gpu/drm/i915/intel_sideband.c b/drivers/gpu/drm/i915/intel_sideband.c
> index 7113fb8850d6..87b5a14c7ca8 100644
> --- a/drivers/gpu/drm/i915/intel_sideband.c
> +++ b/drivers/gpu/drm/i915/intel_sideband.c
> @@ -333,3 +333,199 @@ void intel_sbi_write(struct drm_i915_private *i915, u16 reg, u32 value,
> {
> intel_sbi_rw(i915, reg, destination, &value, false);
> }
> +
> +static inline int gen6_check_mailbox_status(u32 mbox)
> +{
> + switch (mbox & GEN6_PCODE_ERROR_MASK) {
> + case GEN6_PCODE_SUCCESS:
> + return 0;
> + case GEN6_PCODE_UNIMPLEMENTED_CMD:
> + return -ENODEV;
> + case GEN6_PCODE_ILLEGAL_CMD:
> + return -ENXIO;
> + case GEN6_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE:
> + case GEN7_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE:
> + return -EOVERFLOW;
> + case GEN6_PCODE_TIMEOUT:
> + return -ETIMEDOUT;
> + default:
> + MISSING_CASE(mbox & GEN6_PCODE_ERROR_MASK);
> + return 0;
> + }
> +}
> +
> +static inline int gen7_check_mailbox_status(u32 mbox)
> +{
> + switch (mbox & GEN6_PCODE_ERROR_MASK) {
> + case GEN6_PCODE_SUCCESS:
> + return 0;
> + case GEN6_PCODE_ILLEGAL_CMD:
> + return -ENXIO;
> + case GEN7_PCODE_TIMEOUT:
> + return -ETIMEDOUT;
> + case GEN7_PCODE_ILLEGAL_DATA:
> + return -EINVAL;
> + case GEN7_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE:
> + return -EOVERFLOW;
> + default:
> + MISSING_CASE(mbox & GEN6_PCODE_ERROR_MASK);
> + return 0;
> + }
> +}
> +
> +static int __sandybridge_pcode_rw(struct drm_i915_private *i915,
> + u32 mbox, u32 *val,
> + int fast_timeout_us,
> + int slow_timeout_ms,
> + bool is_read)
> +{
> + struct intel_uncore *uncore = &i915->uncore;
> +
> + lockdep_assert_held(&i915->sb_lock);
> +
> + /*
> + * GEN6_PCODE_* are outside of the forcewake domain, we can
> + * use te fw I915_READ variants to reduce the amount of work
> + * required when reading/writing.
> + */
> +
> + if (intel_uncore_read_fw(uncore, GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY)
> + return -EAGAIN;
> +
> + intel_uncore_write_fw(uncore, GEN6_PCODE_DATA, *val);
> + intel_uncore_write_fw(uncore, GEN6_PCODE_DATA1, 0);
> + intel_uncore_write_fw(uncore,
> + GEN6_PCODE_MAILBOX, GEN6_PCODE_READY | mbox);
> +
> + if (__intel_wait_for_register_fw(uncore,
> + GEN6_PCODE_MAILBOX,
> + GEN6_PCODE_READY, 0,
> + fast_timeout_us,
> + slow_timeout_ms,
> + &mbox))
> + return -ETIMEDOUT;
> +
> + if (is_read)
> + *val = intel_uncore_read_fw(uncore, GEN6_PCODE_DATA);
> +
> + if (INTEL_GEN(i915) > 6)
> + return gen7_check_mailbox_status(mbox);
> + else
> + return gen6_check_mailbox_status(mbox);
> +}
> +
> +int sandybridge_pcode_read(struct drm_i915_private *i915, u32 mbox, u32 *val)
> +{
> + int err;
> +
> + mutex_lock(&i915->sb_lock);
> + err = __sandybridge_pcode_rw(i915, mbox, val,
> + 500, 0,
> + true);
> + mutex_unlock(&i915->sb_lock);
> +
> + if (err) {
> + DRM_DEBUG_DRIVER("warning: pcode (read from mbox %x) mailbox access failed for %ps: %d\n",
> + mbox, __builtin_return_address(0), err);
> + }
> +
> + return err;
> +}
> +
> +int sandybridge_pcode_write_timeout(struct drm_i915_private *i915,
> + u32 mbox, u32 val,
> + int fast_timeout_us,
> + int slow_timeout_ms)
> +{
> + int err;
> +
> + mutex_lock(&i915->sb_lock);
> + err = __sandybridge_pcode_rw(i915, mbox, &val,
> + fast_timeout_us, slow_timeout_ms,
> + false);
> + mutex_unlock(&i915->sb_lock);
> +
> + if (err) {
> + DRM_DEBUG_DRIVER("warning: pcode (write of 0x%08x to mbox %x) mailbox access failed for %ps: %d\n",
> + val, mbox, __builtin_return_address(0), err);
> + }
> +
> + return err;
> +}
> +
> +static bool skl_pcode_try_request(struct drm_i915_private *i915, u32 mbox,
> + u32 request, u32 reply_mask, u32 reply,
> + u32 *status)
> +{
> + *status = __sandybridge_pcode_rw(i915, mbox, &request,
> + 500, 0,
> + true);
> +
> + return *status || ((request & reply_mask) == reply);
> +}
> +
> +/**
> + * skl_pcode_request - send PCODE request until acknowledgment
> + * @i915: device private
> + * @mbox: PCODE mailbox ID the request is targeted for
> + * @request: request ID
> + * @reply_mask: mask used to check for request acknowledgment
> + * @reply: value used to check for request acknowledgment
> + * @timeout_base_ms: timeout for polling with preemption enabled
> + *
> + * Keep resending the @request to @mbox until PCODE acknowledges it, PCODE
> + * reports an error or an overall timeout of @timeout_base_ms+50 ms expires.
> + * The request is acknowledged once the PCODE reply dword equals @reply after
> + * applying @reply_mask. Polling is first attempted with preemption enabled
> + * for @timeout_base_ms and if this times out for another 50 ms with
> + * preemption disabled.
> + *
> + * Returns 0 on success, %-ETIMEDOUT in case of a timeout, <0 in case of some
> + * other error as reported by PCODE.
> + */
> +int skl_pcode_request(struct drm_i915_private *i915, u32 mbox, u32 request,
> + u32 reply_mask, u32 reply, int timeout_base_ms)
> +{
> + u32 status;
> + int ret;
> +
> + mutex_lock(&i915->sb_lock);
> +
> +#define COND \
> + skl_pcode_try_request(i915, mbox, request, reply_mask, reply, &status)
> +
> + /*
> + * Prime the PCODE by doing a request first. Normally it guarantees
> + * that a subsequent request, at most @timeout_base_ms later, succeeds.
> + * _wait_for() doesn't guarantee when its passed condition is evaluated
> + * first, so send the first request explicitly.
> + */
> + if (COND) {
> + ret = 0;
> + goto out;
> + }
> + ret = _wait_for(COND, timeout_base_ms * 1000, 10, 10);
> + if (!ret)
> + goto out;
> +
> + /*
> + * The above can time out if the number of requests was low (2 in the
> + * worst case) _and_ PCODE was busy for some reason even after a
> + * (queued) request and @timeout_base_ms delay. As a workaround retry
> + * the poll with preemption disabled to maximize the number of
> + * requests. Increase the timeout from @timeout_base_ms to 50ms to
> + * account for interrupts that could reduce the number of these
> + * requests, and for any quirks of the PCODE firmware that delays
> + * the request completion.
> + */
> + DRM_DEBUG_KMS("PCODE timeout, retrying with preemption disabled\n");
> + WARN_ON_ONCE(timeout_base_ms > 3);
> + preempt_disable();
> + ret = wait_for_atomic(COND, 50);
> + preempt_enable();
> +
> +out:
> + mutex_unlock(&i915->sb_lock);
> + return ret ? ret : status;
> +#undef COND
> +}
> diff --git a/drivers/gpu/drm/i915/intel_sideband.h b/drivers/gpu/drm/i915/intel_sideband.h
> index 9d36bdc17955..a0907e2c4992 100644
> --- a/drivers/gpu/drm/i915/intel_sideband.h
> +++ b/drivers/gpu/drm/i915/intel_sideband.h
> @@ -127,4 +127,14 @@ u32 intel_sbi_read(struct drm_i915_private *i915, u16 reg,
> void intel_sbi_write(struct drm_i915_private *i915, u16 reg, u32 value,
> enum intel_sbi_destination destination);
>
> +int sandybridge_pcode_read(struct drm_i915_private *i915, u32 mbox, u32 *val);
> +int sandybridge_pcode_write_timeout(struct drm_i915_private *i915, u32 mbox,
> + u32 val, int fast_timeout_us,
> + int slow_timeout_ms);
> +#define sandybridge_pcode_write(i915, mbox, val) \
> + sandybridge_pcode_write_timeout(i915, mbox, val, 500, 0)
> +
> +int skl_pcode_request(struct drm_i915_private *i915, u32 mbox, u32 request,
> + u32 reply_mask, u32 reply, int timeout_base_ms);
> +
> #endif /* _INTEL_SIDEBAND_H */
> --
> 2.20.1
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
--
Ville Syrjälä
Intel
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
next prev parent reply other threads:[~2019-04-23 17:23 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-04-19 17:13 Nefarious Baytrail Chris Wilson
2019-04-19 17:13 ` [PATCH 01/10] drm/i915: Disable preemption and sleeping while using the punit sideband Chris Wilson
2019-04-23 16:49 ` Ville Syrjälä
2019-04-19 17:13 ` [PATCH 02/10] drm/i915: Lift acquiring the vlv punit magic to a common sb-get Chris Wilson
2019-04-23 16:55 ` Ville Syrjälä
2019-04-19 17:13 ` [PATCH 03/10] drm/i915: Lift sideband locking for vlv_punit_(read|write) Chris Wilson
2019-04-23 17:14 ` Ville Syrjälä
2019-04-19 17:13 ` [PATCH 04/10] drm/i915: Reduce RPS update frequency on Valleyview/Cherryview Chris Wilson
2019-04-23 17:15 ` Ville Syrjälä
2019-04-19 17:13 ` [PATCH 05/10] Revert "drm/i915: Avoid tweaking evaluation thresholds on Baytrail v3" Chris Wilson
2019-04-19 17:13 ` [PATCH 06/10] drm/i915: Replace pcu_lock with sb_lock Chris Wilson
2019-04-19 17:13 ` [PATCH 07/10] drm/i915: Separate sideband declarations to intel_sideband.h Chris Wilson
2019-04-23 17:17 ` Ville Syrjälä
2019-04-19 17:14 ` [PATCH 08/10] drm/i915: Merge sbi read/write into a single accessor Chris Wilson
2019-04-23 17:21 ` Ville Syrjälä
2019-04-19 17:14 ` [PATCH 09/10] drm/i915: Merge sandybridge_pcode_(read|write) Chris Wilson
2019-04-19 17:14 ` [PATCH 10/10] drm/i915: Move sandybride pcode access to intel_sideband.c Chris Wilson
2019-04-23 17:22 ` Ville Syrjälä [this message]
2019-04-19 17:39 ` ✗ Fi.CI.CHECKPATCH: warning for series starting with [01/10] drm/i915: Disable preemption and sleeping while using the punit sideband Patchwork
2019-04-19 17:43 ` ✗ Fi.CI.SPARSE: " Patchwork
2019-04-19 17:59 ` ✓ Fi.CI.BAT: success " Patchwork
2019-04-19 19:44 ` ✓ Fi.CI.IGT: " Patchwork
-- strict thread matches above, loose matches on Subject: below --
2018-03-07 19:41 vlv punit and sideband tidy Chris Wilson
2018-03-07 19:42 ` [PATCH 10/10] drm/i915: Move sandybride pcode access to intel_sideband.c Chris Wilson
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=20190423172259.GE1747@intel.com \
--to=ville.syrjala@linux.intel.com \
--cc=chris@chris-wilson.co.uk \
--cc=intel-gfx@lists.freedesktop.org \
/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.