From: Anthony Liguori <anthony@codemonkey.ws>
To: qemu-devel@nongnu.org
Cc: Liu Yu <yu.liu@freescale.com>, kvm-ppc@vger.kernel.org
Subject: Re: [Qemu-devel] [PATCH 3/9] powerpc/kvm: Enable mpic for E500 platform
Date: Thu, 15 Jan 2009 21:22:46 +0000 [thread overview]
Message-ID: <496FA926.1040205@codemonkey.ws> (raw)
In-Reply-To: <1232022857-2315-4-git-send-email-yu.liu@freescale.com>
Liu Yu wrote:
> The modify is based on original author's method
> to switch openpic and mpic by static define,
> like the switch between USE_INTEL_GW80314 and USE_MPCxxx.
> (Although the support for intel has broken)
> So they can't be used at the same time.
>
> I guess it's not the correct way to do this.
> but I am not sure is the USE_MPC85xx and openpic are still needed?
>
Have you tested some of the other (TCG) boards (for instance, with the
debian image Aurelien recently posted)?
Regards,
Anthony Liguori
> Signed-off-by: Liu Yu <yu.liu@freescale.com>
> ---
> hw/openpic.c | 384 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
> hw/openpic.h | 19 +++
> hw/ppc_mac.h | 14 +--
> 3 files changed, 389 insertions(+), 28 deletions(-)
> create mode 100644 hw/openpic.h
>
> diff --git a/hw/openpic.c b/hw/openpic.c
> index b8da4d7..bc5f72b 100644
> --- a/hw/openpic.c
> +++ b/hw/openpic.c
> @@ -35,6 +35,7 @@
> #include "hw.h"
> #include "ppc_mac.h"
> #include "pci.h"
> +#include "openpic.h"
>
> //#define DEBUG_OPENPIC
>
> @@ -45,7 +46,8 @@
> #endif
> #define ERROR(fmr, args...) do { printf("ERROR: " fmr , ##args); } while (0)
>
> -#define USE_MPCxxx /* Intel model is broken, for now */
> +/*#define USE_MPCxxx |+ Intel model is broken, for now +|*/
> +#define USE_MPC85xx /* Intel model is broken, for now */
>
> #if defined (USE_INTEL_GW80314)
> /* Intel GW80314 I/O Companion chip */
> @@ -84,15 +86,6 @@ enum {
> #define OPENPIC_LITTLE_ENDIAN 1
> #define OPENPIC_BIG_ENDIAN 0
>
> -#else
> -#error "Please select which OpenPic implementation is to be emulated"
> -#endif
> -
> -#if (OPENPIC_BIG_ENDIAN && !TARGET_WORDS_BIGENDIAN) || \
> - (OPENPIC_LITTLE_ENDIAN && TARGET_WORDS_BIGENDIAN)
> -#define OPENPIC_SWAP
> -#endif
> -
> /* Interrupt definitions */
> #define IRQ_FE (EXT_IRQ) /* Internal functional IRQ */
> #define IRQ_ERR (EXT_IRQ + 1) /* Error IRQ */
> @@ -105,6 +98,61 @@ enum {
> #define IRQ_MBX0 (IRQ_DBL0 + MAX_DBL) /* First mailbox IRQ */
> #endif
>
> +#elif defined(USE_MPC85xx)
> +
> +#define MPIC_MAP_SIZE 0x40000
> +
> +#define MAX_CPU 1
> +#define MAX_EXT 12
> +#define MAX_INT 64
> +#define MAX_DBL 0
> +#define MAX_MBX 0
> +#define MAX_TMR 4
> +#define MAX_MSG 4
> +#define MAX_MSI 8
> +#define MAX_IPI 4
> +#define MAX_IRQ (MAX_EXT + MAX_INT + MAX_TMR + MAX_MSG + MAX_MSI + (MAX_IPI * MAX_CPU))
> +
> +#define VECTOR_BITS 8
> +#define VID 0x0 /* MPIC version ID */
> +#define VENI 0x00000000 /* Vendor ID */
> +
> +enum {
> + IRQ_IPVP = 0,
> + IRQ_IDE,
> +};
> +
> +enum ide_bits {
> + IDR_EP = 0,
> + IDR_CI0 = 1,
> + IDR_CI1 = 2,
> + IDR_P1 = 30,
> + IDR_P0 = 31,
> +};
> +
> +#define OPENPIC_LITTLE_ENDIAN 0
> +#define OPENPIC_BIG_ENDIAN 1
> +
> +/* Interrupt definitions */
> +#define EXT_IRQ 0
> +#define INT_IRQ (EXT_IRQ + MAX_EXT)
> +#define TMR_IRQ (INT_IRQ + MAX_INT)
> +#define MSG_IRQ (TMR_IRQ + MAX_TMR)
> +#define MSI_IRQ (MSG_IRQ + MAX_MSG)
> +#define IPI_IRQ (MSI_IRQ + MAX_MSI)
> +
> +#define IRQ_IPI0 IPI_IRQ
> +#define IRQ_TIM0 TMR_IRQ
> +
> +#else
> +#error "Please select which OpenPic implementation is to be emulated"
> +#endif
> +
> +#if (OPENPIC_BIG_ENDIAN && !TARGET_WORDS_BIGENDIAN) || \
> + (OPENPIC_LITTLE_ENDIAN && TARGET_WORDS_BIGENDIAN)
> +#define OPENPIC_SWAP
> +#endif
> +
> #define BF_WIDTH(_bits_) \
> (((_bits_) + (sizeof(uint32_t) * 8) - 1) / (sizeof(uint32_t) * 8))
>
> @@ -157,6 +205,7 @@ enum IPVP_bits {
> #define IPVP_VECTOR(_ipvpr_) ((_ipvpr_) & IPVP_VECTOR_MASK)
>
> typedef struct IRQ_dst_t {
> + uint32_t tfrr;
> uint32_t pctp; /* CPU current task priority */
> uint32_t pcsr; /* CPU sensitivity register */
> IRQ_queue_t raised;
> @@ -200,6 +249,8 @@ typedef struct openpic_t {
> #endif
> /* IRQ out is used when in bypass mode (not implemented) */
> qemu_irq irq_out;
> + void (*reset) (struct openpic_t *);
> + void (*irq_raise) (struct openpic_t *, int, IRQ_src_t *);
> } openpic_t;
>
> static inline void IRQ_setbit (IRQ_queue_t *q, int n_IRQ)
> @@ -286,7 +337,7 @@ static void IRQ_local_pipe (openpic_t *opp, int n_CPU, int n_IRQ)
> return;
> }
> DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", n_CPU, n_IRQ);
> - qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_INT]);
> + opp->irq_raise(opp, n_CPU, src);
> }
>
> /* update pic state because registers for n_IRQ have changed value */
> @@ -551,8 +602,8 @@ static void openpic_gbl_write (void *opaque, uint32_t addr, uint32_t val)
> case 0x00: /* FREP */
> break;
> case 0x20: /* GLBC */
> - if (val & 0x80000000)
> - openpic_reset(opp);
> + if (val & 0x80000000 && opp->reset)
> + opp->reset(opp);
> opp->glbc = val & ~0x80000000;
> break;
> case 0x80: /* VENI */
> @@ -818,7 +869,7 @@ static void openpic_cpu_write (void *opaque, uint32_t addr, uint32_t val)
> IPVP_PRIORITY(src->ipvp) > dst->servicing.priority)) {
> DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n",
> idx, n_IRQ);
> - qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_INT]);
> + opp->irq_raise(opp, idx, src);
> }
> break;
> default:
> @@ -1001,6 +1052,11 @@ static void openpic_map(PCIDevice *pci_dev, int region_num,
> #endif
> }
>
> +static void openpic_irq_raise(openpic_t *opp, int n_CPU, IRQ_src_t *src)
> +{
> + qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
> +}
> +
> qemu_irq *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus,
> qemu_irq **irqs, qemu_irq irq_out)
> {
> @@ -1058,9 +1114,307 @@ qemu_irq *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus,
> for (i = 0; i < nb_cpus; i++)
> opp->dst[i].irqs = irqs[i];
> opp->irq_out = irq_out;
> - openpic_reset(opp);
> +
> + opp->irq_raise = openpic_irq_raise;
> + opp->reset = openpic_reset;
> +
> + opp->reset(opp);
> if (pmem_index)
> *pmem_index = opp->mem_index;
>
> return qemu_allocate_irqs(openpic_set_irq, opp, MAX_IRQ);
> }
> +
> +static void mpic_irq_raise(openpic_t *mpp, int n_CPU, IRQ_src_t *src)
> +{
> + int n_ci = IDR_CI0 - n_CPU;
> + DPRINTF("%s: cpu:%d irq:%d (testbit idr:%x ci:%d)\n", __func__,
> + n_CPU, n_IRQ, mpp->src[n_IRQ].ide, n_ci);
> + if(test_bit(&src->ide, n_ci)) {
> + qemu_irq_raise(mpp->dst[n_CPU].irqs[OPENPIC_OUTPUT_CINT]);
> + }
> + else {
> + qemu_irq_raise(mpp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
> + }
> +}
> +
> +static void mpic_reset (openpic_t *mpp)
> +{
> + int i;
> +
> + mpp->glbc = 0x80000000;
> + /* Initialise controller registers */
> + mpp->frep = 0x004f0002;
> + mpp->veni = VENI;
> + mpp->pint = 0x00000000;
> + mpp->spve = 0x0000FFFF;
> + /* Initialise IRQ sources */
> + for (i = 0; i < MAX_IRQ; i++) {
> + mpp->src[i].ipvp = 0x80800000;
> + mpp->src[i].ide = 0x00000001;
> + }
> + /* Initialise IRQ destinations */
> + for (i = 0; i < MAX_CPU; i++) {
> + mpp->dst[i].pctp = 0x0000000F;
> + mpp->dst[i].tfrr = 0x00000000;
> + memset(&mpp->dst[i].raised, 0, sizeof(IRQ_queue_t));
> + mpp->dst[i].raised.next = -1;
> + memset(&mpp->dst[i].servicing, 0, sizeof(IRQ_queue_t));
> + mpp->dst[i].servicing.next = -1;
> + }
> + /* Initialise timers */
> + for (i = 0; i < MAX_TMR; i++) {
> + mpp->timers[i].ticc = 0x00000000;
> + mpp->timers[i].tibc = 0x80000000;
> + }
> + /* Go out of RESET state */
> + mpp->glbc = 0x00000000;
> +}
> +
> +static void mpic_timer_write (void *opaque, uint32_t addr, uint32_t val)
> +{
> + openpic_t *mpp = opaque;
> + int idx, cpu;
> +
> + DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
> + if (addr & 0xF)
> + return;
> +#if defined MPIC_SWAP
> + val = bswap32(val);
> +#endif
> + addr &= 0xFFFF;
> + cpu = addr >> 12;
> + idx = (addr >> 6) & 0x3;
> + switch (addr & 0x30) {
> + case 0x00: /* gtccr */
> + break;
> + case 0x10: /* gtbcr */
> + if ((mpp->timers[idx].ticc & 0x80000000) != 0 &&
> + (val & 0x80000000) = 0 &&
> + (mpp->timers[idx].tibc & 0x80000000) != 0)
> + mpp->timers[idx].ticc &= ~0x80000000;
> + mpp->timers[idx].tibc = val;
> + break;
> + case 0x20: /* GTIVPR */
> + write_IRQreg(mpp, TMR_IRQ + idx, IRQ_IPVP, val);
> + break;
> + case 0x30: /* GTIDR & TFRR */
> + if ((addr & 0xF0) = 0xF0)
> + mpp->dst[cpu].tfrr = val;
> + else
> + write_IRQreg(mpp, TMR_IRQ + idx, IRQ_IDE, val);
> + break;
> + }
> +}
> +
> +static uint32_t mpic_timer_read (void *opaque, uint32_t addr)
> +{
> + openpic_t *mpp = opaque;
> + uint32_t retval;
> + int idx, cpu;
> +
> + DPRINTF("%s: addr %08x\n", __func__, addr);
> + retval = 0xFFFFFFFF;
> + if (addr & 0xF)
> + return retval;
> + addr &= 0xFFFF;
> + cpu = addr >> 12;
> + idx = (addr >> 6) & 0x3;
> + switch (addr & 0x30) {
> + case 0x00: /* gtccr */
> + retval = mpp->timers[idx].ticc;
> + break;
> + case 0x10: /* gtbcr */
> + retval = mpp->timers[idx].tibc;
> + break;
> + case 0x20: /* TIPV */
> + retval = read_IRQreg(mpp, TMR_IRQ + idx, IRQ_IPVP);
> + break;
> + case 0x30: /* TIDR */
> + if ((addr &0xF0) = 0XF0)
> + retval = mpp->dst[cpu].tfrr;
> + else
> + retval = read_IRQreg(mpp, TMR_IRQ + idx, IRQ_IDE);
> + break;
> + }
> + DPRINTF("%s: => %08x\n", __func__, retval);
> +#if defined MPIC_SWAP
> + retval = bswap32(retval);
> +#endif
> +
> + return retval;
> +}
> +
> +static void mpic_src_write (void *opaque, uint32_t addr, uint32_t val)
> +{
> + openpic_t *mpp = opaque;
> + int idx;
> +
> + DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
> + if (addr & 0xF)
> + return;
> +#if defined MPIC_SWAP
> + val = tswap32(val);
> +#endif
> + addr = addr & 0xFFF0;
> + if (addr < 0x180) {
> + idx = EXT_IRQ;
> + } else if (addr >= 0x200 && addr < 0xa00) {
> + idx = INT_IRQ;
> + addr -= 0x200;
> + } else if (addr >= 0x1600 && addr < 0x1700) {
> + idx = MSG_IRQ;
> + addr -= 0x1600;
> + } else if (addr >= 0x1C00 && addr < 0x1D00) {
> + idx = MSI_IRQ;
> + addr -= 0x1C00;
> + } else {
> + return;
> + }
> + idx += addr >> 5;
> + if (addr & 0x10) {
> + /* EXDE / IFEDE / IEEDE */
> + write_IRQreg(mpp, idx, IRQ_IDE, val);
> + } else {
> + /* EXVP / IFEVP / IEEVP */
> + write_IRQreg(mpp, idx, IRQ_IPVP, val);
> + }
> +}
> +
> +static uint32_t mpic_src_read (void *opaque, uint32_t addr)
> +{
> + openpic_t *mpp = opaque;
> + uint32_t retval;
> + int idx;
> +
> + DPRINTF("%s: addr %08x\n", __func__, addr);
> + retval = 0xFFFFFFFF;
> + if (addr & 0xF)
> + return retval;
> + addr = addr & 0xFFF0;
> + if (addr < 0x180) {
> + idx = EXT_IRQ;
> + } else if (addr >= 0x200 && addr < 0xa00) {
> + idx = INT_IRQ;
> + addr -= 0x200;
> + } else if (addr >= 0x1600 && addr < 0x1700) {
> + idx = MSG_IRQ;
> + addr -= 0x1600;
> + } else if (addr >= 0x1C00 && addr < 0x1D00) {
> + idx = MSI_IRQ;
> + addr -= 0x1C00;
> + } else {
> + return retval;
> + }
> + idx += addr >> 5;
> + if (addr & 0x10) {
> + /* EXDE / IFEDE / IEEDE */
> + retval = read_IRQreg(mpp, idx, IRQ_IDE);
> + } else {
> + /* EXVP / IFEVP / IEEVP */
> + retval = read_IRQreg(mpp, idx, IRQ_IPVP);
> + }
> + DPRINTF("%s: => %08x\n", __func__, retval);
> +#if defined MPIC_SWAP
> + retval = tswap32(retval);
> +#endif
> +
> + return retval;
> +}
> +
> +static void mpic_writel (void *opaque,
> + target_phys_addr_t addr, uint32_t val)
> +{
> + openpic_t *mpp = opaque;
> +
> + addr &= 0x3FFFF;
> + DPRINTF("%s: offset %08x val: %08x\n", __func__, (int)addr, val);
> + if (addr < 0x10F0) {
> + /* Global registers */
> + openpic_gbl_write(mpp, addr, val);
> + } else if (addr < 0x10000) {
> + /* Timers registers */
> + mpic_timer_write(mpp, addr, val);
> + } else if (addr < 0x20000) {
> + /* Source registers */
> + mpic_src_write(mpp, addr, val);
> + } else if (addr < 0x30000){
> + /* CPU registers */
> + openpic_cpu_write(mpp, addr, val);
> + } else {
> + DPRINTF("wrong mpic write addr %p\n",addr);
> + }
> +}
> +
> +static uint32_t mpic_readl (void *opaque,target_phys_addr_t addr)
> +{
> + openpic_t *mpp = opaque;
> + uint32_t retval = 0;
> +
> + addr &= 0x3FFFF;
> + DPRINTF("%s: offset %08x\n", __func__, (int)addr);
> + if (addr < 0x10F0) {
> + /* Global registers */
> + retval = openpic_gbl_read(mpp, addr);
> + } else if (addr < 0x10000) {
> + /* Timers registers */
> + retval = mpic_timer_read(mpp, addr);
> + } else if (addr < 0x20000) {
> + /* Source registers */
> + retval = mpic_src_read(mpp, addr);
> + } else if (addr < 0x30000){
> + /* CPU registers */
> + retval = openpic_cpu_read(mpp, addr);
> + } else {
> + DPRINTF("wrong mpic read addr %p\n",addr);
> + }
> +
> + return retval;
> +}
> +
> +static CPUWriteMemoryFunc *mpic_write[] = {
> + &openpic_buggy_write,
> + &openpic_buggy_write,
> + &mpic_writel,
> +};
> +
> +static CPUReadMemoryFunc *mpic_read[] = {
> + &openpic_buggy_read,
> + &openpic_buggy_read,
> + &mpic_readl,
> +};
> +
> +qemu_irq *mpic_init (target_phys_addr_t base, int nb_cpus,
> + qemu_irq **irqs, qemu_irq irq_out)
> +{
> + openpic_t *mpp;
> + int i;
> +
> + /* XXX: for now, only one CPU is supported */
> + if (nb_cpus != 1)
> + return NULL;
> +
> + mpp = qemu_mallocz(sizeof(openpic_t));
> +
> + mpp->mem_index = cpu_register_io_memory(0, mpic_read, mpic_write, mpp);
> + if (mpp->mem_index < 0)
> + goto free;
> + cpu_register_physical_memory(base, MPIC_MAP_SIZE, mpp->mem_index);
> +
> + mpp->nb_cpus = nb_cpus;
> +
> + for (i = 0; i < nb_cpus; i++)
> + mpp->dst[i].irqs = irqs[i];
> + mpp->irq_out = irq_out;
> +
> + mpp->irq_raise = mpic_irq_raise;
> + mpp->reset = mpic_reset;
> +
> + mpp->reset(mpp);
> +
> + return qemu_allocate_irqs(openpic_set_irq, mpp, MAX_IRQ);
> +
> +free:
> + qemu_free(mpp);
> + return NULL;
> +}
> diff --git a/hw/openpic.h b/hw/openpic.h
> new file mode 100644
> index 0000000..2f85e16
> --- /dev/null
> +++ b/hw/openpic.h
> @@ -0,0 +1,19 @@
> +
> +#if !defined(OPENPIC_H)
> +#define OPENPIC_H
> +
> +/* OpenPIC have 5 outputs per CPU connected and one IRQ out single output */
> +enum {
> + OPENPIC_OUTPUT_INT = 0, /* IRQ */
> + OPENPIC_OUTPUT_CINT, /* critical IRQ */
> + OPENPIC_OUTPUT_MCK, /* Machine check event */
> + OPENPIC_OUTPUT_DEBUG, /* Inconditional debug event */
> + OPENPIC_OUTPUT_RESET, /* Core reset event */
> + OPENPIC_OUTPUT_NB,
> +};
> +
> +qemu_irq *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus,
> + qemu_irq **irqs, qemu_irq irq_out);
> +qemu_irq *mpic_init (target_phys_addr_t base, int nb_cpus,
> + qemu_irq **irqs, qemu_irq irq_out);
> +#endif /* OPENPIC_H */
> diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h
> index cc70fb7..bf987b1 100644
> --- a/hw/ppc_mac.h
> +++ b/hw/ppc_mac.h
> @@ -113,17 +113,5 @@ void adb_mouse_init(ADBBusState *bus);
>
> extern ADBBusState adb_bus;
>
> -/* openpic.c */
> -/* OpenPIC have 5 outputs per CPU connected and one IRQ out single output */
> -enum {
> - OPENPIC_OUTPUT_INT = 0, /* IRQ */
> - OPENPIC_OUTPUT_CINT, /* critical IRQ */
> - OPENPIC_OUTPUT_MCK, /* Machine check event */
> - OPENPIC_OUTPUT_DEBUG, /* Inconditional debug event */
> - OPENPIC_OUTPUT_RESET, /* Core reset event */
> - OPENPIC_OUTPUT_NB,
> -};
> -qemu_irq *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus,
> - qemu_irq **irqs, qemu_irq irq_out);
> -
> +#include "openpic.h"
> #endif /* !defined(__PPC_MAC_H__) */
>
WARNING: multiple messages have this Message-ID (diff)
From: Anthony Liguori <anthony@codemonkey.ws>
To: qemu-devel@nongnu.org
Cc: Liu Yu <yu.liu@freescale.com>, kvm-ppc@vger.kernel.org
Subject: Re: [Qemu-devel] [PATCH 3/9] powerpc/kvm: Enable mpic for E500 platform
Date: Thu, 15 Jan 2009 15:22:46 -0600 [thread overview]
Message-ID: <496FA926.1040205@codemonkey.ws> (raw)
In-Reply-To: <1232022857-2315-4-git-send-email-yu.liu@freescale.com>
Liu Yu wrote:
> The modify is based on original author's method
> to switch openpic and mpic by static define,
> like the switch between USE_INTEL_GW80314 and USE_MPCxxx.
> (Although the support for intel has broken)
> So they can't be used at the same time.
>
> I guess it's not the correct way to do this.
> but I am not sure is the USE_MPC85xx and openpic are still needed?
>
Have you tested some of the other (TCG) boards (for instance, with the
debian image Aurelien recently posted)?
Regards,
Anthony Liguori
> Signed-off-by: Liu Yu <yu.liu@freescale.com>
> ---
> hw/openpic.c | 384 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
> hw/openpic.h | 19 +++
> hw/ppc_mac.h | 14 +--
> 3 files changed, 389 insertions(+), 28 deletions(-)
> create mode 100644 hw/openpic.h
>
> diff --git a/hw/openpic.c b/hw/openpic.c
> index b8da4d7..bc5f72b 100644
> --- a/hw/openpic.c
> +++ b/hw/openpic.c
> @@ -35,6 +35,7 @@
> #include "hw.h"
> #include "ppc_mac.h"
> #include "pci.h"
> +#include "openpic.h"
>
> //#define DEBUG_OPENPIC
>
> @@ -45,7 +46,8 @@
> #endif
> #define ERROR(fmr, args...) do { printf("ERROR: " fmr , ##args); } while (0)
>
> -#define USE_MPCxxx /* Intel model is broken, for now */
> +/*#define USE_MPCxxx |+ Intel model is broken, for now +|*/
> +#define USE_MPC85xx /* Intel model is broken, for now */
>
> #if defined (USE_INTEL_GW80314)
> /* Intel GW80314 I/O Companion chip */
> @@ -84,15 +86,6 @@ enum {
> #define OPENPIC_LITTLE_ENDIAN 1
> #define OPENPIC_BIG_ENDIAN 0
>
> -#else
> -#error "Please select which OpenPic implementation is to be emulated"
> -#endif
> -
> -#if (OPENPIC_BIG_ENDIAN && !TARGET_WORDS_BIGENDIAN) || \
> - (OPENPIC_LITTLE_ENDIAN && TARGET_WORDS_BIGENDIAN)
> -#define OPENPIC_SWAP
> -#endif
> -
> /* Interrupt definitions */
> #define IRQ_FE (EXT_IRQ) /* Internal functional IRQ */
> #define IRQ_ERR (EXT_IRQ + 1) /* Error IRQ */
> @@ -105,6 +98,61 @@ enum {
> #define IRQ_MBX0 (IRQ_DBL0 + MAX_DBL) /* First mailbox IRQ */
> #endif
>
> +#elif defined(USE_MPC85xx)
> +
> +#define MPIC_MAP_SIZE 0x40000
> +
> +#define MAX_CPU 1
> +#define MAX_EXT 12
> +#define MAX_INT 64
> +#define MAX_DBL 0
> +#define MAX_MBX 0
> +#define MAX_TMR 4
> +#define MAX_MSG 4
> +#define MAX_MSI 8
> +#define MAX_IPI 4
> +#define MAX_IRQ (MAX_EXT + MAX_INT + MAX_TMR + MAX_MSG + MAX_MSI + (MAX_IPI * MAX_CPU))
> +
> +#define VECTOR_BITS 8
> +#define VID 0x0 /* MPIC version ID */
> +#define VENI 0x00000000 /* Vendor ID */
> +
> +enum {
> + IRQ_IPVP = 0,
> + IRQ_IDE,
> +};
> +
> +enum ide_bits {
> + IDR_EP = 0,
> + IDR_CI0 = 1,
> + IDR_CI1 = 2,
> + IDR_P1 = 30,
> + IDR_P0 = 31,
> +};
> +
> +#define OPENPIC_LITTLE_ENDIAN 0
> +#define OPENPIC_BIG_ENDIAN 1
> +
> +/* Interrupt definitions */
> +#define EXT_IRQ 0
> +#define INT_IRQ (EXT_IRQ + MAX_EXT)
> +#define TMR_IRQ (INT_IRQ + MAX_INT)
> +#define MSG_IRQ (TMR_IRQ + MAX_TMR)
> +#define MSI_IRQ (MSG_IRQ + MAX_MSG)
> +#define IPI_IRQ (MSI_IRQ + MAX_MSI)
> +
> +#define IRQ_IPI0 IPI_IRQ
> +#define IRQ_TIM0 TMR_IRQ
> +
> +#else
> +#error "Please select which OpenPic implementation is to be emulated"
> +#endif
> +
> +#if (OPENPIC_BIG_ENDIAN && !TARGET_WORDS_BIGENDIAN) || \
> + (OPENPIC_LITTLE_ENDIAN && TARGET_WORDS_BIGENDIAN)
> +#define OPENPIC_SWAP
> +#endif
> +
> #define BF_WIDTH(_bits_) \
> (((_bits_) + (sizeof(uint32_t) * 8) - 1) / (sizeof(uint32_t) * 8))
>
> @@ -157,6 +205,7 @@ enum IPVP_bits {
> #define IPVP_VECTOR(_ipvpr_) ((_ipvpr_) & IPVP_VECTOR_MASK)
>
> typedef struct IRQ_dst_t {
> + uint32_t tfrr;
> uint32_t pctp; /* CPU current task priority */
> uint32_t pcsr; /* CPU sensitivity register */
> IRQ_queue_t raised;
> @@ -200,6 +249,8 @@ typedef struct openpic_t {
> #endif
> /* IRQ out is used when in bypass mode (not implemented) */
> qemu_irq irq_out;
> + void (*reset) (struct openpic_t *);
> + void (*irq_raise) (struct openpic_t *, int, IRQ_src_t *);
> } openpic_t;
>
> static inline void IRQ_setbit (IRQ_queue_t *q, int n_IRQ)
> @@ -286,7 +337,7 @@ static void IRQ_local_pipe (openpic_t *opp, int n_CPU, int n_IRQ)
> return;
> }
> DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", n_CPU, n_IRQ);
> - qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_INT]);
> + opp->irq_raise(opp, n_CPU, src);
> }
>
> /* update pic state because registers for n_IRQ have changed value */
> @@ -551,8 +602,8 @@ static void openpic_gbl_write (void *opaque, uint32_t addr, uint32_t val)
> case 0x00: /* FREP */
> break;
> case 0x20: /* GLBC */
> - if (val & 0x80000000)
> - openpic_reset(opp);
> + if (val & 0x80000000 && opp->reset)
> + opp->reset(opp);
> opp->glbc = val & ~0x80000000;
> break;
> case 0x80: /* VENI */
> @@ -818,7 +869,7 @@ static void openpic_cpu_write (void *opaque, uint32_t addr, uint32_t val)
> IPVP_PRIORITY(src->ipvp) > dst->servicing.priority)) {
> DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n",
> idx, n_IRQ);
> - qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_INT]);
> + opp->irq_raise(opp, idx, src);
> }
> break;
> default:
> @@ -1001,6 +1052,11 @@ static void openpic_map(PCIDevice *pci_dev, int region_num,
> #endif
> }
>
> +static void openpic_irq_raise(openpic_t *opp, int n_CPU, IRQ_src_t *src)
> +{
> + qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
> +}
> +
> qemu_irq *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus,
> qemu_irq **irqs, qemu_irq irq_out)
> {
> @@ -1058,9 +1114,307 @@ qemu_irq *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus,
> for (i = 0; i < nb_cpus; i++)
> opp->dst[i].irqs = irqs[i];
> opp->irq_out = irq_out;
> - openpic_reset(opp);
> +
> + opp->irq_raise = openpic_irq_raise;
> + opp->reset = openpic_reset;
> +
> + opp->reset(opp);
> if (pmem_index)
> *pmem_index = opp->mem_index;
>
> return qemu_allocate_irqs(openpic_set_irq, opp, MAX_IRQ);
> }
> +
> +static void mpic_irq_raise(openpic_t *mpp, int n_CPU, IRQ_src_t *src)
> +{
> + int n_ci = IDR_CI0 - n_CPU;
> + DPRINTF("%s: cpu:%d irq:%d (testbit idr:%x ci:%d)\n", __func__,
> + n_CPU, n_IRQ, mpp->src[n_IRQ].ide, n_ci);
> + if(test_bit(&src->ide, n_ci)) {
> + qemu_irq_raise(mpp->dst[n_CPU].irqs[OPENPIC_OUTPUT_CINT]);
> + }
> + else {
> + qemu_irq_raise(mpp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
> + }
> +}
> +
> +static void mpic_reset (openpic_t *mpp)
> +{
> + int i;
> +
> + mpp->glbc = 0x80000000;
> + /* Initialise controller registers */
> + mpp->frep = 0x004f0002;
> + mpp->veni = VENI;
> + mpp->pint = 0x00000000;
> + mpp->spve = 0x0000FFFF;
> + /* Initialise IRQ sources */
> + for (i = 0; i < MAX_IRQ; i++) {
> + mpp->src[i].ipvp = 0x80800000;
> + mpp->src[i].ide = 0x00000001;
> + }
> + /* Initialise IRQ destinations */
> + for (i = 0; i < MAX_CPU; i++) {
> + mpp->dst[i].pctp = 0x0000000F;
> + mpp->dst[i].tfrr = 0x00000000;
> + memset(&mpp->dst[i].raised, 0, sizeof(IRQ_queue_t));
> + mpp->dst[i].raised.next = -1;
> + memset(&mpp->dst[i].servicing, 0, sizeof(IRQ_queue_t));
> + mpp->dst[i].servicing.next = -1;
> + }
> + /* Initialise timers */
> + for (i = 0; i < MAX_TMR; i++) {
> + mpp->timers[i].ticc = 0x00000000;
> + mpp->timers[i].tibc = 0x80000000;
> + }
> + /* Go out of RESET state */
> + mpp->glbc = 0x00000000;
> +}
> +
> +static void mpic_timer_write (void *opaque, uint32_t addr, uint32_t val)
> +{
> + openpic_t *mpp = opaque;
> + int idx, cpu;
> +
> + DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
> + if (addr & 0xF)
> + return;
> +#if defined MPIC_SWAP
> + val = bswap32(val);
> +#endif
> + addr &= 0xFFFF;
> + cpu = addr >> 12;
> + idx = (addr >> 6) & 0x3;
> + switch (addr & 0x30) {
> + case 0x00: /* gtccr */
> + break;
> + case 0x10: /* gtbcr */
> + if ((mpp->timers[idx].ticc & 0x80000000) != 0 &&
> + (val & 0x80000000) == 0 &&
> + (mpp->timers[idx].tibc & 0x80000000) != 0)
> + mpp->timers[idx].ticc &= ~0x80000000;
> + mpp->timers[idx].tibc = val;
> + break;
> + case 0x20: /* GTIVPR */
> + write_IRQreg(mpp, TMR_IRQ + idx, IRQ_IPVP, val);
> + break;
> + case 0x30: /* GTIDR & TFRR */
> + if ((addr & 0xF0) == 0xF0)
> + mpp->dst[cpu].tfrr = val;
> + else
> + write_IRQreg(mpp, TMR_IRQ + idx, IRQ_IDE, val);
> + break;
> + }
> +}
> +
> +static uint32_t mpic_timer_read (void *opaque, uint32_t addr)
> +{
> + openpic_t *mpp = opaque;
> + uint32_t retval;
> + int idx, cpu;
> +
> + DPRINTF("%s: addr %08x\n", __func__, addr);
> + retval = 0xFFFFFFFF;
> + if (addr & 0xF)
> + return retval;
> + addr &= 0xFFFF;
> + cpu = addr >> 12;
> + idx = (addr >> 6) & 0x3;
> + switch (addr & 0x30) {
> + case 0x00: /* gtccr */
> + retval = mpp->timers[idx].ticc;
> + break;
> + case 0x10: /* gtbcr */
> + retval = mpp->timers[idx].tibc;
> + break;
> + case 0x20: /* TIPV */
> + retval = read_IRQreg(mpp, TMR_IRQ + idx, IRQ_IPVP);
> + break;
> + case 0x30: /* TIDR */
> + if ((addr &0xF0) == 0XF0)
> + retval = mpp->dst[cpu].tfrr;
> + else
> + retval = read_IRQreg(mpp, TMR_IRQ + idx, IRQ_IDE);
> + break;
> + }
> + DPRINTF("%s: => %08x\n", __func__, retval);
> +#if defined MPIC_SWAP
> + retval = bswap32(retval);
> +#endif
> +
> + return retval;
> +}
> +
> +static void mpic_src_write (void *opaque, uint32_t addr, uint32_t val)
> +{
> + openpic_t *mpp = opaque;
> + int idx;
> +
> + DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
> + if (addr & 0xF)
> + return;
> +#if defined MPIC_SWAP
> + val = tswap32(val);
> +#endif
> + addr = addr & 0xFFF0;
> + if (addr < 0x180) {
> + idx = EXT_IRQ;
> + } else if (addr >= 0x200 && addr < 0xa00) {
> + idx = INT_IRQ;
> + addr -= 0x200;
> + } else if (addr >= 0x1600 && addr < 0x1700) {
> + idx = MSG_IRQ;
> + addr -= 0x1600;
> + } else if (addr >= 0x1C00 && addr < 0x1D00) {
> + idx = MSI_IRQ;
> + addr -= 0x1C00;
> + } else {
> + return;
> + }
> + idx += addr >> 5;
> + if (addr & 0x10) {
> + /* EXDE / IFEDE / IEEDE */
> + write_IRQreg(mpp, idx, IRQ_IDE, val);
> + } else {
> + /* EXVP / IFEVP / IEEVP */
> + write_IRQreg(mpp, idx, IRQ_IPVP, val);
> + }
> +}
> +
> +static uint32_t mpic_src_read (void *opaque, uint32_t addr)
> +{
> + openpic_t *mpp = opaque;
> + uint32_t retval;
> + int idx;
> +
> + DPRINTF("%s: addr %08x\n", __func__, addr);
> + retval = 0xFFFFFFFF;
> + if (addr & 0xF)
> + return retval;
> + addr = addr & 0xFFF0;
> + if (addr < 0x180) {
> + idx = EXT_IRQ;
> + } else if (addr >= 0x200 && addr < 0xa00) {
> + idx = INT_IRQ;
> + addr -= 0x200;
> + } else if (addr >= 0x1600 && addr < 0x1700) {
> + idx = MSG_IRQ;
> + addr -= 0x1600;
> + } else if (addr >= 0x1C00 && addr < 0x1D00) {
> + idx = MSI_IRQ;
> + addr -= 0x1C00;
> + } else {
> + return retval;
> + }
> + idx += addr >> 5;
> + if (addr & 0x10) {
> + /* EXDE / IFEDE / IEEDE */
> + retval = read_IRQreg(mpp, idx, IRQ_IDE);
> + } else {
> + /* EXVP / IFEVP / IEEVP */
> + retval = read_IRQreg(mpp, idx, IRQ_IPVP);
> + }
> + DPRINTF("%s: => %08x\n", __func__, retval);
> +#if defined MPIC_SWAP
> + retval = tswap32(retval);
> +#endif
> +
> + return retval;
> +}
> +
> +static void mpic_writel (void *opaque,
> + target_phys_addr_t addr, uint32_t val)
> +{
> + openpic_t *mpp = opaque;
> +
> + addr &= 0x3FFFF;
> + DPRINTF("%s: offset %08x val: %08x\n", __func__, (int)addr, val);
> + if (addr < 0x10F0) {
> + /* Global registers */
> + openpic_gbl_write(mpp, addr, val);
> + } else if (addr < 0x10000) {
> + /* Timers registers */
> + mpic_timer_write(mpp, addr, val);
> + } else if (addr < 0x20000) {
> + /* Source registers */
> + mpic_src_write(mpp, addr, val);
> + } else if (addr < 0x30000){
> + /* CPU registers */
> + openpic_cpu_write(mpp, addr, val);
> + } else {
> + DPRINTF("wrong mpic write addr %p\n",addr);
> + }
> +}
> +
> +static uint32_t mpic_readl (void *opaque,target_phys_addr_t addr)
> +{
> + openpic_t *mpp = opaque;
> + uint32_t retval = 0;
> +
> + addr &= 0x3FFFF;
> + DPRINTF("%s: offset %08x\n", __func__, (int)addr);
> + if (addr < 0x10F0) {
> + /* Global registers */
> + retval = openpic_gbl_read(mpp, addr);
> + } else if (addr < 0x10000) {
> + /* Timers registers */
> + retval = mpic_timer_read(mpp, addr);
> + } else if (addr < 0x20000) {
> + /* Source registers */
> + retval = mpic_src_read(mpp, addr);
> + } else if (addr < 0x30000){
> + /* CPU registers */
> + retval = openpic_cpu_read(mpp, addr);
> + } else {
> + DPRINTF("wrong mpic read addr %p\n",addr);
> + }
> +
> + return retval;
> +}
> +
> +static CPUWriteMemoryFunc *mpic_write[] = {
> + &openpic_buggy_write,
> + &openpic_buggy_write,
> + &mpic_writel,
> +};
> +
> +static CPUReadMemoryFunc *mpic_read[] = {
> + &openpic_buggy_read,
> + &openpic_buggy_read,
> + &mpic_readl,
> +};
> +
> +qemu_irq *mpic_init (target_phys_addr_t base, int nb_cpus,
> + qemu_irq **irqs, qemu_irq irq_out)
> +{
> + openpic_t *mpp;
> + int i;
> +
> + /* XXX: for now, only one CPU is supported */
> + if (nb_cpus != 1)
> + return NULL;
> +
> + mpp = qemu_mallocz(sizeof(openpic_t));
> +
> + mpp->mem_index = cpu_register_io_memory(0, mpic_read, mpic_write, mpp);
> + if (mpp->mem_index < 0)
> + goto free;
> + cpu_register_physical_memory(base, MPIC_MAP_SIZE, mpp->mem_index);
> +
> + mpp->nb_cpus = nb_cpus;
> +
> + for (i = 0; i < nb_cpus; i++)
> + mpp->dst[i].irqs = irqs[i];
> + mpp->irq_out = irq_out;
> +
> + mpp->irq_raise = mpic_irq_raise;
> + mpp->reset = mpic_reset;
> +
> + mpp->reset(mpp);
> +
> + return qemu_allocate_irqs(openpic_set_irq, mpp, MAX_IRQ);
> +
> +free:
> + qemu_free(mpp);
> + return NULL;
> +}
> diff --git a/hw/openpic.h b/hw/openpic.h
> new file mode 100644
> index 0000000..2f85e16
> --- /dev/null
> +++ b/hw/openpic.h
> @@ -0,0 +1,19 @@
> +
> +#if !defined(OPENPIC_H)
> +#define OPENPIC_H
> +
> +/* OpenPIC have 5 outputs per CPU connected and one IRQ out single output */
> +enum {
> + OPENPIC_OUTPUT_INT = 0, /* IRQ */
> + OPENPIC_OUTPUT_CINT, /* critical IRQ */
> + OPENPIC_OUTPUT_MCK, /* Machine check event */
> + OPENPIC_OUTPUT_DEBUG, /* Inconditional debug event */
> + OPENPIC_OUTPUT_RESET, /* Core reset event */
> + OPENPIC_OUTPUT_NB,
> +};
> +
> +qemu_irq *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus,
> + qemu_irq **irqs, qemu_irq irq_out);
> +qemu_irq *mpic_init (target_phys_addr_t base, int nb_cpus,
> + qemu_irq **irqs, qemu_irq irq_out);
> +#endif /* OPENPIC_H */
> diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h
> index cc70fb7..bf987b1 100644
> --- a/hw/ppc_mac.h
> +++ b/hw/ppc_mac.h
> @@ -113,17 +113,5 @@ void adb_mouse_init(ADBBusState *bus);
>
> extern ADBBusState adb_bus;
>
> -/* openpic.c */
> -/* OpenPIC have 5 outputs per CPU connected and one IRQ out single output */
> -enum {
> - OPENPIC_OUTPUT_INT = 0, /* IRQ */
> - OPENPIC_OUTPUT_CINT, /* critical IRQ */
> - OPENPIC_OUTPUT_MCK, /* Machine check event */
> - OPENPIC_OUTPUT_DEBUG, /* Inconditional debug event */
> - OPENPIC_OUTPUT_RESET, /* Core reset event */
> - OPENPIC_OUTPUT_NB,
> -};
> -qemu_irq *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus,
> - qemu_irq **irqs, qemu_irq irq_out);
> -
> +#include "openpic.h"
> #endif /* !defined(__PPC_MAC_H__) */
>
next prev parent reply other threads:[~2009-01-15 21:22 UTC|newest]
Thread overview: 52+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-01-15 12:34 [PATCH 0/9] powerpc/kvm: Add MPC85xx platform support Liu Yu
2009-01-15 12:34 ` [Qemu-devel] " Liu Yu
2009-01-15 12:34 ` [PATCH 1/9] powerpc/kvm: Fix a uninitialized bug Liu Yu
2009-01-15 12:34 ` [Qemu-devel] " Liu Yu
2009-01-15 12:34 ` [PATCH 2/9] powerpc/kvm: fix a openpic bug Liu Yu
2009-01-15 12:34 ` [Qemu-devel] " Liu Yu
2009-01-15 12:34 ` [PATCH 3/9] powerpc/kvm: Enable mpic for E500 platform Liu Yu
2009-01-15 12:34 ` [Qemu-devel] " Liu Yu
2009-01-15 12:34 ` [PATCH 4/9] powerpc/kvm: enable POWERPC_MMU_BOOKE_FSL when kvm is enabled Liu Yu
2009-01-15 12:34 ` [Qemu-devel] " Liu Yu
2009-01-15 12:34 ` [PATCH 5/9] powerpc/kvm: Add freescale pci controller's support Liu Yu
2009-01-15 12:34 ` [Qemu-devel] " Liu Yu
2009-01-15 12:34 ` [PATCH 6/9] powerpc/kvm: Add E500 irq support Liu Yu
2009-01-15 12:34 ` [Qemu-devel] " Liu Yu
2009-01-15 12:34 ` [PATCH 7/9] powerpc/kvm: Add E500 core emulation Liu Yu
2009-01-15 12:34 ` [Qemu-devel] " Liu Yu
2009-01-15 12:34 ` [PATCH 8/9] powerpc/kvm: extern one function for E500 code use Liu Yu
2009-01-15 12:34 ` [Qemu-devel] " Liu Yu
[not found] ` <1232022857-2315-10-git-send-email-yu.liu@freescale.com>
2009-01-16 8:22 ` [PATCH 9/9] powerpc/kvm: Add MPC85xx board support Liu Yu
2009-01-16 8:22 ` [Qemu-devel] " Liu Yu
2009-01-16 18:09 ` Hollis Blanchard
2009-01-16 18:09 ` [Qemu-devel] " Hollis Blanchard
2009-01-20 3:09 ` Liu Yu
2009-01-20 3:09 ` [Qemu-devel] " Liu Yu
2009-01-20 17:23 ` Hollis Blanchard
2009-01-20 17:23 ` [Qemu-devel] " Hollis Blanchard
2009-01-15 19:53 ` [PATCH 7/9] powerpc/kvm: Add E500 core emulation Hollis Blanchard
2009-01-15 19:53 ` [Qemu-devel] " Hollis Blanchard
2009-01-16 7:51 ` Liu Yu
2009-01-16 7:51 ` [Qemu-devel] " Liu Yu
2009-01-16 18:02 ` Hollis Blanchard
2009-01-16 18:02 ` [Qemu-devel] " Hollis Blanchard
2009-01-19 10:54 ` Liu Yu
2009-01-19 10:54 ` [Qemu-devel] " Liu Yu
2009-01-19 10:59 ` Liu Yu
2009-01-19 10:59 ` [Qemu-devel] " Liu Yu
2009-01-15 20:02 ` [PATCH 5/9] powerpc/kvm: Add freescale pci controller's support Hollis Blanchard
2009-01-15 20:02 ` [Qemu-devel] " Hollis Blanchard
2009-01-16 7:37 ` Liu Yu
2009-01-16 7:37 ` [Qemu-devel] " Liu Yu
2009-01-15 21:22 ` Anthony Liguori [this message]
2009-01-15 21:22 ` [Qemu-devel] [PATCH 3/9] powerpc/kvm: Enable mpic for E500 platform Anthony Liguori
2009-01-16 5:34 ` Liu Yu
2009-01-16 5:34 ` Liu Yu
2009-01-16 18:17 ` [Qemu-devel] [PATCH 3/9] powerpc/kvm: Enable mpic for E500 Hollis Blanchard
2009-01-16 18:17 ` [Qemu-devel] [PATCH 3/9] powerpc/kvm: Enable mpic for E500 platform Hollis Blanchard
2009-01-16 21:20 ` [Qemu-devel] [PATCH 3/9] powerpc/kvm: Enable mpic for E500 Aurelien Jarno
2009-01-16 21:20 ` [Qemu-devel] [PATCH 3/9] powerpc/kvm: Enable mpic for E500 platform Aurelien Jarno
2009-01-15 20:06 ` [PATCH 0/9] powerpc/kvm: Add MPC85xx platform support Hollis Blanchard
2009-01-15 20:06 ` [Qemu-devel] " Hollis Blanchard
2009-01-15 21:26 ` [Qemu-devel] " Anthony Liguori
2009-01-15 21:26 ` Anthony Liguori
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=496FA926.1040205@codemonkey.ws \
--to=anthony@codemonkey.ws \
--cc=kvm-ppc@vger.kernel.org \
--cc=qemu-devel@nongnu.org \
--cc=yu.liu@freescale.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.