All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ben Dooks <ben-linux-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org>
To: dmitry pervushin
	<dpervushin-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
Cc: Ben Dooks <ben-linux-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org>,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Subject: Re: [PATCH, RFC] Freescale STMP: i2c driver
Date: Mon, 8 Jun 2009 23:50:34 +0100	[thread overview]
Message-ID: <20090608225034.GA20446@fluff.org.uk> (raw)
In-Reply-To: <1244059155.4074.19.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>

On Wed, Jun 03, 2009 at 11:59:15PM +0400, dmitry pervushin wrote:
> Hello Ben & i2c people,
> 
> Here is the patch to support I2C bus on Freescale STMP378x platform; any
> comments are welcome
> 
> Signed-off-by: Dmitrij Frasenyak <d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>

should really have a from Dmitrij Frasenyak line as he is the original
authour and you're just pushing it, IIRC.

> Signed-off-by: dmitry pervishin <dpervushin-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
> 
> ---
>  drivers/i2c/busses/Kconfig        |    7 
>  drivers/i2c/busses/Makefile       |    1 
>  drivers/i2c/busses/i2c-stmp378x.c |  440 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 448 insertions(+)
> 
> Index: linux-2.6-arm/drivers/i2c/busses/Kconfig
> ===================================================================
> --- linux-2.6-arm.orig/drivers/i2c/busses/Kconfig
> +++ linux-2.6-arm/drivers/i2c/busses/Kconfig
> @@ -513,6 +513,13 @@ config I2C_SIMTEC
>  	  This driver can also be built as a module. If so, the module
>  	  will be called i2c-simtec.
>  
> +config I2C_STMP378X
> +	tristate "STMP378x I2C adapter"
> +	depends on ARCH_STMP378X
> +	help
> +	  Include support of I2C adapter on Freescale STMP378x board; to build
> +	  the driver as a module, say M here - module will be called i2c-stmp378x.
> +
>  config I2C_VERSATILE
>  	tristate "ARM Versatile/Realview I2C bus support"
>  	depends on ARCH_VERSATILE || ARCH_REALVIEW
> Index: linux-2.6-arm/drivers/i2c/busses/Makefile
> ===================================================================
> --- linux-2.6-arm.orig/drivers/i2c/busses/Makefile
> +++ linux-2.6-arm/drivers/i2c/busses/Makefile
> @@ -48,6 +48,7 @@ obj-$(CONFIG_I2C_S6000)		+= i2c-s6000.o
>  obj-$(CONFIG_I2C_SH7760)	+= i2c-sh7760.o
>  obj-$(CONFIG_I2C_SH_MOBILE)	+= i2c-sh_mobile.o
>  obj-$(CONFIG_I2C_SIMTEC)	+= i2c-simtec.o
> +obj-$(CONFIG_I2C_STMP378X)	+= i2c-stmp378x.o
>  obj-$(CONFIG_I2C_VERSATILE)	+= i2c-versatile.o
>  
>  # External I2C/SMBus adapter drivers
> Index: linux-2.6-arm/drivers/i2c/busses/i2c-stmp378x.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6-arm/drivers/i2c/busses/i2c-stmp378x.c
> @@ -0,0 +1,440 @@
> +/*
> + * Freescale STMP378X I2C bus driver
> + *
> + * Author: Dmitrij Frasenyak <sed-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
> + *
> + * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
> + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
> + */
> +
> +/*
> + * The code contained herein is licensed under the GNU General Public
> + * License. You may obtain a copy of the GNU General Public License
> + * Version 2 or later at the following locations:
> + *
> + * http://www.opensource.org/licenses/gpl-license.html
> + * http://www.gnu.org/copyleft/gpl.html
> + */
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/completion.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/dma-mapping.h>
> +
> +#include <mach/platform.h>
> +#include <mach/stmp3xxx.h>
> +#include <mach/dma.h>
> +#include <mach/regs-i2c.h>
> +#include <mach/regs-apbx.h>
> +
> +static const u32 I2C_READ = 1,
> +		 I2C_WRITE = 0;

do you really want to be defining things with a prefix of I2C, that
might end up clashing with the i2c core?

> +static const struct stmp3xxx_dma_command cmd_i2c_select = {
> +	.cmd =	BF(1, APBX_CHn_CMD_XFER_COUNT)	|
> +		BF(1, APBX_CHn_CMD_CMDWORDS)	|
> +		BM_APBX_CHn_CMD_WAIT4ENDCMD	|
> +		BM_APBX_CHn_CMD_CHAIN		|
> +		BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND),

what is BF() ?

> +	.pio_words = {
> +		[0] = 	BM_I2C_CTRL0_RETAIN_CLOCK   |
> +			BM_I2C_CTRL0_PRE_SEND_START |
> +			BM_I2C_CTRL0_MASTER_MODE    |
> +			BM_I2C_CTRL0_DIRECTION      |
> +			BF(1, I2C_CTRL0_XFER_COUNT),
> +	},
> +};
> +
> +static const struct stmp3xxx_dma_command cmd_i2c_write = {
> +	.cmd =	BM_APBX_CHn_CMD_SEMAPHORE	|
> +		BF(1, APBX_CHn_CMD_CMDWORDS)	|
> +		BM_APBX_CHn_CMD_WAIT4ENDCMD	|
> +		BM_APBX_CHn_CMD_IRQONCMPLT	|
> +		BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND),
> +	.pio_words = {
> +		[0] =	BM_I2C_CTRL0_PRE_SEND_START |
> +			BM_I2C_CTRL0_MASTER_MODE    |
> +			BM_I2C_CTRL0_DIRECTION,
> +	},
> +};
> +
> +static const struct stmp3xxx_dma_command cmd_i2c_read = {
> +	.cmd =	BM_APBX_CHn_CMD_SEMAPHORE	|
> +		BF(1, APBX_CHn_CMD_CMDWORDS)	|
> +		BM_APBX_CHn_CMD_WAIT4ENDCMD	|
> +		BM_APBX_CHn_CMD_IRQONCMPLT	|
> +		BF(BV_APBX_CHn_CMD_COMMAND__DMA_WRITE, APBX_CHn_CMD_COMMAND),
> +	.pio_words = {
> +		[0] =	BM_I2C_CTRL0_SEND_NAK_ON_LAST	|
> +			BM_I2C_CTRL0_MASTER_MODE,
> +	},
> +};
> +
> +struct stmp378x_i2c_dev {
> +	struct device		*dev;
> +	int			irq;
> +	struct completion	cmd_complete;
> +	u32			cmd_err;
> +	struct i2c_adapter	adapter;
> +	void __iomem 		*io;
> +	unsigned		dma;
> +
> +	dma_addr_t		bufp;
> +	unsigned char		*bufv;
> +
> +	struct stmp3xxx_dma_descriptor i2c_dma_read[2],
> +				       i2c_dma_write;
> +};

would be nice to see some kerneldoc descriptions for the fields in
this structure.

> +static int stmp378x_i2c_init(struct stmp378x_i2c_dev *dev)
> +{
> +	int err;
> +
> +	err = stmp3xxx_dma_request(dev->dma, dev->dev, dev_name(dev->dev));
> +	if (err)
> +		goto out;
> +	stmp3xxx_reset_block(dev->io, true);
> +
> +	dev->bufv = dma_alloc_coherent(dev->dev, PAGE_SIZE,
> +				       &dev->bufp, GFP_KERNEL);
> +	if (dma_mapping_error(dev->dev, dev->bufp))
> +		goto out_dma;

hmm, doesn't dma_alloc_coherent() fail with an NULL buffer return?

> +	err = stmp3xxx_request_pin_group(dev->dev->platform_data,
> +			dev_name(dev->dev));
> +	if (err)
> +		goto out_free;
> +
> +	err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_read[0]);
> +	if (err)
> +		goto out_rd0;
> +	err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_read[1]);
> +	if (err)
> +		goto out_rd1;
> +	err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_write);
> +	if (err)
> +		goto out_wr;
> +
> +	stmp3xxx_dma_reset_channel(dev->dma);
> +	stmp3xxx_dma_clear_interrupt(dev->dma);
> +	stmp3xxx_dma_enable_interrupt(dev->dma);
> +
> +	return 0;
> +/*
> +	stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_write);
> +*/
> +out_wr:
> +	stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[1]);
> +out_rd1:
> +	stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[0]);
> +out_rd0:
> +	stmp3xxx_release_pin_group(dev->dev->platform_data, dev_name(dev->dev));
> +out_free:
> +	dma_free_coherent(dev->dev, PAGE_SIZE, dev->bufv, dev->bufp);
> +out_dma:
> +	stmp3xxx_dma_release(dev->dma);
> +out:
> +	return err;
> +}
> +
> +static void stmp378x_i2c_release(struct stmp378x_i2c_dev *dev)
> +{
> +	stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_write);
> +	stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[1]);
> +	stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[0]);
> +	dma_free_coherent(dev->dev, PAGE_SIZE, dev->bufv, dev->bufp);
> +	stmp3xxx_release_pin_group(dev->dev->platform_data, dev_name(dev->dev));
> +	stmp3xxx_dma_release(dev->dma);
> +}
> +/*
> + * Low level master read/write transaction.
> + */
> +static int stmp378x_i2c_xfer_msg(struct i2c_adapter *adap,
> +				 struct i2c_msg *msg, int stop)
> +{
> +	struct stmp378x_i2c_dev *dev = i2c_get_adapdata(adap);
> +	int err;
> +	struct stmp3xxx_dma_descriptor *c, *r;
> +
> +	init_completion(&dev->cmd_complete);
> +	dev->cmd_err = 0;
> +
> +	dev_dbg(dev->dev, "start XFER\n");
> +	dev_dbg(dev->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
> +		msg->addr, msg->len, msg->flags, stop);
> +
> +	if ((msg->len == 0) || (msg->len > (PAGE_SIZE - 1)))
> +		return -EINVAL;
> +
> +	if (msg->flags & I2C_M_RD) {
> +
> +		c = &dev->i2c_dma_read[0];
> +		r = &dev->i2c_dma_read[1];
> +
> +		/* first, setup the SELECT command */
> +		memcpy(c->command, &cmd_i2c_select, sizeof(cmd_i2c_select));
> +		c->command->buf_ptr = dev->bufp;
> +		dev->bufv[0] = msg->addr | I2C_READ;
> +
> +		/* then, setup the READ command */
> +		memcpy(r->command, &cmd_i2c_read, sizeof(cmd_i2c_read));
> +		r->command->cmd |= BF(msg->len, APBX_CHn_CMD_XFER_COUNT);
> +		r->command->pio_words[0] |= BF(msg->len, I2C_CTRL0_XFER_COUNT);
> +		if (stop)
> +			r->command->pio_words[0] |= BM_I2C_CTRL0_POST_SEND_STOP;
> +		/*
> +		 * yes, STMP controller can DMA transfer the data from any
> +		 * byte boundary
> +		 */
> +		r->command->buf_ptr = dev->bufp + 1;
> +
> +		/* and chain them together */
> +		c->command->next = r->handle;
> +
> +	} else {
> +
> +		c = &dev->i2c_dma_write;
> +		memcpy(c->command, &cmd_i2c_write, sizeof(cmd_i2c_write));
> +		c->command->cmd |= BF(msg->len + 1, APBX_CHn_CMD_XFER_COUNT);
> +		c->command->pio_words[0] |=
> +				   BF(msg->len + 1, I2C_CTRL0_XFER_COUNT);
> +		if (stop)
> +			c->command->pio_words[0] |=
> +				   BM_I2C_CTRL0_POST_SEND_STOP;
> +		dev->bufv[0] = msg->addr | I2C_WRITE;
> +		memcpy(dev->bufv + 1, msg->buf, msg->len);
> +	}
> +
> +	stmp3xxx_dma_go(dev->dma, c, 1);
> +
> +	err = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
> +							msecs_to_jiffies(1000));
> +
> +	if (err < 0) {
> +		dev_dbg(dev->dev, "controler is timed out\n");
> +		return -ETIMEDOUT;
> +	}
> +	if (!dev->cmd_err && (msg->flags & I2C_M_RD))
> +		memcpy(msg->buf, dev->bufv + 1, msg->len);
> +
> +	dev_dbg(dev->dev, "done with err=%d\n", dev->cmd_err);
> +
> +	return dev->cmd_err;
> +}
> +
> +
> +static int
> +stmp378x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
> +{
> +	int i;
> +	int err;
> +
> +	if (!msgs->len)
> +		return -EINVAL;
> +
> +	for (i = 0; i < num; i++) {
> +		err = stmp378x_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
> +		if (err)
> +			return err;
> +	}
> +	return num;
> +}
> +
> +static u32
> +stmp378x_i2c_func(struct i2c_adapter *adap)
> +{
> +	return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
> +}
> +
> +#define I2C_IRQ_MASK 0x000000FF
> +static irqreturn_t
> +stmp378x_i2c_isr(int this_irq, void *dev_id)
> +{
> +	struct stmp378x_i2c_dev *dev = dev_id;
> +	u32 stat;
> +	u32 done_mask = BM_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ |
> +			BM_I2C_CTRL1_BUS_FREE_IRQ ;
> +
> +	stat = readl(HW_I2C_CTRL1 + dev->io) & I2C_IRQ_MASK;
> +	if (!stat)
> +		return IRQ_NONE;
> +
> +	if (stat & BM_I2C_CTRL1_NO_SLAVE_ACK_IRQ) {
> +		dev->cmd_err = -EREMOTEIO;
> +
> +		/*
> +		 * Stop DMA
> +		 * Clear NAK
> +		 */
> +		stmp3xxx_setl(BM_I2C_CTRL1_CLR_GOT_A_NAK,
> +			      HW_I2C_CTRL1 + dev->io);
> +		stmp3xxx_dma_reset_channel(dev->dma);
> +		stmp3xxx_dma_clear_interrupt(dev->dma);
> +
> +		complete(&dev->cmd_complete);
> +
> +		goto done;
> +	}
> +
> +	/* Don't care about  BM_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ */
> +	if (stat & (BM_I2C_CTRL1_EARLY_TERM_IRQ |
> +		    BM_I2C_CTRL1_MASTER_LOSS_IRQ |
> +		    BM_I2C_CTRL1_SLAVE_STOP_IRQ |
> +		    BM_I2C_CTRL1_SLAVE_IRQ)) {
> +		dev->cmd_err = -EIO;
> +		complete(&dev->cmd_complete);
> +		goto done;
> +	}
> +
> +	if ((stat & done_mask) == done_mask)
> +		complete(&dev->cmd_complete);
> +
> +done:
> +	stmp3xxx_clearl(stat, dev->io + HW_I2C_CTRL1);
> +	return IRQ_HANDLED;
> +}
> +
> +static const struct i2c_algorithm stmp378x_i2c_algo = {
> +	.master_xfer	= stmp378x_i2c_xfer,
> +	.functionality	= stmp378x_i2c_func,
> +};
> +
> +
> +static int __devinit
> +stmp378x_i2c_probe(struct platform_device *pdev)
> +{
> +	struct stmp378x_i2c_dev	*dev;
> +	struct i2c_adapter	*adap;
> +	struct resource		*mem, *dma;
> +	int err = 0;
> +
> +	/* NOTE: driver uses the static register mapping */
> +	dev = kzalloc(sizeof(struct stmp378x_i2c_dev), GFP_KERNEL);
> +	if (!dev) {
> +		dev_err(&pdev->dev, "no mem \n");
> +		return -ENOMEM;
> +	}
> +
> +	dev->irq = platform_get_irq(pdev, 0); /* Error */
> +	if (dev->irq < 0) {
> +		dev_err(&pdev->dev, "no err_irq resource\n");
> +		err = -ENODEV;
> +		goto nores;
> +	}

-ENODEV isn't what you want here, you'll end up losing errors in the
higher level.

> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!mem) {
> +		dev_err(&pdev->dev, "no memory window\n");
> +		err = -ENODEV;
> +		goto nores;
> +	}
> +
> +	dma = platform_get_resource(pdev, IORESOURCE_DMA, 0);
> +	if (!dma) {
> +		dev_err(&pdev->dev, "no dma channel\n");
> +		err = -ENODEV;
> +		goto nores;
> +	}
> +	dev->dma = dma->start;
> +
> +	dev->dev = &pdev->dev;
> +	dev->io = ioremap(mem->start, resource_size(mem));
> +	if (!dev->io) {
> +		dev_err(&pdev->dev, "cannot ioremap\n");
> +		err = -ENXIO;
> +		goto nores;
> +	}
> +
> +	err = request_irq(dev->irq, stmp378x_i2c_isr, 0, pdev->name, dev);
> +	if (err) {
> +		dev_err(&pdev->dev, "Can't get IRQ\n");
> +		goto no_err_irq;
> +	}
> +
> +	err = stmp378x_i2c_init(dev);
> +	if (err) {
> +		dev_err(&pdev->dev, "HW Init failed\n");
> +		goto init_failed;
> +	}
> +
> +	stmp3xxx_setl(0xFF00, dev->io + HW_I2C_CTRL1);
> +
> +	adap = &dev->adapter;
> +	i2c_set_adapdata(adap, dev);
> +	adap->owner = THIS_MODULE;
> +	adap->class = I2C_CLASS_HWMON;

is this the correct class?

> +	strncpy(adap->name, "378x I2C adapter", sizeof(adap->name));
> +	adap->algo = &stmp378x_i2c_algo;
> +	adap->dev.parent = &pdev->dev;
> +
> +	adap->nr = pdev->id;
> +	err = i2c_add_numbered_adapter(adap);
> +	if (err) {
> +		dev_err(&pdev->dev, "Failed to add adapter\n");
> +		goto no_i2c_adapter;
> +
> +	}
> +
> +	return 0;
> +
> +no_i2c_adapter:
> +	stmp378x_i2c_release(dev);
> +init_failed:
> +	free_irq(dev->irq, dev);
> +no_err_irq:
> +	iounmap(dev->io);
> +nores:
> +	kfree(dev);
> +	return err;
> +}
> +
> +static int __devexit
> +stmp378x_i2c_remove(struct platform_device *pdev)
> +{
> +	struct stmp378x_i2c_dev	*dev = platform_get_drvdata(pdev);
> +	int res;
> +
> +	res = i2c_del_adapter(&dev->adapter);
> +	if (res)
> +		return -EBUSY;
> +
> +	stmp378x_i2c_release(dev);
> +
> +	platform_set_drvdata(pdev, NULL);
> +
> +	free_irq(dev->irq, dev);
> +	iounmap(dev->io);
> +
> +	kfree(dev);
> +	return 0;
> +}
> +
> +static struct platform_driver stmp378x_i2c_driver = {
> +	.probe		= stmp378x_i2c_probe,
> +	.remove		= __devexit_p(stmp378x_i2c_remove),
> +	.driver		= {
> +		.name	= "i2c_stmp",
> +		.owner	= THIS_MODULE,
> +	},
> +};
> +
> +static int __init stmp378x_i2c_init_driver(void)
> +{
> +	return platform_driver_register(&stmp378x_i2c_driver);
> +}
> +module_init(stmp378x_i2c_init_driver);
> +
> +static void __exit stmp378x_i2c_exit_driver(void)
> +{
> +	platform_driver_unregister(&stmp378x_i2c_driver);
> +}
> +module_exit(stmp378x_i2c_exit_driver);
> +
> +MODULE_AUTHOR("d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org");
> +MODULE_DESCRIPTION("I2C for Freescale STMP378x");
> +MODULE_LICENSE("GPL");

no module alias for autoloading?
 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
Ben (ben-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org, http://www.fluff.org/)

  'a smiley only costs 4 bytes'

  parent reply	other threads:[~2009-06-08 22:50 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-06-03 19:59 [PATCH, RFC] Freescale STMP: i2c driver dmitry pervushin
     [not found] ` <1244059155.4074.19.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>
2009-06-08 22:50   ` Ben Dooks [this message]
     [not found]     ` <20090608225034.GA20446-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
2009-06-09  7:46       ` Jean Delvare
2009-06-16 16:25       ` dmitry pervushin
     [not found]         ` <1245169539.32549.12.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>
2009-06-22 10:51           ` Ben Dooks
     [not found]             ` <20090622105148.GC9006-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
2009-06-22 13:41               ` dmitry pervushin
2009-06-15  8:19   ` Ben Dooks
     [not found]     ` <20090615081938.GA19878-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
2009-06-16 16:31       ` dmitry pervushin
     [not found]         ` <1245169913.32549.19.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>
2009-06-22  0:47           ` Ben Dooks
     [not found]             ` <20090622004741.GB9006-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
2009-06-22 13:39               ` dmitry pervushin
2009-06-22 13:42               ` [PATCH, RFC] Freescale STMP: i2c driver, updated with correct signed-off lines dmitry pervushin

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=20090608225034.GA20446@fluff.org.uk \
    --to=ben-linux-elnmno+kys3ytjvyw6ydsg@public.gmane.org \
    --cc=dpervushin-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org \
    --cc=linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.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.