From: Joshua Kinard <kumba@gentoo.org>
To: Linux MIPS List <linux-mips@linux-mips.org>
Subject: IP30: SMP Help
Date: Mon, 03 Nov 2014 00:54:05 -0500 [thread overview]
Message-ID: <5457187D.6030708@gentoo.org> (raw)
[-- Attachment #1: Type: text/plain, Size: 898 bytes --]
I've recently acquired a dual R14000 CPU for the Octane, so I am trying to get
SMP working again, but I can't get things setup properly. I've attached both
ip30-irq.c and ip30-smp.c -- does anyone see any immediate problems (or just
where I am doing it wrong)?
Most reboot cycles with this code panics because init exited with a code of 0xa
or 0xb (which matches w/ SIGSEGV or SIGBUS). Randomly, I can acquire a dash
shell by passing init=/bin/dash. I can't do much in it, though. A basic 'ls'
either segfaults or triggers a SIGBUS. If I execute 'ls' enough times, it
eventually works. Can't get much farther beyond that.
--
Joshua Kinard
Gentoo/MIPS
kumba@gentoo.org
4096R/D25D95E3 2011-03-28
"The past tempts us, the present confuses us, the future frightens us. And our
lives slip away, moment by moment, lost in that vast, terrible in-between."
--Emperor Turhan, Centauri Republic
[-- Attachment #2: ip30-irq.c --]
[-- Type: text/plain, Size: 9125 bytes --]
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* ip30-irq.c: Highlevel interrupt handling for IP30 architecture.
*
* Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@linux-mips.org>
* 2009 Johannes Dickgreber <tanzy@gmx.de>
* 2007-2014 Joshua Kinard <kumba@gentoo.org>
*
* Inspired by ip27-irq.c and ip32-irq.c
*/
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/tick.h>
#include <asm/irq_cpu.h>
#include <asm/pci/bridge.h>
#include <asm/mach-ip30/heart.h>
#include <asm/mach-ip30/pcibr.h>
#include <asm/mach-ip30/racermp.h>
#include <asm/mach-ip30/addrs.h>
#undef DEBUG_IRQ
static DEFINE_SPINLOCK(heart_lock);
static int heart_irq_owner[NR_IRQS];
bridge_t *ip30_irq_to_bridge[NR_IRQS];
unsigned int ip30_irq_to_slot[NR_IRQS];
//extern void ip30_clockevent_broadcast(const int cpu);
//extern void ip30_clockevent_broadcast(const cpumask_t mask);
/* ----------------------------------------------------------------------- */
/**
* ip30_do_error_irq - IRQ dispatch for all HEART error IRQs (51 - 63).
*/
static noinline void
ip30_do_error_irq(void)
{
unsigned long errors;
unsigned long cause;
int i;
irq_enter();
errors = readq(HEART_ISR);
cause = readq(HEART_CAUSE);
writeq(HEART_INT_LEVEL4, HEART_CLR_ISR);
/* Refer to ip30-heart.h for the HC_* macros to know what the cause was */
if (cause > 0) {
printk(KERN_WARNING "IP30: HEART ATTACK! ISR = 0x%.16llx "
"CAUSE = 0x%.16llx\n", (u64)(errors),(u64)(cause));
/* i = 63; i >= 51; i-- */
for (i = HEART_ERR_MASK_END; i >= HEART_ERR_MASK_START; i--)
if ((errors >> i) & 1)
printk(KERN_CONT " HEART Error IRQ #%d\n", i);
}
irq_exit();
}
/**
* ip30_do_heart_irq - IRQ dispatch for all HEART L0, L1, L2 IRQs (0 - 49).
*/
static noinline void
ip30_do_heart_irq(void)
{
int irqnum = 49;
int cpu = smp_processor_id();
u64 heart_isr = readq(HEART_ISR);
u64 heart_imr = readq(HEART_IMR(cpu));
u64 irqs = (heart_isr &
(HEART_INT_LEVEL0 | HEART_INT_LEVEL1 | HEART_INT_LEVEL2) &
heart_imr);
#ifdef DEBUG_IRQ
bridge_t *bvma = (bridge_t *)RAW_NODE_SWIN_BASE(0, 15);
printk(KERN_INFO "IP30: received HEART IRQs: 0x%016llx "
"(mask 0x%016llx) CPU%d BRIDGE %08x\n",
heart_isr, heart_imr, cpu, bvma->b_int_status);
#endif
/* Poll all IRQs in decreasing priority order */
do {
if (irqs & (1UL << irqnum))
do_IRQ(irqnum);
irqnum--;
} while (likely(irqnum >= 0));
}
/* ----------------------------------------------------------------------- */
/**
* plat_irq_dispatch - platform IRQ dispatch.
*/
asmlinkage void
plat_irq_dispatch(void)
{
unsigned long pending;
pending = (read_c0_cause() & read_c0_status() & ST0_IM);
/* L5, CPU Counter/Compare */
if (pending & CAUSEF_IP7)
do_IRQ(MIPS_CPU_IRQ_BASE);
/* L3, HEART Counter/Compare */
else if (likely(pending & CAUSEF_IP5))
do_IRQ(HEART_L3_INT_TIMER);
/* L0-L2, HEART normal IRQs + IPI/SMP IRQs*/
else if (likely(pending & (CAUSEF_IP2 | CAUSEF_IP3 | CAUSEF_IP4)))
ip30_do_heart_irq();
/* L4, HEART Errors */
else if (unlikely(pending & CAUSEF_IP6))
ip30_do_error_irq();
}
/* ----------------------------------------------------------------------- */
/* ----------------------------------------------------------------------- */
/* HEART IRQ Ops */
/**
* startup_heart_irq - assigns a HEART IRQ to a CPU and/or Bridge slot.
* @d: struct irq_data containing IRQ information.
*
* Returns 0 for now (XXX: this may need review, possible pending interrupts).
*/
static unsigned int
startup_heart_irq(struct irq_data *d)
{
bridge_t *bridge;
int cpu = smp_processor_id();
u32 device, slot;
u64 *imr;
spin_lock(&heart_lock);
if (heart_irq_owner[d->irq] != -1) {
printk(KERN_ERR "DEBUG: startup_heart_irq: bad IRQ "
"startup request for IRQ %d on CPU %d "
"(already assigned to %d)!\n",
d->irq, cpu, heart_irq_owner[d->irq]);
goto out;
}
/* store which CPU owns this IRQ */
heart_irq_owner[d->irq] = cpu;
#ifdef DEBUG_IRQ
printk(KERN_INFO "DEBUG: IP30: IRQ: startup_heart_irq: "
"IRQ %d on CPU %d!\n", d->irq, heart_irq_owner[d->irq]);
#endif
/* clear IRQ flag */
writeq(HEART_VEC_TO_IBIT(d->irq), HEART_CLR_ISR);
/* unmask IRQ */
imr = HEART_IMR(cpu);
writeq(readq(imr) | HEART_VEC_TO_IBIT(d->irq), imr);
/* Handle BRIDGE IRQs. */
bridge = ip30_irq_to_bridge[d->irq];
if (bridge) {
slot = ip30_irq_to_slot[d->irq];
bridge->b_int_enable |= (1 << slot);
bridge->b_int_mode |= (1 << slot);
device = bridge->b_int_device;
device &= ~BRIDGE_INT_DEV_MASK(slot);
device |= BRIDGE_INT_DEV_SET(slot, slot);
bridge->b_int_device = device;
bridge->b_widget.w_tflush;
}
out:
spin_unlock(&heart_lock);
/* XXX: This is probably not right; we could have pending irqs */
return 0;
}
/**
* shutdown_heart_irq - removes a HEART IRQ from a CPU and/or Bridge slot.
* @d: struct irq_data containing IRQ information.
*/
static void
shutdown_heart_irq(struct irq_data *d)
{
bridge_t *bridge;
u64 *imr;
spin_lock(&heart_lock);
imr = HEART_IMR(heart_irq_owner[d->irq]);
writeq(readq(imr) & ~(HEART_VEC_TO_IBIT(d->irq)), imr);
bridge = ip30_irq_to_bridge[d->irq];
if (bridge)
bridge->b_int_enable &= ~(1UL << ip30_irq_to_slot[d->irq]);
heart_irq_owner[d->irq] = -1;
spin_unlock(&heart_lock);
}
/**
* ack_heart_irq - acks a HEART IRQ.
* @d: struct irq_data containing IRQ information.
*/
static void
ack_heart_irq(struct irq_data *d)
{
spin_lock(&heart_lock);
writeq(HEART_VEC_TO_IBIT(d->irq), HEART_CLR_ISR);
spin_unlock(&heart_lock);
}
/**
* mask_heart_irq - masks a HEART IRQ.
* @d: struct irq_data containing IRQ information.
*/
static void
mask_heart_irq(struct irq_data *d)
{
u64 *imr;
spin_lock(&heart_lock);
imr = HEART_IMR(heart_irq_owner[d->irq]);
writeq(readq(imr) & ~(HEART_VEC_TO_IBIT(d->irq)), imr);
spin_unlock(&heart_lock);
}
/**
* mask_and_ack_heart_irq - masks and acks a HEART IRQ.
* @d: struct irq_data containing IRQ information.
*/
static void
mask_and_ack_heart_irq(struct irq_data *d)
{
u64 *imr;
spin_lock(&heart_lock);
writeq(HEART_VEC_TO_IBIT(d->irq), HEART_CLR_ISR);
imr = HEART_IMR(heart_irq_owner[d->irq]);
writeq(readq(imr) & ~(HEART_VEC_TO_IBIT(d->irq)), imr);
spin_unlock(&heart_lock);
}
/**
* unmask_heart_irq - unmasks a HEART IRQ.
* @d: struct irq_data containing IRQ information.
*/
static void
unmask_heart_irq(struct irq_data *d)
{
u64 *imr;
spin_lock(&heart_lock);
imr = HEART_IMR(heart_irq_owner[d->irq]);
writeq(readq(imr) | HEART_VEC_TO_IBIT(d->irq), imr);
spin_unlock(&heart_lock);
}
/**
* struct ip30_heart_irq - HEART struct irq_chip ops.
* @irq_startup: startup function.
* @irq_shutdown: shutdown function.
* @irq_ack: ack function.
* @irq_mask: mask function.
* @irq_mask_ack: mask & ack function.
* @irq_unmask: unmask function.
*/
static struct
irq_chip ip30_heart_irq = {
.name = "HEART",
.irq_startup = startup_heart_irq,
.irq_shutdown = shutdown_heart_irq,
.irq_ack = ack_heart_irq,
.irq_mask = mask_heart_irq,
.irq_mask_ack = mask_and_ack_heart_irq,
.irq_unmask = unmask_heart_irq,
};
/* ----------------------------------------------------------------------- */
static inline void ip30_unmask_boot_cpu_irq(struct irq_data *d)
{
set_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
}
static inline void ip30_mask_boot_cpu_irq(struct irq_data *d)
{
clear_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
}
static struct irq_chip ip30_boot_cpu_irq_controller = {
.name = "IP30-CPU0-IRQ",
.irq_ack = ip30_mask_boot_cpu_irq,
.irq_mask = ip30_mask_boot_cpu_irq,
.irq_mask_ack = ip30_mask_boot_cpu_irq,
.irq_unmask = ip30_unmask_boot_cpu_irq,
.irq_eoi = ip30_unmask_boot_cpu_irq,
};
/* ----------------------------------------------------------------------- */
/* Arch IRQ initialization - runs on CPU0 only. */
/**
* arch_init_irq - arch initialization function.
*/
void __init
arch_init_irq(void)
{
int irq;
/* Ack everything */
writeq(HEART_ACK_ALL_MASK, HEART_CLR_ISR);
/* Mask all IRQs */
writeq(HEART_CLR_ALL_MASK, HEART_IMR(0));
writeq(HEART_CLR_ALL_MASK, HEART_IMR(1));
writeq(HEART_CLR_ALL_MASK, HEART_IMR(2));
writeq(HEART_CLR_ALL_MASK, HEART_IMR(3));
/* Leave HEART errors enabled */
writeq(HEART_BR_ERR_MASK, HEART_IMR(0));
for (irq = HEART_IRQ_BASE; irq < HEART_IRQS; irq++) {
heart_irq_owner[irq] = -1;
irq_set_chip_and_handler(irq, &ip30_heart_irq, handle_level_irq);
}
/* Init CPU0 IRQs */
// mips_cpu_irq_init();
irq_set_chip_and_handler(MIPS_CPU_IRQ_BASE,
&ip30_boot_cpu_irq_controller,
handle_percpu_irq);
change_c0_status(ST0_IM, (STATUSF_IP2 | STATUSF_IP3 | STATUSF_IP4 |
STATUSF_IP5 | STATUSF_IP6 | STATUSF_IP7));
printk(KERN_INFO "IP30: HEART interrupt controller initialized.\n");
}
/* ----------------------------------------------------------------------- */
[-- Attachment #3: ip30-smp.c --]
[-- Type: text/plain, Size: 7600 bytes --]
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* ip30-smp.c: SMP on IP30 architecture.
*
* Copyright (C) 2005-2007 Stanislaw Skowronek <skylark@linux-mips.org>
* 2006-2007 Joshua Kinard <kumba@gentoo.org>
* 2009 Johannes Dickgreber <tanzy@gmx.de>
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/tick.h>
#include <linux/spinlock_types.h>
#include <asm/time.h>
#include <asm/cacheflush.h>
#include <asm/mach-ip30/heart.h>
#include <asm/mach-ip30/racermp.h>
static DEFINE_SPINLOCK(ip30_ipi_lock);
//static DEFINE_SPINLOCK(ip30_timer_lock);
static DEFINE_PER_CPU(int, ip30_ipi_action_mask);
static DEFINE_PER_CPU(char[15], ip30_ipi_name);
//static DEFINE_PER_CPU(char[17], ip30_timer_name);
//void ip30_clockevent_broadcast(const int cpu)
//{
//#ifdef CONFIG_SMP
// /* There can only ever be 2 CPUs in an Octane, so keep it simple. */
//#endif
//}
/* ----------------------------------------------------------------------- */
/* SMP IPI Ops */
/* Runs on CPUx and sends an IPI to CPUy. */
static void ip30_send_ipi_single(int cpu, unsigned int action)
{
// unsigned long flags;
// printk(KERN_INFO "DEBUG: IP30: SMP: ip30_send_ipi_single CPU%d -> CPU%d: action: 0x%.8x\n", smp_processor_id(), cpu, action);
// spin_lock_irqsave(&ip30_ipi_lock, flags);
spin_lock(&ip30_ipi_lock);
per_cpu(ip30_ipi_action_mask, cpu) |= action;
writeq(HEART_VEC_TO_IBIT(HEART_L2_INT_IPI_CPU(cpu)), HEART_SET_ISR);
spin_unlock(&ip30_ipi_lock);
// spin_unlock_irqrestore(&ip30_ipi_lock, flags);
}
/* IRQ handler that runs on CPUy and services the IPI. */
static irqreturn_t ip30_ipi_irq(int irq, void *dev_id)
{
int action;
int cpu = smp_processor_id();
// unsigned long flags;
// spin_lock_irqsave(&ip30_ipi_lock, flags);
spin_lock(&ip30_ipi_lock);
action = __get_cpu_var(ip30_ipi_action_mask);
per_cpu(ip30_ipi_action_mask, cpu) = 0;
writeq(HEART_VEC_TO_IBIT(HEART_L2_INT_IPI_CPU(cpu)), HEART_CLR_ISR);
spin_unlock(&ip30_ipi_lock);
// spin_unlock_irqrestore(&ip30_ipi_lock, flags);
// printk(KERN_INFO "DEBUG: IP30: SMP: ip30_ipi_irq: cpu%d: action: 0x%.8x\n", cpu, action);
if (action & SMP_RESCHEDULE_YOURSELF)
scheduler_ipi();
if (action & SMP_CALL_FUNCTION)
smp_call_function_interrupt();
return IRQ_HANDLED;
}
///* IRQ handler that runs on CPUy and services the CPU Timer. */
//static irqreturn_t ip30_timer_irq(int irq, void *dev_id)
//{
// int cpu = smp_processor_id();
// struct clock_event_device *cd;
//
// spin_lock(&ip30_timer_lock);
// cd = tick_get_device(cpu)->evtdev;
// cd->event_handler(cd);
//// printk(KERN_INFO "DEBUG: IP30: SMP: ip30_timer_irq: CPU%d: %s\n", cpu, cd->name);
// spin_unlock(&ip30_timer_lock);
// writeq(HEART_VEC_TO_IBIT(HEART_L2_INT_TIMER_CPU(cpu)), HEART_CLR_ISR);
//
// return IRQ_HANDLED;
//}
static void ip30_send_ipi_mask(const struct cpumask *mask, unsigned int action)
{
u32 i;
for_each_cpu(i, mask)
ip30_send_ipi_single(i, action);
}
static void __init ip30_smp_setup(void)
{
int i;
int ncpu = 0;
int vcpu;
for (i = 0; i < NR_CPUS; i++) {
if (readl(MP_MAGIC(i)) == MPCONF_MAGIC) {
vcpu = readl(MP_VIRTID(i));
__cpu_number_map[i] = vcpu;
__cpu_logical_map[vcpu] = i;
set_cpu_possible(i, true);
set_cpu_present(i, true);
printk(KERN_INFO "IP30: Slot: %d, PrID: %.8x, PhyID: %d, VirtID: %d\n",
i, readl(MP_PRID(i)), readl(MP_PHYSID(i)), vcpu);
ncpu++;
}
}
printk(KERN_INFO "IP30: Detected %d CPU(s) present.\n", ncpu);
}
/* Runs on CPU0 */
static void __init ip30_prepare_cpus(unsigned int max_cpus)
{
int cpu = smp_processor_id();
unsigned char *ipi_name = per_cpu(ip30_ipi_name, cpu);
// unsigned char *timer_name = per_cpu(ip30_timer_name, cpu);
printk(KERN_INFO "DEBUG: IP30: SMP: ip30_prepare_cpus\n");
/* Request an IRQ number for CPU0 IPI. */
snprintf(ipi_name, 15, "ip30-cpu%d-ipi", cpu);
if (request_irq(HEART_L2_INT_IPI_CPU(cpu), ip30_ipi_irq, IRQF_PERCPU,
ipi_name, NULL))
panic("Can't request CPU%d IPI interrupt", cpu);
// snprintf(timer_name, 17, "ip30-cpu%d-timer", cpu);
// if (request_irq(HEART_L2_INT_TIMER_CPU(cpu), ip30_timer_irq, IRQF_PERCPU,
// timer_name, NULL))
// panic("Can't request CPU%d Broadcast Timer interrupt", cpu);
/* Enable the IPI interrupt on CPU0 IP4. */
// change_c0_status(ST0_IM, STATUSF_IP4);
}
/* Runs on CPU0 and boots CPUx, where x > 0 */
static void ip30_boot_secondary(int cpu, struct task_struct *idle)
{
int boot_cpu = smp_processor_id();
// struct thread_info *gp = task_thread_info(idle);
printk(KERN_INFO "DEBUG: IP30: SMP: ip30_boot_secondary: CPU%d -> CPU%d\n", boot_cpu, cpu);
/* Stack pointer (sp). */
writeq(__KSTK_TOS(idle), MP_STACKADDR(cpu));
/* Global pointer (gp). */
writeq((unsigned long)task_thread_info(idle), MP_LPARM(cpu));
// writeq((unsigned long)gp, MP_LPARM(cpu));
/* XXX: I don't know if this is needed or not. Copied from elsewhere. */
// flush_icache_range((unsigned long)gp,
// (unsigned long)(gp + sizeof(struct thread_info)));
/* Boot CPU1. */
writeq((unsigned long)ip30_smp_bootstrap, MP_LAUNCH(cpu));
// mb();
/* XXX: This is needed to "kick" the CPU awake. Bug? */
pr_info("SMP: Booting CPU%d...\n", cpu);
}
/* Runs on CPUx, where x > 0 */
static void ip30_init_secondary(void)
{
int cpu = smp_processor_id();
printk(KERN_INFO "DEBUG: IP30: SMP: ip30_init_secondary on CPU%d\n", cpu);
}
static inline void ip30_unmask_2nd_cpu_irq(struct irq_data *d)
{
set_c0_status(0x100 << (d->irq - (MIPS_CPU_IRQ_BASE + 1)));
}
static inline void ip30_mask_2nd_cpu_irq(struct irq_data *d)
{
clear_c0_status(0x100 << (d->irq - (MIPS_CPU_IRQ_BASE + 1)));
}
static struct irq_chip ip30_2nd_cpu_irq_controller = {
.name = "IP30-CPU1-IRQ",
.irq_ack = ip30_mask_2nd_cpu_irq,
.irq_mask = ip30_mask_2nd_cpu_irq,
.irq_mask_ack = ip30_mask_2nd_cpu_irq,
.irq_unmask = ip30_unmask_2nd_cpu_irq,
.irq_eoi = ip30_unmask_2nd_cpu_irq,
};
static void ip30_smp_finish(void)
{
int cpu = smp_processor_id();
unsigned char *ipi_name = per_cpu(ip30_ipi_name, cpu);
// unsigned char *timer_name = per_cpu(ip30_timer_name, cpu);
printk(KERN_INFO "DEBUG: IP30: SMP: ip30_smp_finish: CPU%d\n", cpu);
irq_set_chip_and_handler((MIPS_CPU_IRQ_BASE + 1),
&ip30_2nd_cpu_irq_controller,
handle_percpu_irq);
// write_c0_compare(read_c0_count() + mips_hpt_frequency / HZ);
change_c0_status(ST0_IM, (STATUSF_IP2 | STATUSF_IP3 | STATUSF_IP4 |
STATUSF_IP5 | STATUSF_IP6 | STATUSF_IP7));
local_irq_enable();
/* Request an IRQ number for CPU1 IPI. */
snprintf(ipi_name, 15, "ip30-cpu%d-ipi", cpu);
if (request_irq(HEART_L2_INT_IPI_CPU(cpu), ip30_ipi_irq, IRQF_PERCPU,
ipi_name, NULL))
panic("Can't request CPU%d IPI interrupt", cpu);
// snprintf(timer_name, 17, "ip30-cpu%d-timer", cpu);
// if (request_irq(HEART_L2_INT_TIMER_CPU(cpu), ip30_timer_irq, IRQF_PERCPU,
// timer_name, NULL))
// panic("Can't request CPU%d Broadcast Timer interrupt", cpu);
pr_info("SMP: CPU%d is running\n", smp_processor_id());
}
struct plat_smp_ops ip30_smp_ops = {
.send_ipi_single = ip30_send_ipi_single,
.send_ipi_mask = ip30_send_ipi_mask,
.init_secondary = ip30_init_secondary,
.smp_finish = ip30_smp_finish,
.boot_secondary = ip30_boot_secondary,
.smp_setup = ip30_smp_setup,
.prepare_cpus = ip30_prepare_cpus,
};
next reply other threads:[~2014-11-03 5:54 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-11-03 5:54 Joshua Kinard [this message]
2014-11-10 8:17 ` IP30: SMP Help Joshua Kinard
2014-11-18 9:30 ` Joshua Kinard
2014-11-18 10:05 ` Maciej W. Rozycki
2014-11-18 12:37 ` Joshua Kinard
2014-11-18 13:10 ` Maciej W. Rozycki
2014-11-19 10:59 ` Joshua Kinard
2014-11-19 12:06 ` Maciej W. Rozycki
2014-11-19 15:22 ` Ralf Baechle
2014-11-21 6:13 ` Joshua Kinard
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=5457187D.6030708@gentoo.org \
--to=kumba@gentoo.org \
--cc=linux-mips@linux-mips.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox