All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jiri Slaby <jirislaby@kernel.org>
To: Frank Li <Frank.Li@nxp.com>, miquel.raynal@bootlin.com
Cc: alexandre.belloni@bootlin.com, conor.culhane@silvaco.com,
	imx@lists.linux.dev, joe@perches.com,
	linux-i3c@lists.infradead.org, linux-kernel@vger.kernel.org,
	zbigniew.lukwinski@linux.intel.com, gregkh@linuxfoundation.org,
	linux-serial@vger.kernel.org
Subject: Re: [PATCH v5 7/7] tty: i3c: add TTY over I3C master support
Date: Tue, 5 Dec 2023 10:56:09 +0100	[thread overview]
Message-ID: <2c3824d7-0960-4de7-8fae-01478fdef8fe@kernel.org> (raw)
In-Reply-To: <20231130224408.3591288-8-Frank.Li@nxp.com>

On 30. 11. 23, 23:44, Frank Li wrote:
> In typical embedded Linux systems, UART consoles require at least two pins,
> TX and RX. In scenarios where I2C/I3C devices like sensors or PMICs are
> present, we can save these two pins by using this driver. Pins is crucial
> resources, especially in small chip packages.
> 
> This introduces support for using the I3C bus to transfer console tty data,
> effectively replacing the need for dedicated UART pins. This not only
> conserves valuable pin resources but also facilitates testing of I3C's
> advanced features, including early termination, in-band interrupt (IBI)
> support, and the creation of more complex data patterns. Additionally,
> it aids in identifying and addressing issues within the I3C controller
> driver.
> 
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
...
> --- a/drivers/tty/Kconfig
> +++ b/drivers/tty/Kconfig
> @@ -412,6 +412,19 @@ config RPMSG_TTY
>   	  To compile this driver as a module, choose M here: the module will be
>   	  called rpmsg_tty.
>   
> +config I3C_TTY
> +	tristate "TTY over I3C"
> +	depends on I3C
> +	help
> +	  Select this options if you'd like use TTY over I3C master controller

this option
to use
add a period to the end.

> +
> +	  This makes it possible for user-space programs to send and receive
> +	  data as a standard tty protocol. I3C provide relatively higher data
> +	  transfer rate and less pin numbers, SDA/SCL are shared with other
> +	  devices.
> +
> +	  If unsure, say N
> +
>   endif # TTY
>   
>   source "drivers/tty/serdev/Kconfig"
...
> --- /dev/null
> +++ b/drivers/tty/i3c_tty.c
> @@ -0,0 +1,443 @@
...
> +struct ttyi3c_port {
> +	struct tty_port port;
> +	int minor;
> +	spinlock_t xlock; /* protect xmit */
> +	char tx_buff[I3C_TTY_TRANS_SIZE];
> +	char rx_buff[I3C_TTY_TRANS_SIZE];

These should be u8 as per the other changes throughout the tty layer.

> +	struct i3c_device *i3cdev;
> +	struct work_struct txwork;
> +	struct work_struct rxwork;
> +	struct completion txcomplete;
> +	unsigned long status;
> +	int buf_overrun;

Can this be ever negative?

> +};
> +
> +struct workqueue_struct *workqueue;

Is this related:
     Still below items not be fixed (according to Jiri Slaby's comments)
     - rxwork thread: need trigger from two position.
     - common thread queue: need some suggestion
?

As I don't remember, could you elaborate again why you need your own 
workqueue? You need to do it in the commit log anyway.

...
> +static ssize_t i3c_write(struct tty_struct *tty, const unsigned char *buf, size_t count)
> +{
> +	struct ttyi3c_port *sport = tty->driver_data;
> +	unsigned long flags;
> +	bool is_empty;
> +	int ret;
> +
> +	spin_lock_irqsave(&sport->xlock, flags);
> +	ret = kfifo_in(&sport->port.xmit_fifo, buf, count);
> +	is_empty = kfifo_is_empty(&sport->port.xmit_fifo);
> +	spin_unlock_irqrestore(&sport->xlock, flags);
> +
> +	if (!is_empty)
> +		queue_work(workqueue, &sport->txwork);
> +
> +	return ret;
> +}
> +
> +static int i3c_put_char(struct tty_struct *tty, unsigned char ch)
> +{
> +	struct ttyi3c_port *sport = tty->driver_data;
> +	unsigned long flags;
> +	int ret = 0;

Unneeded initialization.

> +
> +	spin_lock_irqsave(&sport->xlock, flags);
> +	ret = kfifo_put(&sport->port.xmit_fifo, ch);
> +	spin_unlock_irqrestore(&sport->xlock, flags);
> +
> +	return ret;
> +}
...
> +static void tty_i3c_rxwork(struct work_struct *work)
> +{
> +	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, rxwork);
> +	struct i3c_priv_xfer xfers;
> +	int retry = I3C_TTY_RETRY;

Likely, should be unsigned.

> +	u16 status = BIT(0);
> +	int ret;
> +
> +	memset(&xfers, 0, sizeof(xfers));
> +	xfers.data.in = sport->rx_buff;
> +	xfers.len = I3C_TTY_TRANS_SIZE;
> +	xfers.rnw = 1;
> +
> +	do {
> +		if (test_bit(I3C_TTY_RX_STOP, &sport->status))
> +			break;
> +
> +		i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
> +
> +		if (xfers.actual_len) {
> +			ret = tty_insert_flip_string(&sport->port, sport->rx_buff,
> +						     xfers.actual_len);
> +			if (ret < xfers.actual_len)
> +				sport->buf_overrun++;
> +
> +			retry = I3C_TTY_RETRY;
> +			continue;
> +		}
> +
> +		status = BIT(0);
> +		i3c_device_getstatus_format1(sport->i3cdev, &status);
> +		/*
> +		 * Target side needs some time to fill data into fifo. Target side may not
> +		 * have hardware update status in real time. Software update status always
> +		 * needs some delays.
> +		 *
> +		 * Generally, target side have circular buffer in memory, it will be moved
> +		 * into FIFO by CPU or DMA. 'status' just show if circular buffer empty. But
> +		 * there are gap, especially CPU have not response irq to fill FIFO in time.
> +		 * So xfers.actual will be zero, wait for little time to avoid flood
> +		 * transfer in i3c bus.
> +		 */
> +		usleep_range(I3C_TTY_YIELD_US, 10 * I3C_TTY_YIELD_US);
> +		retry--;
> +
> +	} while (retry && (status & BIT(0)));
> +
> +	tty_flip_buffer_push(&sport->port);
> +}
> +
> +static void tty_i3c_txwork(struct work_struct *work)
> +{
> +	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, txwork);
> +	struct i3c_priv_xfer xfers;
> +	int retry = I3C_TTY_RETRY;

Detto.

> +	unsigned long flags;
> +	int ret;
> +
> +	xfers.rnw = 0;
> +	xfers.data.out = sport->tx_buff;
> +
> +	while (!kfifo_is_empty(&sport->port.xmit_fifo) && retry) {
> +		xfers.len = kfifo_len(&sport->port.xmit_fifo);
> +		xfers.len = min_t(u16, I3C_TTY_TRANS_SIZE, xfers.len);
> +
> +		xfers.len = kfifo_out_peek(&sport->port.xmit_fifo, sport->tx_buff, xfers.len);

Can this simply be:
xfers.len = kfifo_out_peek(&sport->port.xmit_fifo, sport->tx_buff, 
I3C_TTY_TRANS_SIZE);
?

> +
> +		ret = i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
> +		if (ret) {
> +			/*
> +			 * Target side may not move data out of FIFO. delay can't resolve problem,
> +			 * just reduce some possiblity. Target can't end I3C SDR mode write
> +			 * transfer, discard data is reasonable when FIFO overrun.
> +			 */
> +			usleep_range(I3C_TTY_YIELD_US, 10 * I3C_TTY_YIELD_US);
> +			retry--;
> +		} else {
> +			retry = I3C_TTY_RETRY;
> +			ret = kfifo_out(&sport->port.xmit_fifo, sport->tx_buff, xfers.len);

Just to make sure: xfers.len is nor overwritten by 
i3c_device_do_priv_xfers(), right?

> +		}
> +	}
> +
> +	spin_lock_irqsave(&sport->xlock, flags);

Why do you take the lock here, but not during the kfifo operations above?

> +	if (kfifo_is_empty(&sport->port.xmit_fifo))
> +		complete(&sport->txcomplete);
> +	spin_unlock_irqrestore(&sport->xlock, flags);
> +}

> +static int __init i3c_tty_init(void)
> +{
> +	int ret;
> +
> +	i3c_tty_driver = tty_alloc_driver(I3C_TTY_MINORS,
> +					  TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
> +
> +	if (IS_ERR(i3c_tty_driver))
> +		return PTR_ERR(i3c_tty_driver);
> +
> +	i3c_tty_driver->driver_name = "ttyI3C";
> +	i3c_tty_driver->name = "ttyI3C";
> +	i3c_tty_driver->minor_start = 0,
> +	i3c_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
> +	i3c_tty_driver->subtype = SERIAL_TYPE_NORMAL,
> +	i3c_tty_driver->init_termios = tty_std_termios;
> +	i3c_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL |
> +					       CLOCAL;
> +	i3c_tty_driver->init_termios.c_lflag = 0;
> +
> +	tty_set_operations(i3c_tty_driver, &i3c_tty_ops);
> +
> +	ret = tty_register_driver(i3c_tty_driver);
> +	if (ret)
> +		goto err_tty_register_driver;
> +
> +	ret = i3c_driver_register(&i3c_driver);
> +	if (ret)
> +		goto err_i3c_driver_register;
> +
> +	workqueue = alloc_workqueue("ttyI3C", 0, 0);

Can it happen that you already queue something on this wq, while not 
allocated yet? I mean: should this be done first in i3c_tty_init()?

> +	if (!workqueue) {
> +		ret = PTR_ERR(workqueue);
> +		goto err_alloc_workqueue;
> +	}
> +
> +	return 0;
> +
> +err_alloc_workqueue:
> +	i3c_driver_unregister(&i3c_driver);
> +
> +err_i3c_driver_register:
> +	tty_unregister_driver(i3c_tty_driver);
> +
> +err_tty_register_driver:
> +	tty_driver_kref_put(i3c_tty_driver);
> +
> +	return ret;
> +}
> +
> +static void __exit i3c_tty_exit(void)
> +{
> +	i3c_driver_unregister(&i3c_driver);
> +	tty_unregister_driver(i3c_tty_driver);
> +	tty_driver_kref_put(i3c_tty_driver);
> +	idr_destroy(&i3c_tty_minors);

What about the wq?

> +}

regards,
-- 
js
suse labs


WARNING: multiple messages have this Message-ID (diff)
From: Jiri Slaby <jirislaby@kernel.org>
To: Frank Li <Frank.Li@nxp.com>, miquel.raynal@bootlin.com
Cc: alexandre.belloni@bootlin.com, conor.culhane@silvaco.com,
	imx@lists.linux.dev, joe@perches.com,
	linux-i3c@lists.infradead.org, linux-kernel@vger.kernel.org,
	zbigniew.lukwinski@linux.intel.com, gregkh@linuxfoundation.org,
	linux-serial@vger.kernel.org
Subject: Re: [PATCH v5 7/7] tty: i3c: add TTY over I3C master support
Date: Tue, 5 Dec 2023 10:56:09 +0100	[thread overview]
Message-ID: <2c3824d7-0960-4de7-8fae-01478fdef8fe@kernel.org> (raw)
In-Reply-To: <20231130224408.3591288-8-Frank.Li@nxp.com>

On 30. 11. 23, 23:44, Frank Li wrote:
> In typical embedded Linux systems, UART consoles require at least two pins,
> TX and RX. In scenarios where I2C/I3C devices like sensors or PMICs are
> present, we can save these two pins by using this driver. Pins is crucial
> resources, especially in small chip packages.
> 
> This introduces support for using the I3C bus to transfer console tty data,
> effectively replacing the need for dedicated UART pins. This not only
> conserves valuable pin resources but also facilitates testing of I3C's
> advanced features, including early termination, in-band interrupt (IBI)
> support, and the creation of more complex data patterns. Additionally,
> it aids in identifying and addressing issues within the I3C controller
> driver.
> 
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
...
> --- a/drivers/tty/Kconfig
> +++ b/drivers/tty/Kconfig
> @@ -412,6 +412,19 @@ config RPMSG_TTY
>   	  To compile this driver as a module, choose M here: the module will be
>   	  called rpmsg_tty.
>   
> +config I3C_TTY
> +	tristate "TTY over I3C"
> +	depends on I3C
> +	help
> +	  Select this options if you'd like use TTY over I3C master controller

this option
to use
add a period to the end.

> +
> +	  This makes it possible for user-space programs to send and receive
> +	  data as a standard tty protocol. I3C provide relatively higher data
> +	  transfer rate and less pin numbers, SDA/SCL are shared with other
> +	  devices.
> +
> +	  If unsure, say N
> +
>   endif # TTY
>   
>   source "drivers/tty/serdev/Kconfig"
...
> --- /dev/null
> +++ b/drivers/tty/i3c_tty.c
> @@ -0,0 +1,443 @@
...
> +struct ttyi3c_port {
> +	struct tty_port port;
> +	int minor;
> +	spinlock_t xlock; /* protect xmit */
> +	char tx_buff[I3C_TTY_TRANS_SIZE];
> +	char rx_buff[I3C_TTY_TRANS_SIZE];

These should be u8 as per the other changes throughout the tty layer.

> +	struct i3c_device *i3cdev;
> +	struct work_struct txwork;
> +	struct work_struct rxwork;
> +	struct completion txcomplete;
> +	unsigned long status;
> +	int buf_overrun;

Can this be ever negative?

> +};
> +
> +struct workqueue_struct *workqueue;

Is this related:
     Still below items not be fixed (according to Jiri Slaby's comments)
     - rxwork thread: need trigger from two position.
     - common thread queue: need some suggestion
?

As I don't remember, could you elaborate again why you need your own 
workqueue? You need to do it in the commit log anyway.

...
> +static ssize_t i3c_write(struct tty_struct *tty, const unsigned char *buf, size_t count)
> +{
> +	struct ttyi3c_port *sport = tty->driver_data;
> +	unsigned long flags;
> +	bool is_empty;
> +	int ret;
> +
> +	spin_lock_irqsave(&sport->xlock, flags);
> +	ret = kfifo_in(&sport->port.xmit_fifo, buf, count);
> +	is_empty = kfifo_is_empty(&sport->port.xmit_fifo);
> +	spin_unlock_irqrestore(&sport->xlock, flags);
> +
> +	if (!is_empty)
> +		queue_work(workqueue, &sport->txwork);
> +
> +	return ret;
> +}
> +
> +static int i3c_put_char(struct tty_struct *tty, unsigned char ch)
> +{
> +	struct ttyi3c_port *sport = tty->driver_data;
> +	unsigned long flags;
> +	int ret = 0;

Unneeded initialization.

> +
> +	spin_lock_irqsave(&sport->xlock, flags);
> +	ret = kfifo_put(&sport->port.xmit_fifo, ch);
> +	spin_unlock_irqrestore(&sport->xlock, flags);
> +
> +	return ret;
> +}
...
> +static void tty_i3c_rxwork(struct work_struct *work)
> +{
> +	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, rxwork);
> +	struct i3c_priv_xfer xfers;
> +	int retry = I3C_TTY_RETRY;

Likely, should be unsigned.

> +	u16 status = BIT(0);
> +	int ret;
> +
> +	memset(&xfers, 0, sizeof(xfers));
> +	xfers.data.in = sport->rx_buff;
> +	xfers.len = I3C_TTY_TRANS_SIZE;
> +	xfers.rnw = 1;
> +
> +	do {
> +		if (test_bit(I3C_TTY_RX_STOP, &sport->status))
> +			break;
> +
> +		i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
> +
> +		if (xfers.actual_len) {
> +			ret = tty_insert_flip_string(&sport->port, sport->rx_buff,
> +						     xfers.actual_len);
> +			if (ret < xfers.actual_len)
> +				sport->buf_overrun++;
> +
> +			retry = I3C_TTY_RETRY;
> +			continue;
> +		}
> +
> +		status = BIT(0);
> +		i3c_device_getstatus_format1(sport->i3cdev, &status);
> +		/*
> +		 * Target side needs some time to fill data into fifo. Target side may not
> +		 * have hardware update status in real time. Software update status always
> +		 * needs some delays.
> +		 *
> +		 * Generally, target side have circular buffer in memory, it will be moved
> +		 * into FIFO by CPU or DMA. 'status' just show if circular buffer empty. But
> +		 * there are gap, especially CPU have not response irq to fill FIFO in time.
> +		 * So xfers.actual will be zero, wait for little time to avoid flood
> +		 * transfer in i3c bus.
> +		 */
> +		usleep_range(I3C_TTY_YIELD_US, 10 * I3C_TTY_YIELD_US);
> +		retry--;
> +
> +	} while (retry && (status & BIT(0)));
> +
> +	tty_flip_buffer_push(&sport->port);
> +}
> +
> +static void tty_i3c_txwork(struct work_struct *work)
> +{
> +	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, txwork);
> +	struct i3c_priv_xfer xfers;
> +	int retry = I3C_TTY_RETRY;

Detto.

> +	unsigned long flags;
> +	int ret;
> +
> +	xfers.rnw = 0;
> +	xfers.data.out = sport->tx_buff;
> +
> +	while (!kfifo_is_empty(&sport->port.xmit_fifo) && retry) {
> +		xfers.len = kfifo_len(&sport->port.xmit_fifo);
> +		xfers.len = min_t(u16, I3C_TTY_TRANS_SIZE, xfers.len);
> +
> +		xfers.len = kfifo_out_peek(&sport->port.xmit_fifo, sport->tx_buff, xfers.len);

Can this simply be:
xfers.len = kfifo_out_peek(&sport->port.xmit_fifo, sport->tx_buff, 
I3C_TTY_TRANS_SIZE);
?

> +
> +		ret = i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
> +		if (ret) {
> +			/*
> +			 * Target side may not move data out of FIFO. delay can't resolve problem,
> +			 * just reduce some possiblity. Target can't end I3C SDR mode write
> +			 * transfer, discard data is reasonable when FIFO overrun.
> +			 */
> +			usleep_range(I3C_TTY_YIELD_US, 10 * I3C_TTY_YIELD_US);
> +			retry--;
> +		} else {
> +			retry = I3C_TTY_RETRY;
> +			ret = kfifo_out(&sport->port.xmit_fifo, sport->tx_buff, xfers.len);

Just to make sure: xfers.len is nor overwritten by 
i3c_device_do_priv_xfers(), right?

> +		}
> +	}
> +
> +	spin_lock_irqsave(&sport->xlock, flags);

Why do you take the lock here, but not during the kfifo operations above?

> +	if (kfifo_is_empty(&sport->port.xmit_fifo))
> +		complete(&sport->txcomplete);
> +	spin_unlock_irqrestore(&sport->xlock, flags);
> +}

> +static int __init i3c_tty_init(void)
> +{
> +	int ret;
> +
> +	i3c_tty_driver = tty_alloc_driver(I3C_TTY_MINORS,
> +					  TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
> +
> +	if (IS_ERR(i3c_tty_driver))
> +		return PTR_ERR(i3c_tty_driver);
> +
> +	i3c_tty_driver->driver_name = "ttyI3C";
> +	i3c_tty_driver->name = "ttyI3C";
> +	i3c_tty_driver->minor_start = 0,
> +	i3c_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
> +	i3c_tty_driver->subtype = SERIAL_TYPE_NORMAL,
> +	i3c_tty_driver->init_termios = tty_std_termios;
> +	i3c_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL |
> +					       CLOCAL;
> +	i3c_tty_driver->init_termios.c_lflag = 0;
> +
> +	tty_set_operations(i3c_tty_driver, &i3c_tty_ops);
> +
> +	ret = tty_register_driver(i3c_tty_driver);
> +	if (ret)
> +		goto err_tty_register_driver;
> +
> +	ret = i3c_driver_register(&i3c_driver);
> +	if (ret)
> +		goto err_i3c_driver_register;
> +
> +	workqueue = alloc_workqueue("ttyI3C", 0, 0);

Can it happen that you already queue something on this wq, while not 
allocated yet? I mean: should this be done first in i3c_tty_init()?

> +	if (!workqueue) {
> +		ret = PTR_ERR(workqueue);
> +		goto err_alloc_workqueue;
> +	}
> +
> +	return 0;
> +
> +err_alloc_workqueue:
> +	i3c_driver_unregister(&i3c_driver);
> +
> +err_i3c_driver_register:
> +	tty_unregister_driver(i3c_tty_driver);
> +
> +err_tty_register_driver:
> +	tty_driver_kref_put(i3c_tty_driver);
> +
> +	return ret;
> +}
> +
> +static void __exit i3c_tty_exit(void)
> +{
> +	i3c_driver_unregister(&i3c_driver);
> +	tty_unregister_driver(i3c_tty_driver);
> +	tty_driver_kref_put(i3c_tty_driver);
> +	idr_destroy(&i3c_tty_minors);

What about the wq?

> +}

regards,
-- 
js
suse labs


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

  parent reply	other threads:[~2023-12-05  9:56 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-11-30 22:44 [PATCH v5 0/7] i3c: master: some improvment for i3c master Frank Li
2023-11-30 22:44 ` Frank Li
2023-11-30 22:44 ` [PATCH v5 1/7] i3c: master: add enable(disable) hot join in sys entry Frank Li
2023-11-30 22:44   ` Frank Li
2023-11-30 22:44 ` [PATCH v5 2/7] i3c: master: svc: add hot join support Frank Li
2023-11-30 22:44   ` Frank Li
2023-11-30 22:44 ` [PATCH v5 3/7] i3c: add actual_len in i3c_priv_xfer Frank Li
2023-11-30 22:44   ` Frank Li
2023-11-30 22:44 ` [PATCH v5 4/7] i3c: master: svc: rename read_len as actual_len Frank Li
2023-11-30 22:44   ` Frank Li
2023-11-30 22:44 ` [PATCH v5 5/7] i3c: master: svc: return actual transfer data len Frank Li
2023-11-30 22:44   ` Frank Li
2023-11-30 22:44 ` [PATCH v5 6/7] i3c: add API i3c_dev_gettstatus_format1() to get target device status Frank Li
2023-11-30 22:44   ` Frank Li
2023-11-30 22:44 ` [PATCH v5 7/7] tty: i3c: add TTY over I3C master support Frank Li
2023-11-30 22:44   ` Frank Li
2023-12-01 11:59   ` Miquel Raynal
2023-12-01 11:59     ` Miquel Raynal
2023-12-05  9:56   ` Jiri Slaby [this message]
2023-12-05  9:56     ` Jiri Slaby
2023-12-05 15:50     ` Frank Li
2023-12-05 15:50       ` Frank Li

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=2c3824d7-0960-4de7-8fae-01478fdef8fe@kernel.org \
    --to=jirislaby@kernel.org \
    --cc=Frank.Li@nxp.com \
    --cc=alexandre.belloni@bootlin.com \
    --cc=conor.culhane@silvaco.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=imx@lists.linux.dev \
    --cc=joe@perches.com \
    --cc=linux-i3c@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=miquel.raynal@bootlin.com \
    --cc=zbigniew.lukwinski@linux.intel.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.