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);
> +}
> +
>
> ...
>
next prev parent 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).