From patchwork Tue Apr 17 17:09:41 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Brown X-Patchwork-Id: 10346061 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id E760660542 for ; Tue, 17 Apr 2018 17:11:00 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D63971FF65 for ; Tue, 17 Apr 2018 17:11:00 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C6802285B5; Tue, 17 Apr 2018 17:11:00 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8EB131FF65 for ; Tue, 17 Apr 2018 17:10:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753325AbeDQRKN (ORCPT ); Tue, 17 Apr 2018 13:10:13 -0400 Received: from heliosphere.sirena.org.uk ([172.104.155.198]:35706 "EHLO heliosphere.sirena.org.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752819AbeDQRJs (ORCPT ); Tue, 17 Apr 2018 13:09:48 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sirena.org.uk; s=20170815-heliosphere; h=Date:Message-Id:In-Reply-To: Subject:Cc:To:From:Sender:Reply-To:MIME-Version:Content-Type: Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:References: List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner: List-Archive; bh=2kD3Vw1CT4X7jb4sRWuTbFz8hS+KKSyC0ZqeYFgmdxE=; b=dJOrK1NxdDql mjtB7M0dzqQ4zeuaWxcQo4k/D14yDYxAyo7trw46SYiHx5HzTu1W505Kd1wVkNyScvHmlzlW6pXMD qwlpqltPhiFD78RuU9kM/k2oTULxdECOgizRr/KUMW3iczaNxY0Btl+dOgVu1bT8iMEX5h6EzKQ54 xPX5s=; Received: from debutante.sirena.org.uk ([2001:470:1f1d:6b5::3] helo=debutante) by heliosphere.sirena.org.uk with esmtpsa (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89) (envelope-from ) id 1f8U6w-0007uH-7t; Tue, 17 Apr 2018 17:09:42 +0000 Received: from broonie by debutante with local (Exim 4.90_1) (envelope-from ) id 1f8U6v-0001jG-Pw; Tue, 17 Apr 2018 18:09:41 +0100 From: Mark Brown To: Jarkko Nikula Cc: Mark Brown , linux-spi@vger.kernel.org, Mark Brown , Robert Jarzmik , Haojian Zhuang , Daniel Mack , linux-spi@vger.kernel.org Subject: Applied "spi: pxa2xx: Use core message processing loop" to the spi tree In-Reply-To: <20180417142002.450-3-jarkko.nikula@linux.intel.com> Message-Id: Date: Tue, 17 Apr 2018 18:09:41 +0100 Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The patch spi: pxa2xx: Use core message processing loop has been applied to the spi tree at https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted. You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed. If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced. Please add any relevant lists and maintainers to the CCs when replying to this mail. Thanks, Mark From d5898e19c0d74cd41b9f5c8c8ea87e559c3fe0c1 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Tue, 17 Apr 2018 17:20:02 +0300 Subject: [PATCH] spi: pxa2xx: Use core message processing loop Convert the pump_transfers() transfer tasklet to transfer_one() hook the SPI core calls to process single transfer instead of handling message processing and chip select handling in the driver. This not only simplifies the driver but also brings transfer statistics from the core. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-dma.c | 28 +++-- drivers/spi/spi-pxa2xx.c | 229 +++++++++++------------------------ drivers/spi/spi-pxa2xx.h | 17 +-- 3 files changed, 89 insertions(+), 185 deletions(-) diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c index 5d0d8fe07966..2fa7f4b43492 100644 --- a/drivers/spi/spi-pxa2xx-dma.c +++ b/drivers/spi/spi-pxa2xx-dma.c @@ -51,19 +51,15 @@ static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data, if (!pxa25x_ssp_comp(drv_data)) pxa2xx_spi_write(drv_data, SSTO, 0); - if (!error) { - msg->actual_length += drv_data->len; - msg->state = pxa2xx_spi_next_transfer(drv_data); - } else { + if (error) { /* In case we got an error we disable the SSP now */ pxa2xx_spi_write(drv_data, SSCR0, pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE); - - msg->state = ERROR_STATE; + msg->status = -EIO; } - tasklet_schedule(&drv_data->pump_transfers); + spi_finalize_current_transfer(drv_data->master); } } @@ -74,11 +70,11 @@ static void pxa2xx_spi_dma_callback(void *data) static struct dma_async_tx_descriptor * pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data, - enum dma_transfer_direction dir) + enum dma_transfer_direction dir, + struct spi_transfer *xfer) { struct chip_data *chip = spi_get_ctldata(drv_data->master->cur_msg->spi); - struct spi_transfer *xfer = drv_data->cur_transfer; enum dma_slave_buswidth width; struct dma_slave_config cfg; struct dma_chan *chan; @@ -144,12 +140,13 @@ irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) return IRQ_NONE; } -int pxa2xx_spi_dma_prepare(struct driver_data *drv_data) +int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, + struct spi_transfer *xfer) { struct dma_async_tx_descriptor *tx_desc, *rx_desc; int err; - tx_desc = pxa2xx_spi_dma_prepare_one(drv_data, DMA_MEM_TO_DEV); + tx_desc = pxa2xx_spi_dma_prepare_one(drv_data, DMA_MEM_TO_DEV, xfer); if (!tx_desc) { dev_err(&drv_data->pdev->dev, "failed to get DMA TX descriptor\n"); @@ -157,7 +154,7 @@ int pxa2xx_spi_dma_prepare(struct driver_data *drv_data) goto err_tx; } - rx_desc = pxa2xx_spi_dma_prepare_one(drv_data, DMA_DEV_TO_MEM); + rx_desc = pxa2xx_spi_dma_prepare_one(drv_data, DMA_DEV_TO_MEM, xfer); if (!rx_desc) { dev_err(&drv_data->pdev->dev, "failed to get DMA RX descriptor\n"); @@ -187,6 +184,13 @@ void pxa2xx_spi_dma_start(struct driver_data *drv_data) atomic_set(&drv_data->dma_running, 1); } +void pxa2xx_spi_dma_stop(struct driver_data *drv_data) +{ + atomic_set(&drv_data->dma_running, 0); + dmaengine_terminate_sync(drv_data->master->dma_rx); + dmaengine_terminate_sync(drv_data->master->dma_tx); +} + int pxa2xx_spi_dma_setup(struct driver_data *drv_data) { struct pxa2xx_spi_master *pdata = drv_data->master_info; diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index e4930542bc4b..c852ea5c28c6 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -340,9 +340,11 @@ static void lpss_ssp_setup(struct driver_data *drv_data) } } -static void lpss_ssp_select_cs(struct driver_data *drv_data, +static void lpss_ssp_select_cs(struct spi_device *spi, const struct lpss_config *config) { + struct driver_data *drv_data = + spi_controller_get_devdata(spi->controller); u32 value, cs; if (!config->cs_sel_mask) @@ -350,7 +352,7 @@ static void lpss_ssp_select_cs(struct driver_data *drv_data, value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl); - cs = drv_data->master->cur_msg->spi->chip_select; + cs = spi->chip_select; cs <<= config->cs_sel_shift; if (cs != (value & config->cs_sel_mask)) { /* @@ -369,15 +371,17 @@ static void lpss_ssp_select_cs(struct driver_data *drv_data, } } -static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable) +static void lpss_ssp_cs_control(struct spi_device *spi, bool enable) { + struct driver_data *drv_data = + spi_controller_get_devdata(spi->controller); const struct lpss_config *config; u32 value; config = lpss_get_config(drv_data); if (enable) - lpss_ssp_select_cs(drv_data, config); + lpss_ssp_select_cs(spi, config); value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl); if (enable) @@ -387,10 +391,11 @@ static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable) __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value); } -static void cs_assert(struct driver_data *drv_data) +static void cs_assert(struct spi_device *spi) { - struct chip_data *chip = - spi_get_ctldata(drv_data->master->cur_msg->spi); + struct chip_data *chip = spi_get_ctldata(spi); + struct driver_data *drv_data = + spi_controller_get_devdata(spi->controller); if (drv_data->ssp_type == CE4100_SSP) { pxa2xx_spi_write(drv_data, SSSR, chip->frm); @@ -408,13 +413,14 @@ static void cs_assert(struct driver_data *drv_data) } if (is_lpss_ssp(drv_data)) - lpss_ssp_cs_control(drv_data, true); + lpss_ssp_cs_control(spi, true); } -static void cs_deassert(struct driver_data *drv_data) +static void cs_deassert(struct spi_device *spi) { - struct chip_data *chip = - spi_get_ctldata(drv_data->master->cur_msg->spi); + struct chip_data *chip = spi_get_ctldata(spi); + struct driver_data *drv_data = + spi_controller_get_devdata(spi->controller); unsigned long timeout; if (drv_data->ssp_type == CE4100_SSP) @@ -437,7 +443,15 @@ static void cs_deassert(struct driver_data *drv_data) } if (is_lpss_ssp(drv_data)) - lpss_ssp_cs_control(drv_data, false); + lpss_ssp_cs_control(spi, false); +} + +static void pxa2xx_spi_set_cs(struct spi_device *spi, bool level) +{ + if (level) + cs_deassert(spi); + else + cs_assert(spi); } int pxa2xx_spi_flush(struct driver_data *drv_data) @@ -549,70 +563,6 @@ static int u32_reader(struct driver_data *drv_data) return drv_data->rx == drv_data->rx_end; } -void *pxa2xx_spi_next_transfer(struct driver_data *drv_data) -{ - struct spi_message *msg = drv_data->master->cur_msg; - struct spi_transfer *trans = drv_data->cur_transfer; - - /* Move to next transfer */ - if (trans->transfer_list.next != &msg->transfers) { - drv_data->cur_transfer = - list_entry(trans->transfer_list.next, - struct spi_transfer, - transfer_list); - return RUNNING_STATE; - } else - return DONE_STATE; -} - -/* caller already set message->status; dma and pio irqs are blocked */ -static void giveback(struct driver_data *drv_data) -{ - struct spi_transfer* last_transfer; - struct spi_message *msg; - - msg = drv_data->master->cur_msg; - drv_data->cur_transfer = NULL; - - last_transfer = list_last_entry(&msg->transfers, struct spi_transfer, - transfer_list); - - /* Delay if requested before any change in chip select */ - if (last_transfer->delay_usecs) - udelay(last_transfer->delay_usecs); - - /* Drop chip select UNLESS cs_change is true or we are returning - * a message with an error, or next message is for another chip - */ - if (!last_transfer->cs_change) - cs_deassert(drv_data); - else { - struct spi_message *next_msg; - - /* Holding of cs was hinted, but we need to make sure - * the next message is for the same chip. Don't waste - * time with the following tests unless this was hinted. - * - * We cannot postpone this until pump_messages, because - * after calling msg->complete (below) the driver that - * sent the current message could be unloaded, which - * could invalidate the cs_control() callback... - */ - - /* get a pointer to the next message, if any */ - next_msg = spi_get_next_queued_message(drv_data->master); - - /* see if the next and current messages point - * to the same chip - */ - if ((next_msg && next_msg->spi != msg->spi) || - msg->state == ERROR_STATE) - cs_deassert(drv_data); - } - - spi_finalize_current_message(drv_data->master); -} - static void reset_sccr1(struct driver_data *drv_data) { struct chip_data *chip = @@ -648,8 +598,8 @@ static void int_error_stop(struct driver_data *drv_data, const char* msg) dev_err(&drv_data->pdev->dev, "%s\n", msg); - drv_data->master->cur_msg->state = ERROR_STATE; - tasklet_schedule(&drv_data->pump_transfers); + drv_data->master->cur_msg->status = -EIO; + spi_finalize_current_transfer(drv_data->master); } static void int_transfer_complete(struct driver_data *drv_data) @@ -660,19 +610,7 @@ static void int_transfer_complete(struct driver_data *drv_data) if (!pxa25x_ssp_comp(drv_data)) pxa2xx_spi_write(drv_data, SSTO, 0); - /* Update total byte transferred return count actual bytes read */ - drv_data->master->cur_msg->actual_length += drv_data->len - - (drv_data->rx_end - drv_data->rx); - - /* Transfer delays and chip select release are - * handled in pump_transfers or giveback - */ - - /* Move to next transfer */ - drv_data->master->cur_msg->state = pxa2xx_spi_next_transfer(drv_data); - - /* Schedule transfer tasklet */ - tasklet_schedule(&drv_data->pump_transfers); + spi_finalize_current_transfer(drv_data->master); } static irqreturn_t interrupt_transfer(struct driver_data *drv_data) @@ -973,17 +911,16 @@ static bool pxa2xx_spi_can_dma(struct spi_controller *master, xfer->len >= chip->dma_burst_size; } -static void pump_transfers(unsigned long data) +int pxa2xx_spi_transfer_one(struct spi_controller *master, + struct spi_device *spi, + struct spi_transfer *transfer) { - struct driver_data *drv_data = (struct driver_data *)data; - struct spi_controller *master = drv_data->master; + struct driver_data *drv_data = spi_controller_get_devdata(master); struct spi_message *message = master->cur_msg; struct chip_data *chip = spi_get_ctldata(message->spi); u32 dma_thresh = chip->dma_threshold; u32 dma_burst = chip->dma_burst_size; u32 change_mask = pxa2xx_spi_get_ssrc1_change_mask(drv_data); - struct spi_transfer *transfer; - struct spi_transfer *previous; u32 clk_div; u8 bits; u32 speed; @@ -992,36 +929,6 @@ static void pump_transfers(unsigned long data) int err; int dma_mapped; - /* Get current state information */ - transfer = drv_data->cur_transfer; - - /* Handle for abort */ - if (message->state == ERROR_STATE) { - message->status = -EIO; - giveback(drv_data); - return; - } - - /* Handle end of message */ - if (message->state == DONE_STATE) { - message->status = 0; - giveback(drv_data); - return; - } - - /* Delay if requested at end of transfer before CS change */ - if (message->state == RUNNING_STATE) { - previous = list_entry(transfer->transfer_list.prev, - struct spi_transfer, - transfer_list); - if (previous->delay_usecs) - udelay(previous->delay_usecs); - - /* Drop chip select only if cs_change is requested */ - if (previous->cs_change) - cs_deassert(drv_data); - } - /* Check if we can DMA this transfer */ if (transfer->len > MAX_DMA_LEN && chip->enable_dma) { @@ -1031,30 +938,25 @@ static void pump_transfers(unsigned long data) dev_err(&drv_data->pdev->dev, "Mapped transfer length of %u is greater than %d\n", transfer->len, MAX_DMA_LEN); - message->status = -EINVAL; - giveback(drv_data); - return; + return -EINVAL; } /* warn ... we force this to PIO mode */ dev_warn_ratelimited(&message->spi->dev, "DMA disabled for transfer length %ld greater than %d\n", - (long)drv_data->len, MAX_DMA_LEN); + (long)transfer->len, MAX_DMA_LEN); } /* Setup the transfer state based on the type of transfer */ if (pxa2xx_spi_flush(drv_data) == 0) { dev_err(&drv_data->pdev->dev, "Flush failed\n"); - message->status = -EIO; - giveback(drv_data); - return; + return -EIO; } drv_data->n_bytes = chip->n_bytes; drv_data->tx = (void *)transfer->tx_buf; drv_data->tx_end = drv_data->tx + transfer->len; drv_data->rx = transfer->rx_buf; drv_data->rx_end = drv_data->rx + transfer->len; - drv_data->len = transfer->len; drv_data->write = drv_data->tx ? chip->write : null_writer; drv_data->read = drv_data->rx ? chip->read : null_reader; @@ -1096,8 +998,6 @@ static void pump_transfers(unsigned long data) "DMA burst size reduced to match bits_per_word\n"); } - message->state = RUNNING_STATE; - dma_mapped = master->can_dma && master->can_dma(master, message->spi, transfer) && master->cur_msg_mapped; @@ -1106,12 +1006,9 @@ static void pump_transfers(unsigned long data) /* Ensure we have the correct interrupt handler */ drv_data->transfer_handler = pxa2xx_spi_dma_transfer; - err = pxa2xx_spi_dma_prepare(drv_data); - if (err) { - message->status = err; - giveback(drv_data); - return; - } + err = pxa2xx_spi_dma_prepare(drv_data, transfer); + if (err) + return err; /* Clear status and start DMA engine */ cr1 = chip->cr1 | dma_thresh | drv_data->dma_cr1; @@ -1173,27 +1070,40 @@ static void pump_transfers(unsigned long data) pxa2xx_spi_write(drv_data, SSTO, chip->timeout); } - cs_assert(drv_data); - - /* after chip select, release the data by enabling service - * requests and interrupts, without changing any mode bits */ + /* + * Release the data by enabling service requests and interrupts, + * without changing any mode bits + */ pxa2xx_spi_write(drv_data, SSCR1, cr1); + + return 1; } -static int pxa2xx_spi_transfer_one_message(struct spi_controller *master, - struct spi_message *msg) +static void pxa2xx_spi_handle_err(struct spi_controller *master, + struct spi_message *msg) { struct driver_data *drv_data = spi_controller_get_devdata(master); - /* Initial message state*/ - msg->state = START_STATE; - drv_data->cur_transfer = list_entry(msg->transfers.next, - struct spi_transfer, - transfer_list); + /* Disable the SSP */ + pxa2xx_spi_write(drv_data, SSCR0, + pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE); + /* Clear and disable interrupts and service requests */ + write_SSSR_CS(drv_data, drv_data->clear_sr); + pxa2xx_spi_write(drv_data, SSCR1, + pxa2xx_spi_read(drv_data, SSCR1) + & ~(drv_data->int_cr1 | drv_data->dma_cr1)); + if (!pxa25x_ssp_comp(drv_data)) + pxa2xx_spi_write(drv_data, SSTO, 0); - /* Mark as busy and launch transfers */ - tasklet_schedule(&drv_data->pump_transfers); - return 0; + /* + * Stop the DMA if running. Note DMA callback handler may have unset + * the dma_running already, which is fine as stopping is not needed + * then but we shouldn't rely this flag for anything else than + * stopping. For instance to differentiate between PIO and DMA + * transfers. + */ + if (atomic_read(&drv_data->dma_running)) + pxa2xx_spi_dma_stop(drv_data); } static int pxa2xx_spi_unprepare_transfer(struct spi_controller *master) @@ -1649,7 +1559,9 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) master->dma_alignment = DMA_ALIGNMENT; master->cleanup = cleanup; master->setup = setup; - master->transfer_one_message = pxa2xx_spi_transfer_one_message; + master->set_cs = pxa2xx_spi_set_cs; + master->transfer_one = pxa2xx_spi_transfer_one; + master->handle_err = pxa2xx_spi_handle_err; master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer; master->fw_translate_cs = pxa2xx_spi_fw_translate_cs; master->auto_runtime_pm = true; @@ -1785,9 +1697,6 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) } } - tasklet_init(&drv_data->pump_transfers, pump_transfers, - (unsigned long)drv_data); - pm_runtime_set_autosuspend_delay(&pdev->dev, 50); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_active(&pdev->dev); diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index 08aa7e50d011..baf558b89c73 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -46,15 +46,10 @@ struct driver_data { u32 clear_sr; u32 mask_sr; - /* Message Transfer pump */ - struct tasklet_struct pump_transfers; - /* DMA engine support */ atomic_t dma_running; - /* Current message transfer state info */ - struct spi_transfer *cur_transfer; - size_t len; + /* Current transfer state info */ void *tx; void *tx_end; void *rx; @@ -104,11 +99,6 @@ static inline void pxa2xx_spi_write(const struct driver_data *drv_data, __raw_writel(val, drv_data->ioaddr + reg); } -#define START_STATE ((void *)0) -#define RUNNING_STATE ((void *)1) -#define DONE_STATE ((void *)2) -#define ERROR_STATE ((void *)-1) - #define DMA_ALIGNMENT 8 static inline int pxa25x_ssp_comp(struct driver_data *drv_data) @@ -133,14 +123,15 @@ static inline void write_SSSR_CS(struct driver_data *drv_data, u32 val) } extern int pxa2xx_spi_flush(struct driver_data *drv_data); -extern void *pxa2xx_spi_next_transfer(struct driver_data *drv_data); #define MAX_DMA_LEN SZ_64K #define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL) extern irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data); -extern int pxa2xx_spi_dma_prepare(struct driver_data *drv_data); +extern int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, + struct spi_transfer *xfer); extern void pxa2xx_spi_dma_start(struct driver_data *drv_data); +extern void pxa2xx_spi_dma_stop(struct driver_data *drv_data); extern int pxa2xx_spi_dma_setup(struct driver_data *drv_data); extern void pxa2xx_spi_dma_release(struct driver_data *drv_data); extern int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip,