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: 37+ 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 3:47 ` 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
2010-02-08 8:59 ` [RFC][PATCH v2] " 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 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.