linux-serial.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Andrew Morton <akpm@linux-foundation.org>
To: Feng Tang <feng.tang@intel.com>
Cc: Greg Kroah-Hartman <gregkh@suse.de>,
	David Brownell <dbrownell@users.sourceforge.net>,
	Grant Likely <grant.likely@secretlab.ca>,
	spi-devel-list <spi-devel-general@lists.sourceforge.net>,
	"linux-serial@vger.kernel.org" <linux-serial@vger.kernel.org>,
	"alan@lxorguk.ukuu.org.uk" <alan@lxorguk.ukuu.org.uk>,
	Greg KH <greg@kroah.com>
Subject: Re: [RFC][PATCH v2] serial: spi: add spi-uart driver for Maxim 3110
Date: Mon, 8 Feb 2010 16:20:10 -0800	[thread overview]
Message-ID: <20100208162010.b1f69728.akpm@linux-foundation.org> (raw)
In-Reply-To: <20100208165946.0e4dde83@feng-i7>

On Mon, 8 Feb 2010 16:59:46 +0800
Feng Tang <feng.tang@intel.com> wrote:

> Hi All,
> 
> Here is a driver for Maxim 3110 SPI-UART device, please help to review.
> 
> It has been validated with Designware SPI controller (drivers/spi: dw_spi.c &
> dw_spi_pci.c). It supports polling and IRQ mode, supports batch read, and
> provides a console.
> 
> change since v1:
> 	* Address comments from Alan Cox
> 	* Use a "use_irq" module parameter to runtime check whether
> 	  to use IRQ mode
> 	* Add the suspend/resume implementation
> 
> Thanks!
> Feng
> 
> >From 6d14c5d68cdae8d48b6d8a00b6653022f2b100d0 Mon Sep 17 00:00:00 2001
> From: Feng Tang <feng.tang@intel.com>
> Date: Mon, 8 Feb 2010 16:02:59 +0800
> Subject: [PATCH] serial: spi: add spi-uart driver for Maxim 3110
> 
> This is the driver for Max3110 SPI-UART device, which connect
> to host with SPI interface. It supports baud rates from 300 to
> 230400, and supports both polling and IRQ mode, as well as
> providing a console for system use
> 
> Its datasheet could be found here:
> http://datasheets.maxim-ic.com/en/ds/MAX3110E-MAX3111E.pdf
> 

I wonder if this is an "spi" subsystem thing or a "serial" subsystem
thing.  It looks more like a serial driver to me.

> ---
>  drivers/serial/Kconfig      |   12 +
>  drivers/serial/max3110.c    |  848 +++++++++++++++++++++++++++++++++++++++++++
>  drivers/serial/max3110.h    |   59 +++
>  include/linux/serial_core.h |    3 +


> diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
> index 9ff47db..f7daf2f 100644
> --- a/drivers/serial/Kconfig
> +++ b/drivers/serial/Kconfig
>
> ...
>
> +static int use_irq = 1;
> +module_param(use_irq, int, 0444);
> +MODULE_PARM_DESC(use_irq, "Whether using Max3110's IRQ capability");
> +
> +static void receive_chars(struct uart_max3110 *max,
> +				unsigned char *str, int len);
> +static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf);
> +static void max3110_con_receive(struct uart_max3110 *max);
> +
> +int max3110_write_then_read(struct uart_max3110 *max,
> +		const u8 *txbuf, u8 *rxbuf, unsigned len, int always_fast)

The driver has a number of identifiers which have global scope,
apparently unnecessarily.  Please review all such identifiers, see if
they can be made static.

> +{
> +	struct spi_device *spi = max->spi;
> +	struct spi_message	message;
> +	struct spi_transfer	x;
> +	int ret;
> +
> +	spi_message_init(&message);
> +	memset(&x, 0, sizeof x);
> +	x.len = len;
> +	x.tx_buf = txbuf;
> +	x.rx_buf = rxbuf;
> +	spi_message_add_tail(&x, &message);
> +
> +	if (always_fast)
> +		x.speed_hz = spi->max_speed_hz;
> +	else if (max->baud)
> +		x.speed_hz = max->baud;
> +
> +	/* Do the i/o */
> +	ret = spi_sync(spi, &message);
> +	return ret;
> +}
> +
> +/* Write a u16 to the device, and return one u16 read back */
> +int max3110_out(struct uart_max3110 *max, const u16 out)

Another.

> +{
> +	u16 tmp;
> +	int ret;
> +
> +	ret = max3110_write_then_read(max, (u8 *)&out, (u8 *)&tmp, 2, 1);

Something nasty is happening here.  Modifying a u16 via a u8* is to
assume a certain endianness, surely.  Will the driver break when used
on other-endian architectures?

> +	if (ret)
> +		return ret;
> +
> +	/* If some valid data is read back */
> +	if (tmp & MAX3110_READ_DATA_AVAILABLE) {
> +		tmp &= 0xff;
> +		receive_chars(max, (unsigned char *)&tmp, 1);
> +	}
> +
> +	return ret;
> +}
> +
> +#define MAX_READ_LEN	20
> +/*
> + * This is usually used to read data from SPIC RX FIFO, which doesn't
> + * need any delay like flushing character out.
> + * Returns how many valide bytes are read back
> + */
> +static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf)
> +{
> +	u16 out[MAX_READ_LEN], in[MAX_READ_LEN];
> +	u8 *pbuf, valid_str[MAX_READ_LEN];
> +	int i, j, bytelen;
> +
> +	bytelen = len * 2;
> +	memset(out, 0, bytelen);
> +	memset(in, 0, bytelen);
> +
> +	if (max3110_write_then_read(max, (u8 *)out, (u8 *)in, bytelen, 1))
> +		return 0;
>

max3110_write_then_read() can return an error code (from spi_async()). 
But here that error code simply gets lost.  It should be reported to
higher-level code somehow?

> +	/* If caller doesn't provide a buffer, then handle received char */
> +	pbuf = buf ? buf : valid_str;
> +
> +	for (i = 0, j = 0; i < len; i++) {
> +		if (in[i] & MAX3110_READ_DATA_AVAILABLE)
> +			pbuf[j++] = (u8)(in[i] & 0xff);
> +	}
> +
> +	if (j && (pbuf == valid_str))
> +		receive_chars(max, valid_str, j);
> +
> +	return j;
> +}
> +
> +static void serial_m3110_con_putchar(struct uart_port *port, int ch)
> +{
> +	struct uart_max3110 *max =
> +		container_of(port, struct uart_max3110, port);
> +	struct circ_buf *xmit = &max->con_xmit;
> +
> +	if (uart_circ_chars_free(xmit)) {
> +		xmit->buf[xmit->head] = (char)ch;
> +		xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1);
> +	}
> +
> +	if (!atomic_read(&max->con_tx_need)) {
> +		atomic_set(&max->con_tx_need, 1);

This manipulation of con_tx_need looks racy on SMP or preempt.

> +		wake_up_process(max->main_thread);
> +	}
> +}
> +
>
> ...
>
> +#define WORDS_PER_XFER	128
> +static inline void send_circ_buf(struct uart_max3110 *max,
> +				struct circ_buf *xmit)

I suggest that the `inline' be removed.  Modern gcc will take care of
this, if it is of benefit.

> +{
> +	int len, left = 0;
> +	u16 obuf[WORDS_PER_XFER], ibuf[WORDS_PER_XFER];
> +	u8 valid_str[WORDS_PER_XFER];
> +	int i, j;
> +
> +	while (!uart_circ_empty(xmit)) {
> +		left = uart_circ_chars_pending(xmit);
> +		while (left) {
> +			len = (left >= WORDS_PER_XFER) ? WORDS_PER_XFER : left;

Use

			len = min(left, WORDS_PER_XFER);

> +
> +			memset(obuf, 0, len * 2);
> +			memset(ibuf, 0, len * 2);

Using

			memset(obuf, 0, sizeof(obuf));

would remove the assumptions that a) obuf is of type u16 and b) that
sizeof(u16)==2.


> +			for (i = 0; i < len; i++) {
> +				obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG;
> +				xmit->tail = (xmit->tail + 1) &
> +						(UART_XMIT_SIZE - 1);

Could this driver use include/linux/kfifo.h, rather than open-coding it?

> +			}
> +			max3110_write_then_read(max, (u8 *)obuf,
> +						(u8 *)ibuf, len * 2, 0);

Error codes are ignored.

> +			for (i = 0, j = 0; i < len; i++) {
> +				if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE)
> +					valid_str[j++] = (u8)(ibuf[i] & 0xff);
> +			}
> +
> +			if (j)
> +				receive_chars(max, valid_str, j);
> +
> +			max->port.icount.tx += len;
> +			left -= len;
> +		}
> +	}
> +}
> +
>
> ...
>
> +static void max3110_con_receive(struct uart_max3110 *max)
> +{
> +	int loop = 1, num, total = 0;
> +	u8 recv_buf[512], *pbuf;
> +
> +	pbuf = recv_buf;
> +	do {
> +		/* 3110 RX buffer is 8 words */
> +		num = max3110_read_multi(max, 8, pbuf);
> +
> +		if (num) {
> +			loop = 5;
> +			pbuf += num;
> +			total += num;
> +
> +			if (total >= 500) {
> +				receive_chars(max, recv_buf, total);
> +				pbuf = recv_buf;
> +				total = 0;
> +			}
> +		}

hm, what do all the magic numbers do?  Some comments explaining them
would be good.

> +	} while (--loop);
> +
> +	if (total)
> +		receive_chars(max, recv_buf, total);
> +}
> +
> +static int max3110_main_thread(void *_max)
> +{
> +	struct uart_max3110 *max = _max;
> +	wait_queue_head_t *wq = &max->wq;
> +	int ret = 0;
> +	struct circ_buf *xmit = &max->con_xmit;
> +
> +	init_waitqueue_head(wq);
> +	pr_info(PR_FMT "start main thread\n");
> +
> +	do {
> +		wait_event_interruptible(*wq, (atomic_read(&max->irq_pending) ||
> +					       atomic_read(&max->con_tx_need) ||
> +					     atomic_read(&max->uart_tx_need)) ||
> +					     kthread_should_stop());
> +		max->mthread_up = 1;
> +
> +		if (use_irq && atomic_read(&max->irq_pending)) {
> +			max3110_con_receive(max);
> +			atomic_set(&max->irq_pending, 0);
> +		}

The manipulation of irq_pending looks racy.  Perhaps something list
test_and_clear_bit() would fix that.


> +		/* First handle console output */
> +		if (atomic_read(&max->con_tx_need)) {
> +			send_circ_buf(max, xmit);
> +			atomic_set(&max->con_tx_need, 0);
> +		}

Ditto.

> +		/* Handle uart output */
> +		if (atomic_read(&max->uart_tx_need)) {
> +			transmit_char(max);
> +			atomic_set(&max->uart_tx_need, 0);
> +		}

Ditto.

> +		max->mthread_up = 0;
> +	} while (!kthread_should_stop());
> +
> +	return ret;
> +}
> +
> +irqreturn_t static serial_m3110_irq(int irq, void *dev_id)

`static irqreturn_t' would be more typical.

> +{
> +	struct uart_max3110 *max = dev_id;
> +
> +	/* max3110's irq is a falling edge, not level triggered,
> +	 * so no need to disable the irq */
> +	if (!atomic_read(&max->irq_pending)) {
> +		atomic_inc(&max->irq_pending);

racy?

> +		wake_up_process(max->main_thread);
> +	}
> +	return IRQ_HANDLED;
> +}
> +
> +/* If don't use RX IRQ, then need a thread to polling read */
> +static int max3110_read_thread(void *_max)
> +{
> +	struct uart_max3110 *max = _max;
> +
> +	pr_info(PR_FMT "start read thread\n");
> +	do {
> +		if (!max->mthread_up)
> +			max3110_con_receive(max);
> +
> +		set_current_state(TASK_INTERRUPTIBLE);
> +		schedule_timeout(HZ / 20);

Use schedule_timeout_interruptible()

> +	} while (!kthread_should_stop());
> +
> +	return 0;
> +}
> +
> +static int serial_m3110_startup(struct uart_port *port)
> +{
> +	struct uart_max3110 *max =
> +		container_of(port, struct uart_max3110, port);
> +	u16 config = 0;
> +	int ret = 0;
> +
> +	if (port->line != 0)
> +		pr_err(PR_FMT "uart port startup failed\n");
> +
> +	/* Firstly disable all IRQ and config it to 115200, 8n1 */
> +	config = WC_TAG | WC_FIFO_ENABLE
> +			| WC_1_STOPBITS
> +			| WC_8BIT_WORD
> +			| WC_BAUD_DR2;
> +	ret = max3110_out(max, config);
> +
> +	/* As we use thread to handle tx/rx, need set low latency */
> +	port->state->port.tty->low_latency = 1;
> +
> +	if (use_irq) {
> +		ret = request_irq(max->irq, serial_m3110_irq,
> +					IRQ_TYPE_EDGE_FALLING, "max3110", max);
> +		if (ret)
> +			return ret;
> +
> +		/* Enable RX IRQ only */
> +		config |= WC_RXA_IRQ_ENABLE;
> +		max3110_out(max, config);
> +	} else {
> +		/* if IRQ is disabled, start a read thread for input data */
> +		max->read_thread =
> +			kthread_run(max3110_read_thread, max, "max3110_read");

Check for kthread_run() failure?

> +	}
> +
> +	max->cur_conf = config;
> +	return 0;
> +}
> +
>
> ...
>
> +static int serial_m3110_probe(struct spi_device *spi)

Should this be __init or __devinit?

> +{
> +	struct uart_max3110 *max;
> +	int ret;
> +	unsigned char *buffer;
> +
> +	max = kzalloc(sizeof(*max), GFP_KERNEL);
> +	if (!max)
> +		return -ENOMEM;
> +
> +	/* set spi info */
> +	spi->mode = SPI_MODE_0;
> +	spi->bits_per_word = 16;
> +#ifdef CONFIG_MAX3110_DESIGNWARE
> +	spi->controller_data = &spi_uart;
> +#endif
> +	spi_setup(spi);
> +
> +	max->clock = MAX3110_HIGH_CLK;
> +	max->port.type = PORT_MAX3110;
> +	max->port.fifosize = 2;		/* only have 16b buffer */
> +	max->port.ops = &serial_m3110_ops;
> +	max->port.line = 0;
> +	max->port.dev = &spi->dev;
> +	max->port.uartclk = 115200;
> +
> +	max->spi = spi;
> +	max->name = spi->modalias;	/* use spi name as the name */
> +	max->irq = (u16)spi->irq;
> +
> +	spin_lock_init(&max->lock);
> +
> +	max->word_7bits = 0;
> +	max->parity = 0;
> +	max->baud = 0;
> +
> +	max->cur_conf = 0;
> +	atomic_set(&max->irq_pending, 0);
> +
> +	buffer = (unsigned char *)__get_free_page(GFP_KERNEL);
> +	if (!buffer) {
> +		ret = -ENOMEM;
> +		goto err_get_page;
> +	}
> +	max->con_xmit.buf = (unsigned char *)buffer;
> +	max->con_xmit.head = max->con_xmit.tail = 0;
> +
> +	max->main_thread = kthread_run(max3110_main_thread,
> +					max, "max3110_main");
> +	if (IS_ERR(max->main_thread)) {
> +		ret = PTR_ERR(max->main_thread);
> +		goto err_kthread;
> +	}
> +
> +	pmax = max;
> +	/* Give membase a psudo value to pass serial_core's check */
> +	max->port.membase = (void *)0xff110000;
> +	uart_add_one_port(&serial_m3110_reg, &max->port);
> +
> +	return 0;
> +
> +err_kthread:
> +	free_page((unsigned long)buffer);
> +err_get_page:
> +	pmax = NULL;
> +	kfree(max);
> +	return ret;
> +}
> +
>
> ...
>
> +static struct spi_driver uart_max3110_driver = {
> +	.driver = {
> +			.name	= "spi_max3111",
> +			.bus	= &spi_bus_type,
> +			.owner	= THIS_MODULE,
> +	},
> +	.probe		= serial_m3110_probe,
> +	.remove		= __devexit_p(max3110_remove),
> +	.suspend	= serial_m3110_suspend,
> +	.resume		= serial_m3110_resume,
> +};
> +
> +int __init serial_m3110_init(void)

static?

> +{
> +	int ret = 0;
> +
> +	ret = uart_register_driver(&serial_m3110_reg);
> +	if (ret)
> +		return ret;
> +
> +	ret = spi_register_driver(&uart_max3110_driver);
> +	if (ret)
> +		uart_unregister_driver(&serial_m3110_reg);
> +
> +	return ret;
> +}
> +
> +void __exit serial_m3110_exit(void)

static?

> +{
> +	spi_unregister_driver(&uart_max3110_driver);
> +	uart_unregister_driver(&serial_m3110_reg);
> +}
> +
>
> ...
>


  reply	other threads:[~2010-02-09  0:20 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-12-29 14:20 [RFC][PATCH] serial: spi: add spi-uart driver for Maxim 3110 Feng Tang
2009-12-29 14:59 ` [spi-devel-general] " Baruch Siach
2009-12-29 16:05   ` Tang, Feng
2009-12-29 18:43     ` Erwin Authried
2009-12-30  1:54       ` Feng Tang
2010-02-25  4:47       ` David Brownell
2010-02-25  7:49         ` Feng Tang
2009-12-29 15:02 ` Alan Cox
2010-02-08  8:59 ` [RFC][PATCH v2] " Feng Tang
2010-02-09  0:20   ` Andrew Morton [this message]
2010-02-09  0:26     ` Grant Likely
2010-02-09  1:36       ` Feng Tang
2010-02-17 22:58         ` Greg KH
2010-02-24  5:11           ` [RFC][PATCH v3] " Feng Tang
2010-02-24 10:44             ` Alan Cox
2010-02-24 14:25               ` Grant Likely
2010-02-24 23:18             ` Andrew Morton
2010-02-25  6:39               ` Feng Tang
2010-02-25  4:43             ` David Brownell
2010-02-25  7:44               ` Feng Tang
2010-02-25  8:11                 ` David Brownell
2010-02-26  3:47             ` [PATCH v4] " Feng Tang
2010-02-26  9:59               ` Masakazu Mokuno
2010-02-26 19:41                 ` David Brownell
2010-03-01  2:30                   ` Feng Tang
2010-03-02  3:38                 ` Feng Tang
2010-02-09  9:25     ` [RFC][PATCH v2] " Alan Cox
2010-03-03  2:57   ` [PATCH v5] " Feng Tang
2010-03-03  3:59     ` Grant Likely
2010-03-03  4:51     ` David Brownell
2010-03-03  5:52       ` Feng Tang
2010-03-03  6:16         ` David Brownell
2010-03-03  6:37           ` Feng Tang
2010-03-03  7:25             ` David Brownell
2010-03-03  7:42               ` Feng Tang

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=20100208162010.b1f69728.akpm@linux-foundation.org \
    --to=akpm@linux-foundation.org \
    --cc=alan@lxorguk.ukuu.org.uk \
    --cc=dbrownell@users.sourceforge.net \
    --cc=feng.tang@intel.com \
    --cc=grant.likely@secretlab.ca \
    --cc=greg@kroah.com \
    --cc=gregkh@suse.de \
    --cc=linux-serial@vger.kernel.org \
    --cc=spi-devel-general@lists.sourceforge.net \
    /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 NNTP newsgroup(s).