LKML Archive on lore.kernel.org
 help / color / Atom feed
From: <Nicolas.Ferre@microchip.com>
To: <Claudiu.Beznea@microchip.com>, <robh+dt@kernel.org>,
	<mark.rutland@arm.com>, <alexandre.belloni@bootlin.com>,
	<Ludovic.Desroches@microchip.com>, <daniel.lezcano@linaro.org>,
	<tglx@linutronix.de>
Cc: <devicetree@vger.kernel.org>,
	<linux-arm-kernel@lists.infradead.org>,
	<linux-kernel@vger.kernel.org>
Subject: Re: [PATCH 2/5] clocksource/drivers/timer-microchip-pit64b: add Microchip PIT64B support
Date: Mon, 1 Apr 2019 08:40:50 +0000
Message-ID: <a7e95532-697f-161e-709a-196a56ec8857@microchip.com> (raw)
In-Reply-To: <1552580772-8499-3-git-send-email-claudiu.beznea@microchip.com>

On 14/03/2019 at 17:26, Claudiu Beznea - M18063 wrote:
> From: Claudiu Beznea <claudiu.beznea@microchip.com>
> 
> Add driver for Microchip PIT64B timer. Timer could be used in continuous
> mode or oneshot mode. The hardware has 2x32 bit registers for period
> emulating a 64 bit timer. The LSB_PR and MSB_PR registers are used to set
> the period value (compare value). TLSB and TMSB keeps the current value
> of the counter. After a compare the TLSB and TMSB register resets. Apart
> from this the hardware has SMOD bit in mode register that allow to
> reconfigure the timer without reset and start commands (start command
> while timer is active is ignored).
> The driver uses PIT64B timer as clocksource or clockevent. First requested
> timer would be registered as clockevent, second one would be registered as
> clocksource. Individual PIT64B hardware resources were used for clocksource
> and clockevent to be able to support high resolution timers with this
> hardware implementation.
> 
> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>

Look good to me:
Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com>


> ---
>   drivers/clocksource/Kconfig                  |   6 +
>   drivers/clocksource/Makefile                 |   1 +
>   drivers/clocksource/timer-microchip-pit64b.c | 464 +++++++++++++++++++++++++++
>   3 files changed, 471 insertions(+)
>   create mode 100644 drivers/clocksource/timer-microchip-pit64b.c
> 
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index 5d93e580e5dc..2ad6f881a0bb 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -448,6 +448,12 @@ config OXNAS_RPS_TIMER
>   config SYS_SUPPORTS_SH_CMT
>           bool
>   
> +config MICROCHIP_PIT64B
> +	bool "Microchip PIT64B support"
> +	depends on OF || COMPILE_TEST
> +	help
> +	  This option enables Microchip PIT64B timer.
> +
>   config MTK_TIMER
>   	bool "Mediatek timer driver" if COMPILE_TEST
>   	depends on HAS_IOMEM
> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
> index c4a8e9ef932a..c53fa12b9b94 100644
> --- a/drivers/clocksource/Makefile
> +++ b/drivers/clocksource/Makefile
> @@ -35,6 +35,7 @@ obj-$(CONFIG_U300_TIMER)	+= timer-u300.o
>   obj-$(CONFIG_SUN4I_TIMER)	+= timer-sun4i.o
>   obj-$(CONFIG_SUN5I_HSTIMER)	+= timer-sun5i.o
>   obj-$(CONFIG_MESON6_TIMER)	+= timer-meson6.o
> +obj-$(CONFIG_MICROCHIP_PIT64B)	+= timer-microchip-pit64b.o
>   obj-$(CONFIG_TEGRA_TIMER)	+= timer-tegra20.o
>   obj-$(CONFIG_VT8500_TIMER)	+= timer-vt8500.o
>   obj-$(CONFIG_NSPIRE_TIMER)	+= timer-zevio.o
> diff --git a/drivers/clocksource/timer-microchip-pit64b.c b/drivers/clocksource/timer-microchip-pit64b.c
> new file mode 100644
> index 000000000000..6787aa98ef01
> --- /dev/null
> +++ b/drivers/clocksource/timer-microchip-pit64b.c
> @@ -0,0 +1,464 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright (C) 2019 Microchip Technology Inc.
> +// Copyright (C) 2019 Claudiu Beznea (claudiu.beznea@microchip.com)
> +
> +#include <linux/clk.h>
> +#include <linux/clockchips.h>
> +#include <linux/interrupt.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/sched_clock.h>
> +#include <linux/slab.h>
> +
> +#define MCHP_PIT64B_CR		0x00	/* Control Register */
> +#define MCHP_PIT64B_CR_START	BIT(0)
> +#define MCHP_PIT64B_CR_SWRST	BIT(8)
> +
> +#define MCHP_PIT64B_MR		0x04	/* Mode Register */
> +#define MCHP_PIT64B_MR_CONT	BIT(0)
> +#define MCHP_PIT64B_MR_SGCLK	BIT(3)
> +#define MCHP_PIT64B_MR_SMOD	BIT(4)
> +#define MCHP_PIT64B_MR_PRES	GENMASK(11, 8)
> +
> +#define MCHP_PIT64B_LSB_PR	0x08	/* LSB Period Register */
> +
> +#define MCHP_PIT64B_MSB_PR	0x0C	/* MSB Period Register */
> +
> +#define MCHP_PIT64B_IER		0x10	/* Interrupt Enable Register */
> +#define MCHP_PIT64B_IER_PERIOD	BIT(0)
> +
> +#define MCHP_PIT64B_ISR		0x1C	/* Interrupt Status Register */
> +#define MCHP_PIT64B_ISR_PERIOD	BIT(0)
> +
> +#define MCHP_PIT64B_TLSBR	0x20	/* Timer LSB Register */
> +
> +#define MCHP_PIT64B_TMSBR	0x24	/* Timer MSB Register */
> +
> +#define MCHP_PIT64B_PRES_MAX	0x10
> +#define MCHP_PIT64B_DEF_FREQ	2500000UL	/* 2.5 MHz */
> +#define MCHP_PIT64B_LSBMASK	GENMASK_ULL(31, 0)
> +#define MCHP_PIT64B_PRESCALER(p)	(MCHP_PIT64B_MR_PRES & ((p) << 8))
> +
> +#define MCHP_PIT64B_NAME	"pit64b"
> +
> +struct mchp_pit64b_common_data {
> +	void __iomem *base;
> +	struct clk *pclk;
> +	struct clk *gclk;
> +	u64 cycles;
> +	u8 pres;
> +};
> +
> +struct mchp_pit64b_clksrc_data {
> +	struct clocksource *clksrc;
> +	struct mchp_pit64b_common_data *cd;
> +};
> +
> +struct mchp_pit64b_clkevt_data {
> +	struct clock_event_device *clkevt;
> +	struct mchp_pit64b_common_data *cd;
> +};
> +
> +static struct mchp_pit64b_data {
> +	struct mchp_pit64b_clksrc_data *csd;
> +	struct mchp_pit64b_clkevt_data *ced;
> +} data;
> +
> +static inline u32 mchp_pit64b_read(void __iomem *base, u32 offset)
> +{
> +	return readl_relaxed(base + offset);
> +}
> +
> +static inline void mchp_pit64b_write(void __iomem *base, u32 offset, u32 val)
> +{
> +	writel_relaxed(val, base + offset);
> +}
> +
> +static inline u64 mchp_pit64b_get_period(void __iomem *base)
> +{
> +	u32 lsb, msb;
> +
> +	/* LSB must be read first to guarantee an atomic read of the 64 bit
> +	 * timer.
> +	 */
> +	lsb = mchp_pit64b_read(base, MCHP_PIT64B_TLSBR);
> +	msb = mchp_pit64b_read(base, MCHP_PIT64B_TMSBR);
> +
> +	return (((u64)msb << 32) | lsb);
> +}
> +
> +static inline void mchp_pit64b_set_period(void __iomem *base, u64 cycles)
> +{
> +	u32 lsb, msb;
> +
> +	lsb = cycles & MCHP_PIT64B_LSBMASK;
> +	msb = cycles >> 32;
> +
> +	/* LSB must be write last to guarantee an atomic update of the timer
> +	 * even when SMOD=1.
> +	 */
> +	mchp_pit64b_write(base, MCHP_PIT64B_MSB_PR, msb);
> +	mchp_pit64b_write(base, MCHP_PIT64B_LSB_PR, lsb);
> +}
> +
> +static inline void mchp_pit64b_reset(struct mchp_pit64b_common_data *data,
> +				     u32 mode, bool irq_ena)
> +{
> +	mode |= MCHP_PIT64B_PRESCALER(data->pres);
> +	if (data->gclk)
> +		mode |= MCHP_PIT64B_MR_SGCLK;
> +
> +	mchp_pit64b_write(data->base, MCHP_PIT64B_CR, MCHP_PIT64B_CR_SWRST);
> +	mchp_pit64b_write(data->base, MCHP_PIT64B_MR, mode);
> +	mchp_pit64b_set_period(data->base, data->cycles);
> +	if (irq_ena)
> +		mchp_pit64b_write(data->base, MCHP_PIT64B_IER,
> +				  MCHP_PIT64B_IER_PERIOD);
> +	mchp_pit64b_write(data->base, MCHP_PIT64B_CR, MCHP_PIT64B_CR_START);
> +}
> +
> +static u64 mchp_pit64b_read_clk(struct clocksource *cs)
> +{
> +	return mchp_pit64b_get_period(data.csd->cd->base);
> +}
> +
> +static u64 mchp_sched_read_clk(void)
> +{
> +	return mchp_pit64b_get_period(data.csd->cd->base);
> +}
> +
> +static struct clocksource mchp_pit64b_clksrc = {
> +	.name = MCHP_PIT64B_NAME,
> +	.mask = CLOCKSOURCE_MASK(64),
> +	.flags = CLOCK_SOURCE_IS_CONTINUOUS,
> +	.rating = 210,
> +	.read = mchp_pit64b_read_clk,
> +};
> +
> +static int mchp_pit64b_clkevt_shutdown(struct clock_event_device *cedev)
> +{
> +	mchp_pit64b_write(data.ced->cd->base, MCHP_PIT64B_CR,
> +			  MCHP_PIT64B_CR_SWRST);
> +
> +	return 0;
> +}
> +
> +static int mchp_pit64b_clkevt_set_periodic(struct clock_event_device *cedev)
> +{
> +	mchp_pit64b_reset(data.ced->cd, MCHP_PIT64B_MR_CONT, true);
> +
> +	return 0;
> +}
> +
> +static int mchp_pit64b_clkevt_set_oneshot(struct clock_event_device *cedev)
> +{
> +	mchp_pit64b_reset(data.ced->cd, MCHP_PIT64B_MR_SMOD, true);
> +
> +	return 0;
> +}
> +
> +static int mchp_pit64b_clkevt_set_next_event(unsigned long evt,
> +					     struct clock_event_device *cedev)
> +{
> +	mchp_pit64b_set_period(data.ced->cd->base, evt);
> +	mchp_pit64b_write(data.ced->cd->base, MCHP_PIT64B_CR,
> +			  MCHP_PIT64B_CR_START);
> +
> +	return 0;
> +}
> +
> +static void mchp_pit64b_clkevt_suspend(struct clock_event_device *cedev)
> +{
> +	mchp_pit64b_write(data.ced->cd->base, MCHP_PIT64B_CR,
> +			  MCHP_PIT64B_CR_SWRST);
> +	if (data.ced->cd->gclk)
> +		clk_disable_unprepare(data.ced->cd->gclk);
> +	clk_disable_unprepare(data.ced->cd->pclk);
> +}
> +
> +static void mchp_pit64b_clkevt_resume(struct clock_event_device *cedev)
> +{
> +	u32 mode = MCHP_PIT64B_MR_SMOD;
> +
> +	clk_prepare_enable(data.ced->cd->pclk);
> +	if (data.ced->cd->gclk)
> +		clk_prepare_enable(data.ced->cd->gclk);
> +
> +	if (clockevent_state_periodic(data.ced->clkevt))
> +		mode = MCHP_PIT64B_MR_CONT;
> +
> +	mchp_pit64b_reset(data.ced->cd, mode, true);
> +}
> +
> +static struct clock_event_device mchp_pit64b_clkevt = {
> +	.name = MCHP_PIT64B_NAME,
> +	.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
> +	.rating = 150,
> +	.set_state_shutdown = mchp_pit64b_clkevt_shutdown,
> +	.set_state_periodic = mchp_pit64b_clkevt_set_periodic,
> +	.set_state_oneshot = mchp_pit64b_clkevt_set_oneshot,
> +	.set_next_event = mchp_pit64b_clkevt_set_next_event,
> +	.suspend = mchp_pit64b_clkevt_suspend,
> +	.resume = mchp_pit64b_clkevt_resume,
> +};
> +
> +static irqreturn_t mchp_pit64b_interrupt(int irq, void *dev_id)
> +{
> +	struct mchp_pit64b_clkevt_data *irq_data = dev_id;
> +
> +	if (data.ced != irq_data)
> +		return IRQ_NONE;
> +
> +	if (mchp_pit64b_read(irq_data->cd->base, MCHP_PIT64B_ISR) &
> +	    MCHP_PIT64B_ISR_PERIOD) {
> +		irq_data->clkevt->event_handler(irq_data->clkevt);
> +		return IRQ_HANDLED;
> +	}
> +
> +	return IRQ_NONE;
> +}
> +
> +static int __init mchp_pit64b_pres_compute(u32 *pres, u32 clk_rate,
> +					   u32 max_rate)
> +{
> +	u32 tmp;
> +
> +	for (*pres = 0; *pres < MCHP_PIT64B_PRES_MAX; (*pres)++) {
> +		tmp = clk_rate / (*pres + 1);
> +		if (tmp <= max_rate)
> +			break;
> +	}
> +
> +	if (*pres == MCHP_PIT64B_PRES_MAX)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int __init mchp_pit64b_pres_prepare(struct mchp_pit64b_common_data *cd,
> +					   unsigned long max_rate)
> +{
> +	unsigned long pclk_rate, diff = 0, best_diff = ULONG_MAX;
> +	long gclk_round = 0;
> +	u32 pres, best_pres;
> +	int ret = 0;
> +
> +	pclk_rate = clk_get_rate(cd->pclk);
> +	if (!pclk_rate)
> +		return -EINVAL;
> +
> +	if (cd->gclk) {
> +		gclk_round = clk_round_rate(cd->gclk, max_rate);
> +		if (gclk_round < 0)
> +			goto pclk;
> +
> +		if (pclk_rate / gclk_round < 3)
> +			goto pclk;
> +
> +		ret = mchp_pit64b_pres_compute(&pres, gclk_round, max_rate);
> +		if (ret)
> +			best_diff = abs(gclk_round - max_rate);
> +		else
> +			best_diff = abs(gclk_round / (pres + 1) - max_rate);
> +		best_pres = pres;
> +	}
> +
> +pclk:
> +	/* Check if requested rate could be obtained using PCLK. */
> +	ret = mchp_pit64b_pres_compute(&pres, pclk_rate, max_rate);
> +	if (ret)
> +		diff = abs(pclk_rate - max_rate);
> +	else
> +		diff = abs(pclk_rate / (pres + 1) - max_rate);
> +
> +	if (best_diff > diff) {
> +		/* Use PCLK. */
> +		cd->gclk = NULL;
> +		best_pres = pres;
> +	} else {
> +		clk_set_rate(cd->gclk, gclk_round);
> +	}
> +
> +	cd->pres = best_pres;
> +
> +	pr_info("PIT64B: using clk=%s with prescaler %u, freq=%lu [Hz]\n",
> +		cd->gclk ? "gclk" : "pclk", cd->pres,
> +		cd->gclk ? gclk_round / (cd->pres + 1)
> +			 : pclk_rate / (cd->pres + 1));
> +
> +	return 0;
> +}
> +
> +static int __init mchp_pit64b_dt_init_clksrc(struct mchp_pit64b_common_data *cd)
> +{
> +	struct mchp_pit64b_clksrc_data *csd;
> +	unsigned long clk_rate;
> +	int ret;
> +
> +	csd = kzalloc(sizeof(*csd), GFP_KERNEL);
> +	if (!csd)
> +		return -ENOMEM;
> +
> +	csd->cd = cd;
> +
> +	if (csd->cd->gclk)
> +		clk_rate = clk_get_rate(csd->cd->gclk);
> +	else
> +		clk_rate = clk_get_rate(csd->cd->pclk);
> +
> +	clk_rate = clk_rate / (cd->pres + 1);
> +	csd->cd->cycles = ULLONG_MAX;
> +	mchp_pit64b_reset(csd->cd, MCHP_PIT64B_MR_CONT, false);
> +
> +	data.csd = csd;
> +
> +	csd->clksrc = &mchp_pit64b_clksrc;
> +
> +	ret = clocksource_register_hz(csd->clksrc, clk_rate);
> +	if (ret) {
> +		pr_debug("clksrc: Failed to register PIT64B clocksource!\n");
> +		goto free;
> +	}
> +
> +	sched_clock_register(mchp_sched_read_clk, 64, clk_rate);
> +
> +	return 0;
> +
> +free:
> +	kfree(csd);
> +	data.csd = NULL;
> +
> +	return ret;
> +}
> +
> +static int __init mchp_pit64b_dt_init_clkevt(struct mchp_pit64b_common_data *cd,
> +					     u32 irq)
> +{
> +	struct mchp_pit64b_clkevt_data *ced;
> +	unsigned long clk_rate;
> +	int ret;
> +
> +	ced = kzalloc(sizeof(*ced), GFP_KERNEL);
> +	if (!ced)
> +		return -ENOMEM;
> +
> +	ced->cd = cd;
> +
> +	if (ced->cd->gclk)
> +		clk_rate = clk_get_rate(ced->cd->gclk);
> +	else
> +		clk_rate = clk_get_rate(ced->cd->pclk);
> +
> +	clk_rate = clk_rate / (ced->cd->pres + 1);
> +	ced->cd->cycles = DIV_ROUND_CLOSEST(clk_rate, HZ);
> +
> +	ret = request_irq(irq, mchp_pit64b_interrupt, IRQF_TIMER, "pit64b_tick",
> +			  ced);
> +	if (ret) {
> +		pr_debug("clkevt: Failed to setup PIT64B IRQ\n");
> +		goto free;
> +	}
> +
> +	data.ced = ced;
> +
> +	/* Set up and register clockevents. */
> +	ced->clkevt = &mchp_pit64b_clkevt;
> +	ced->clkevt->cpumask = cpumask_of(0);
> +	ced->clkevt->irq = irq;
> +	clockevents_config_and_register(ced->clkevt, clk_rate, 1, ULONG_MAX);
> +
> +	return 0;
> +
> +free:
> +	kfree(ced);
> +	data.ced = NULL;
> +
> +	return ret;
> +}
> +
> +static int __init mchp_pit64b_dt_init(struct device_node *node)
> +{
> +	struct mchp_pit64b_common_data *cd;
> +	u32 irq, freq = MCHP_PIT64B_DEF_FREQ;
> +	int ret;
> +
> +	if (data.csd && data.ced)
> +		return -EBUSY;
> +
> +	cd = kzalloc(sizeof(*cd), GFP_KERNEL);
> +	if (!cd)
> +		return -ENOMEM;
> +
> +	cd->pclk = of_clk_get_by_name(node, "pclk");
> +	if (IS_ERR(cd->pclk)) {
> +		ret = PTR_ERR(cd->pclk);
> +		goto free;
> +	}
> +
> +	cd->gclk = of_clk_get_by_name(node, "gclk");
> +	if (IS_ERR(cd->gclk))
> +		cd->gclk = NULL;
> +
> +	ret = of_property_read_u32(node, "clock-frequency", &freq);
> +	if (ret)
> +		pr_debug("PIT64B: failed to read clock frequency. Using default!\n");
> +
> +	ret = mchp_pit64b_pres_prepare(cd, freq);
> +	if (ret)
> +		goto free;
> +
> +	cd->base = of_iomap(node, 0);
> +	if (!cd->base) {
> +		pr_debug("%s: Could not map PIT64B address!\n",
> +			 MCHP_PIT64B_NAME);
> +		ret = -ENXIO;
> +		goto free;
> +	}
> +
> +	ret = clk_prepare_enable(cd->pclk);
> +	if (ret)
> +		goto unmap;
> +
> +	if (cd->gclk) {
> +		ret = clk_prepare_enable(cd->gclk);
> +		if (ret)
> +			goto pclk_unprepare;
> +	}
> +
> +	if (!data.ced) {
> +		irq = irq_of_parse_and_map(node, 0);
> +		if (!irq) {
> +			pr_debug("%s: Failed to get PIT64B clockevent IRQ!\n",
> +				 MCHP_PIT64B_NAME);
> +			ret = -ENODEV;
> +			goto gclk_unprepare;
> +		}
> +		ret = mchp_pit64b_dt_init_clkevt(cd, irq);
> +		if (ret)
> +			goto irq_unmap;
> +	} else {
> +		ret = mchp_pit64b_dt_init_clksrc(cd);
> +		if (ret)
> +			goto gclk_unprepare;
> +	}
> +
> +	return 0;
> +
> +irq_unmap:
> +	irq_dispose_mapping(irq);
> +gclk_unprepare:
> +	if (cd->gclk)
> +		clk_disable_unprepare(cd->gclk);
> +pclk_unprepare:
> +	clk_disable_unprepare(cd->pclk);
> +unmap:
> +	iounmap(cd->base);
> +free:
> +	kfree(cd);
> +
> +	return ret;
> +}
> +
> +TIMER_OF_DECLARE(mchp_pit64b_clksrc, "microchip,sam9x60-pit64b",
> +		 mchp_pit64b_dt_init);
> 


-- 
Nicolas Ferre

  reply index

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-03-14 16:26 [PATCH 0/2] add Microchip PIT64B timer Claudiu.Beznea
2019-03-14 16:26 ` [PATCH 1/5] dt-bindings: arm: atmel: add bindings for PIT64B Claudiu.Beznea
2019-03-31  6:40   ` Rob Herring
2019-04-01  8:41   ` Nicolas.Ferre
2019-03-14 16:26 ` [PATCH 2/5] clocksource/drivers/timer-microchip-pit64b: add Microchip PIT64B support Claudiu.Beznea
2019-04-01  8:40   ` Nicolas.Ferre [this message]
2019-04-08  8:43   ` Daniel Lezcano
2019-04-08 11:48     ` Claudiu.Beznea
2019-04-08 12:11     ` Alexandre Belloni
2019-04-08 12:35       ` Daniel Lezcano
2019-04-08 12:42         ` Alexandre Belloni
2019-04-08 13:22           ` Daniel Lezcano
2019-04-08 14:01             ` Alexandre Belloni
2019-05-30  7:46       ` Claudiu.Beznea
2019-05-31 10:41         ` Daniel Lezcano
2019-06-13 14:12           ` Claudiu.Beznea
2019-06-20  8:53             ` Daniel Lezcano
2019-06-21 10:34               ` Claudiu.Beznea
2019-06-24  8:06                 ` Daniel Lezcano
2019-03-14 16:26 ` [PATCH 3/5] MAINTAINERS: change section name to be more generic Claudiu.Beznea
2019-04-01  8:41   ` Nicolas.Ferre
2019-03-14 16:26 ` [PATCH 4/5] MAINTAINERS: add myself as maintainer Claudiu.Beznea
2019-04-01  8:41   ` Nicolas.Ferre
2019-03-14 16:26 ` [PATCH 5/5] MAINTAINERS: add timer-microchip-pit64c.c Claudiu.Beznea
2019-04-01  8:41   ` Nicolas.Ferre

Reply instructions:

You may reply publically 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=a7e95532-697f-161e-709a-196a56ec8857@microchip.com \
    --to=nicolas.ferre@microchip.com \
    --cc=Claudiu.Beznea@microchip.com \
    --cc=Ludovic.Desroches@microchip.com \
    --cc=alexandre.belloni@bootlin.com \
    --cc=daniel.lezcano@linaro.org \
    --cc=devicetree@vger.kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=robh+dt@kernel.org \
    --cc=tglx@linutronix.de \
    /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

LKML Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/lkml/0 lkml/git/0.git
	git clone --mirror https://lore.kernel.org/lkml/1 lkml/git/1.git
	git clone --mirror https://lore.kernel.org/lkml/2 lkml/git/2.git
	git clone --mirror https://lore.kernel.org/lkml/3 lkml/git/3.git
	git clone --mirror https://lore.kernel.org/lkml/4 lkml/git/4.git
	git clone --mirror https://lore.kernel.org/lkml/5 lkml/git/5.git
	git clone --mirror https://lore.kernel.org/lkml/6 lkml/git/6.git
	git clone --mirror https://lore.kernel.org/lkml/7 lkml/git/7.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 lkml lkml/ https://lore.kernel.org/lkml \
		linux-kernel@vger.kernel.org
	public-inbox-index lkml

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-kernel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git