Linux-i3c Archive mirror
 help / color / mirror / Atom feed
From: Frank Li <Frank.li@nxp.com>
To: Jiri Slaby <jirislaby@kernel.org>
Cc: miquel.raynal@bootlin.com, 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:50:46 -0500	[thread overview]
Message-ID: <ZW9G1qVlYOS2TQ+l@lizhi-Precision-Tower-5810> (raw)
In-Reply-To: <2c3824d7-0960-4de7-8fae-01478fdef8fe@kernel.org>

On Tue, Dec 05, 2023 at 10:56:09AM +0100, Jiri Slaby wrote:
> 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.

I just learn from other drivers, such as USB ACM, usb gadget ACM driver.
If use common workqueue, which one prefered.

I will send fixed version after i3c part accepted.

Frank
> 
> ...
> > +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

      reply	other threads:[~2023-12-05 15:51 UTC|newest]

Thread overview: 11+ 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 ` [PATCH v5 1/7] i3c: master: add enable(disable) hot join in sys entry 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 ` [PATCH v5 3/7] i3c: add actual_len in i3c_priv_xfer 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 ` [PATCH v5 5/7] i3c: master: svc: return actual transfer data len 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 ` [PATCH v5 7/7] tty: i3c: add TTY over I3C master support Frank Li
2023-12-01 11:59   ` Miquel Raynal
2023-12-05  9:56   ` Jiri Slaby
2023-12-05 15:50     ` Frank Li [this message]

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=ZW9G1qVlYOS2TQ+l@lizhi-Precision-Tower-5810 \
    --to=frank.li@nxp.com \
    --cc=alexandre.belloni@bootlin.com \
    --cc=conor.culhane@silvaco.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=imx@lists.linux.dev \
    --cc=jirislaby@kernel.org \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).