From patchwork Thu Apr 22 17:55:03 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mika Westerberg X-Patchwork-Id: 94181 Received: from lists.sourceforge.net (lists.sourceforge.net [216.34.181.88]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o3MHwm1V026871 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Thu, 22 Apr 2010 17:59:24 GMT Received: from localhost ([127.0.0.1] helo=sfs-ml-3.v29.ch3.sourceforge.com) by sfs-ml-3.v29.ch3.sourceforge.com with esmtp (Exim 4.69) (envelope-from ) id 1O50fo-0006ZF-7F; Thu, 22 Apr 2010 17:58:48 +0000 Received: from sfi-mx-2.v28.ch3.sourceforge.com ([172.29.28.122] helo=mx.sourceforge.net) by sfs-ml-3.v29.ch3.sourceforge.com with esmtp (Exim 4.69) (envelope-from ) id 1O50fm-0006Yv-Cv for spi-devel-general@lists.sourceforge.net; Thu, 22 Apr 2010 17:58:46 +0000 Received-SPF: pass (sfi-mx-2.v28.ch3.sourceforge.com: domain of gmail.com designates 72.14.220.153 as permitted sender) client-ip=72.14.220.153; envelope-from=mika.westerberg@gmail.com; helo=fg-out-1718.google.com; Received: from fg-out-1718.google.com ([72.14.220.153]) by sfi-mx-2.v28.ch3.sourceforge.com with esmtp (Exim 4.69) id 1O50fl-00085H-1Y for spi-devel-general@lists.sourceforge.net; Thu, 22 Apr 2010 17:58:46 +0000 Received: by fg-out-1718.google.com with SMTP id 19so497074fgg.10 for ; Thu, 22 Apr 2010 10:58:38 -0700 (PDT) Received: by 10.223.63.16 with SMTP id z16mr238616fah.44.1271958907218; Thu, 22 Apr 2010 10:55:07 -0700 (PDT) Received: from gw.healthdatacare.com (a88-112-70-218.elisa-laajakaista.fi [88.112.70.218]) by mx.google.com with ESMTPS id p17sm333024fka.46.2010.04.22.10.55.05 (version=SSLv3 cipher=RC4-MD5); Thu, 22 Apr 2010 10:55:06 -0700 (PDT) Date: Thu, 22 Apr 2010 20:55:03 +0300 From: Mika Westerberg To: H Hartley Sweeten Message-ID: <20100422175503.GE26418@gw.healthdatacare.com> References: <0D753D10438DA54287A00B0270842697636D85BCCC@AUSP01VMBX24.collaborationhost.net> <0D753D10438DA54287A00B0270842697636D85BDD8@AUSP01VMBX24.collaborationhost.net> <0D753D10438DA54287A00B0270842697636D85BE26@AUSP01VMBX24.collaborationhost.net> <20100421104651.GO19534@gw.healthdatacare.com> <0D753D10438DA54287A00B0270842697636D8C8672@AUSP01VMBX24.collaborationhost.net> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <0D753D10438DA54287A00B0270842697636D8C8672@AUSP01VMBX24.collaborationhost.net> User-Agent: Mutt/1.5.18 (2008-05-17) X-Spam-Score: -1.1 (-) X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. -1.5 SPF_CHECK_PASS SPF reports sender host as permitted sender for sender-domain -0.0 SPF_PASS SPF: sender matches SPF record -0.0 DKIM_VERIFIED Domain Keys Identified Mail: signature passes verification 0.0 DKIM_SIGNED Domain Keys Identified Mail: message has a signature 0.4 AWL AWL: From: address is in the auto white-list X-Headers-End: 1O50fl-00085H-1Y Cc: "spi-devel-general@lists.sourceforge.net" , Martin Guy , "ryan@bluewatersys.com" , "linux-arm-kernel@lists.infradead.org" Subject: Re: [spi-devel-general] [PATCH v4 1/2] spi: implemented driver for Cirrus EP93xx SPI controller X-BeenThere: spi-devel-general@lists.sourceforge.net X-Mailman-Version: 2.1.9 Precedence: list List-Id: Linux SPI core/device drivers discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: spi-devel-general-bounces@lists.sourceforge.net X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Thu, 22 Apr 2010 17:59:24 +0000 (UTC) diff --git a/drivers/spi/ep93xx_spi.c b/drivers/spi/ep93xx_spi.c index 310032d..7a5c89e 100644 --- a/drivers/spi/ep93xx_spi.c +++ b/drivers/spi/ep93xx_spi.c @@ -105,6 +105,7 @@ struct ep93xx_spi { int irq; unsigned long min_rate; unsigned long max_rate; + bool continuous; bool running; struct workqueue_struct *wq; struct work_struct msg_work; @@ -380,6 +381,7 @@ static int ep93xx_spi_transfer(struct spi_device *spi, struct spi_message *msg) struct ep93xx_spi *espi = spi_master_get_devdata(spi->master); struct spi_transfer *t; unsigned long flags; + bool continuous = true; if (!msg || !msg->complete) return -EINVAL; @@ -389,9 +391,19 @@ static int ep93xx_spi_transfer(struct spi_device *spi, struct spi_message *msg) if (t->bits_per_word) { if (t->bits_per_word < 4 || t->bits_per_word > 16) return -EINVAL; + continuous = false; } - if (t->speed_hz && t->speed_hz < espi->min_rate) + if (t->speed_hz) { + if (t->speed_hz < espi->min_rate) return -EINVAL; + continuous = false; + } + if (t->cs_change) { + if (!list_is_last(&t->transfer_list, &msg->transfers)) + continuous = false; + } + if (t->delay_usecs) + continuous = false; } /* @@ -399,8 +411,11 @@ static int ep93xx_spi_transfer(struct spi_device *spi, struct spi_message *msg) * suitable for us. We use @msg->status to signal whether there was * error in transfer and @msg->state is used to hold pointer to the * current transfer (or %NULL if no active current transfer). + * + * We hold continuation information in @msg->state while it is not + * processed. */ - msg->state = NULL; + msg->state = (void *)continuous; msg->status = 0; msg->actual_length = 0; @@ -550,6 +565,91 @@ static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi, ep93xx_spi_chip_setup(espi, chip); } +/** + * bits_per_word() - returns bits per word for current message + */ +static inline int bits_per_word(const struct ep93xx_spi *espi) +{ + struct spi_message *msg = espi->current_msg; + struct spi_transfer *t = msg->state; + + return t->bits_per_word ? t->bits_per_word : msg->spi->bits_per_word; +} + +static void ep93xx_do_write(struct ep93xx_spi *espi, struct spi_transfer *t) +{ + if (bits_per_word(espi) > 8) { + u16 tx_val = 0; + + if (t->tx_buf) + tx_val = ((u16 *)t->tx_buf)[espi->tx]; + ep93xx_spi_write_u16(espi, SSPDR, tx_val); + espi->tx += sizeof(tx_val); + } else { + u8 tx_val = 0; + + if (t->tx_buf) + tx_val = ((u8 *)t->tx_buf)[espi->tx]; + ep93xx_spi_write_u8(espi, SSPDR, tx_val); + espi->tx += sizeof(tx_val); + } +} + +static void ep93xx_do_read(struct ep93xx_spi *espi, struct spi_transfer *t) +{ + if (bits_per_word(espi) > 8) { + u16 rx_val; + + rx_val = ep93xx_spi_read_u16(espi, SSPDR); + if (t->rx_buf) + ((u16 *)t->rx_buf)[espi->rx] = rx_val; + espi->rx += sizeof(rx_val); + } else { + u8 rx_val; + + rx_val = ep93xx_spi_read_u8(espi, SSPDR); + if (t->rx_buf) + ((u8 *)t->rx_buf)[espi->rx] = rx_val; + espi->rx += sizeof(rx_val); + } +} + +/** + * ep93xx_spi_read_write() - perform next RX/TX transfer + * @espi: ep93xx SPI controller struct + * + * This function transfers next bytes (or half-words) to/from RX/TX FIFOs. If + * called several times, the whole transfer will be completed. Returns %0 when + * current transfer was not yet completed otherwise length of the transfer + * (>%0). When this function is finished, RX FIFO should be empty and TX FIFO + * should be full. + */ +static int ep93xx_spi_read_write(struct ep93xx_spi *espi) +{ + struct spi_message *msg = espi->current_msg; + struct spi_transfer *t = msg->state; + + /* read as long as RX FIFO has frames in it */ + while ((ep93xx_spi_read_u8(espi, SSPSR) & SSPSR_RNE)) { + ep93xx_do_read(espi, t); + espi->fifo_level--; + } + + /* write as long as TX FIFO has room */ + while (espi->fifo_level < SPI_FIFO_SIZE && espi->tx < t->len) { + ep93xx_do_write(espi, t); + espi->fifo_level++; + } + + if (espi->rx == t->len) { + msg->actual_length += t->len; + return t->len; + } + + return 0; +} + + /* * ep93xx_spi_process_message() - process one SPI message * @espi: ep93xx SPI controller struct @@ -569,6 +669,8 @@ static void ep93xx_spi_process_message(struct ep93xx_spi *espi, struct spi_transfer *t; int err; + espi->continuous = (bool)msg->state; + /* * Enable the SPI controller and its clock. */ @@ -606,12 +708,43 @@ static void ep93xx_spi_process_message(struct ep93xx_spi *espi, ep93xx_spi_chip_setup(espi, spi_get_ctldata(msg->spi)); ep93xx_spi_select_device(espi, msg->spi); - list_for_each_entry(t, &msg->transfers, transfer_list) { - ep93xx_spi_process_transfer(espi, msg, t); - if (msg->status) - break; + if (espi->continuous) { + /* + * We can transfer all the transfers continuously. + */ + espi->rx = 0; + espi->tx = 0; + msg->state = list_first_entry(&msg->transfers, struct spi_transfer, + transfer_list); + + /* prime the first transfer */ + if (ep93xx_spi_read_write(espi)) { + /* + * Transfer was completed. Pick next one and continue + * if necessary. + */ + t = msg->state; + if (list_is_last(&t->transfer_list, &msg->transfers)) + goto done; + + espi->rx = 0; + espi->tx = 0; + msg->state = list_entry(t->transfer_list.next, + struct spi_transfer, + transfer_list); + } + + ep93xx_spi_enable_interrupts(espi); + wait_for_completion(&espi->wait); + } else { + list_for_each_entry(t, &msg->transfers, transfer_list) { + ep93xx_spi_process_transfer(espi, msg, t); + if (msg->status) + break; + } } +done: /* * Now the whole message is transferred (or failed for some reason). We * deselect the device and disable the SPI controller. @@ -667,90 +800,6 @@ static void ep93xx_spi_work(struct work_struct *work) } /** - * bits_per_word() - returns bits per word for current message - */ -static inline int bits_per_word(const struct ep93xx_spi *espi) -{ - struct spi_message *msg = espi->current_msg; - struct spi_transfer *t = msg->state; - - return t->bits_per_word ? t->bits_per_word : msg->spi->bits_per_word; -} - -static void ep93xx_do_write(struct ep93xx_spi *espi, struct spi_transfer *t) -{ - if (bits_per_word(espi) > 8) { - u16 tx_val = 0; - - if (t->tx_buf) - tx_val = ((u16 *)t->tx_buf)[espi->tx]; - ep93xx_spi_write_u16(espi, SSPDR, tx_val); - espi->tx += sizeof(tx_val); - } else { - u8 tx_val = 0; - - if (t->tx_buf) - tx_val = ((u8 *)t->tx_buf)[espi->tx]; - ep93xx_spi_write_u8(espi, SSPDR, tx_val); - espi->tx += sizeof(tx_val); - } -} - -static void ep93xx_do_read(struct ep93xx_spi *espi, struct spi_transfer *t) -{ - if (bits_per_word(espi) > 8) { - u16 rx_val; - - rx_val = ep93xx_spi_read_u16(espi, SSPDR); - if (t->rx_buf) - ((u16 *)t->rx_buf)[espi->rx] = rx_val; - espi->rx += sizeof(rx_val); - } else { - u8 rx_val; - - rx_val = ep93xx_spi_read_u8(espi, SSPDR); - if (t->rx_buf) - ((u8 *)t->rx_buf)[espi->rx] = rx_val; - espi->rx += sizeof(rx_val); - } -} - -/** - * ep93xx_spi_read_write() - perform next RX/TX transfer - * @espi: ep93xx SPI controller struct - * - * This function transfers next bytes (or half-words) to/from RX/TX FIFOs. If - * called several times, the whole transfer will be completed. Returns %0 when - * current transfer was not yet completed otherwise length of the transfer - * (>%0). When this function is finished, RX FIFO should be empty and TX FIFO - * should be full. - */ -static int ep93xx_spi_read_write(struct ep93xx_spi *espi) -{ - struct spi_message *msg = espi->current_msg; - struct spi_transfer *t = msg->state; - - /* read as long as RX FIFO has frames in it */ - while ((ep93xx_spi_read_u8(espi, SSPSR) & SSPSR_RNE)) { - ep93xx_do_read(espi, t); - espi->fifo_level--; - } - - /* write as long as TX FIFO has room */ - while (espi->fifo_level < SPI_FIFO_SIZE && espi->tx < t->len) { - ep93xx_do_write(espi, t); - espi->fifo_level++; - } - - if (espi->rx == t->len) { - msg->actual_length += t->len; - return t->len; - } - - return 0; -} - -/** * ep93xx_spi_interrupt() - SPI interrupt handler * @irq: IRQ number (not used) * @dev_id: pointer to EP93xx controller struct @@ -792,6 +841,27 @@ static irqreturn_t ep93xx_spi_interrupt(int irq, void *dev_id) * interrupt then. */ return IRQ_HANDLED; + } else { + struct spi_message *msg = espi->current_msg; + struct spi_transfer *t = msg->state; + + if (!espi->continuous) + goto done; + + if (!list_is_last(&t->transfer_list, &msg->transfers)) { + /* + * Pick next transfer and refill the FIFO. + */ + msg->state = list_entry(t->transfer_list.next, + struct spi_transfer, + transfer_list); + espi->rx = 0; + espi->tx = 0; + + ep93xx_spi_read_write(espi); + return IRQ_HANDLED; + } + /* we are done here */ } } @@ -800,6 +870,7 @@ static irqreturn_t ep93xx_spi_interrupt(int irq, void *dev_id) * any case we disable interrupts and notify the worker to handle * any post-processing of the message. */ +done: ep93xx_spi_disable_interrupts(espi); complete(&espi->wait); return IRQ_HANDLED;