summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/dma/Kconfig7
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/ep93xx_dma.c1355
-rw-r--r--drivers/spi/Kconfig48
-rw-r--r--drivers/spi/Makefile111
-rw-r--r--drivers/spi/atmel_spi.h167
-rw-r--r--drivers/spi/spi-altera.c (renamed from drivers/spi/spi_altera.c)0
-rw-r--r--drivers/spi/spi-ath79.c (renamed from drivers/spi/ath79_spi.c)2
-rw-r--r--drivers/spi/spi-atmel.c (renamed from drivers/spi/atmel_spi.c)155
-rw-r--r--drivers/spi/spi-au1550.c (renamed from drivers/spi/au1550_spi.c)2
-rw-r--r--drivers/spi/spi-bfin-sport.c (renamed from drivers/spi/spi_bfin_sport.c)0
-rw-r--r--drivers/spi/spi-bfin5xx.c (renamed from drivers/spi/spi_bfin5xx.c)218
-rw-r--r--drivers/spi/spi-bitbang-txrx.h (renamed from drivers/spi/spi_bitbang_txrx.h)0
-rw-r--r--drivers/spi/spi-bitbang.c (renamed from drivers/spi/spi_bitbang.c)8
-rw-r--r--drivers/spi/spi-butterfly.c (renamed from drivers/spi/spi_butterfly.c)4
-rw-r--r--drivers/spi/spi-coldfire-qspi.c (renamed from drivers/spi/coldfire_qspi.c)0
-rw-r--r--drivers/spi/spi-davinci.c (renamed from drivers/spi/davinci_spi.c)0
-rw-r--r--drivers/spi/spi-dw-mid.c (renamed from drivers/spi/dw_spi_mid.c)4
-rw-r--r--drivers/spi/spi-dw-mmio.c (renamed from drivers/spi/dw_spi_mmio.c)4
-rw-r--r--drivers/spi/spi-dw-pci.c (renamed from drivers/spi/dw_spi_pci.c)4
-rw-r--r--drivers/spi/spi-dw.c (renamed from drivers/spi/dw_spi.c)8
-rw-r--r--drivers/spi/spi-dw.h (renamed from drivers/spi/dw_spi.h)1
-rw-r--r--drivers/spi/spi-ep93xx.c (renamed from drivers/spi/ep93xx_spi.c)303
-rw-r--r--drivers/spi/spi-fsl-espi.c (renamed from drivers/spi/spi_fsl_espi.c)2
-rw-r--r--drivers/spi/spi-fsl-lib.c (renamed from drivers/spi/spi_fsl_lib.c)2
-rw-r--r--drivers/spi/spi-fsl-lib.h (renamed from drivers/spi/spi_fsl_lib.h)0
-rw-r--r--drivers/spi/spi-fsl-spi.c (renamed from drivers/spi/spi_fsl_spi.c)30
-rw-r--r--drivers/spi/spi-gpio.c (renamed from drivers/spi/spi_gpio.c)6
-rw-r--r--drivers/spi/spi-imx.c (renamed from drivers/spi/spi_imx.c)466
-rw-r--r--drivers/spi/spi-lm70llp.c (renamed from drivers/spi/spi_lm70llp.c)4
-rw-r--r--drivers/spi/spi-mpc512x-psc.c (renamed from drivers/spi/mpc512x_psc_spi.c)0
-rw-r--r--drivers/spi/spi-mpc52xx-psc.c (renamed from drivers/spi/mpc52xx_psc_spi.c)0
-rw-r--r--drivers/spi/spi-mpc52xx.c (renamed from drivers/spi/mpc52xx_spi.c)0
-rw-r--r--drivers/spi/spi-nuc900.c (renamed from drivers/spi/spi_nuc900.c)5
-rw-r--r--drivers/spi/spi-oc-tiny.c (renamed from drivers/spi/spi_oc_tiny.c)0
-rw-r--r--drivers/spi/spi-omap-100k.c (renamed from drivers/spi/omap_spi_100k.c)0
-rw-r--r--drivers/spi/spi-omap-uwire.c (renamed from drivers/spi/omap_uwire.c)2
-rw-r--r--drivers/spi/spi-omap2-mcspi.c (renamed from drivers/spi/omap2_mcspi.c)10
-rw-r--r--drivers/spi/spi-orion.c (renamed from drivers/spi/orion_spi.c)8
-rw-r--r--drivers/spi/spi-pl022.c (renamed from drivers/spi/amba-pl022.c)111
-rw-r--r--drivers/spi/spi-ppc4xx.c (renamed from drivers/spi/spi_ppc4xx.c)2
-rw-r--r--drivers/spi/spi-pxa2xx-pci.c (renamed from drivers/spi/pxa2xx_spi_pci.c)0
-rw-r--r--drivers/spi/spi-pxa2xx.c (renamed from drivers/spi/pxa2xx_spi.c)0
-rw-r--r--drivers/spi/spi-s3c24xx-fiq.S (renamed from drivers/spi/spi_s3c24xx_fiq.S)2
-rw-r--r--drivers/spi/spi-s3c24xx-fiq.h (renamed from drivers/spi/spi_s3c24xx_fiq.h)0
-rw-r--r--drivers/spi/spi-s3c24xx.c (renamed from drivers/spi/spi_s3c24xx.c)5
-rw-r--r--drivers/spi/spi-s3c64xx.c (renamed from drivers/spi/spi_s3c64xx.c)3
-rw-r--r--drivers/spi/spi-sh-msiof.c (renamed from drivers/spi/spi_sh_msiof.c)0
-rw-r--r--drivers/spi/spi-sh-sci.c (renamed from drivers/spi/spi_sh_sci.c)2
-rw-r--r--drivers/spi/spi-sh.c (renamed from drivers/spi/spi_sh.c)0
-rw-r--r--drivers/spi/spi-stmp.c (renamed from drivers/spi/spi_stmp.c)0
-rw-r--r--drivers/spi/spi-tegra.c (renamed from drivers/spi/spi_tegra.c)20
-rw-r--r--drivers/spi/spi-ti-ssp.c (renamed from drivers/spi/ti-ssp-spi.c)0
-rw-r--r--drivers/spi/spi-tle62x0.c (renamed from drivers/spi/tle62x0.c)2
-rw-r--r--drivers/spi/spi-topcliff-pch.c (renamed from drivers/spi/spi_topcliff_pch.c)1158
-rw-r--r--drivers/spi/spi-txx9.c (renamed from drivers/spi/spi_txx9.c)2
-rw-r--r--drivers/spi/spi-xilinx.c (renamed from drivers/spi/xilinx_spi.c)0
-rw-r--r--drivers/spi/spi.c2
-rw-r--r--drivers/spi/spi_s3c24xx_gpio.c201
-rw-r--r--drivers/spi/spidev.c2
60 files changed, 3150 insertions, 1299 deletions
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 25cf327cd1cb..2e3b3d38c465 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -237,6 +237,13 @@ config MXS_DMA
Support the MXS DMA engine. This engine including APBH-DMA
and APBX-DMA is integrated into Freescale i.MX23/28 chips.
+config EP93XX_DMA
+ bool "Cirrus Logic EP93xx DMA support"
+ depends on ARCH_EP93XX
+ select DMA_ENGINE
+ help
+ Enable support for the Cirrus Logic EP93xx M2P/M2M DMA controller.
+
config DMA_ENGINE
bool
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 836095ab3c5c..30cf3b1f0c5c 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -25,3 +25,4 @@ obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o
obj-$(CONFIG_PL330_DMA) += pl330.o
obj-$(CONFIG_PCH_DMA) += pch_dma.o
obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
+obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
new file mode 100644
index 000000000000..0766c1e53b1d
--- /dev/null
+++ b/drivers/dma/ep93xx_dma.c
@@ -0,0 +1,1355 @@
+/*
+ * Driver for the Cirrus Logic EP93xx DMA Controller
+ *
+ * Copyright (C) 2011 Mika Westerberg
+ *
+ * DMA M2P implementation is based on the original
+ * arch/arm/mach-ep93xx/dma-m2p.c which has following copyrights:
+ *
+ * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Copyright (C) 2006 Applied Data Systems
+ * Copyright (C) 2009 Ryan Mallon <rmallon@gmail.com>
+ *
+ * This driver is based on dw_dmac and amba-pl08x drivers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/dmaengine.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <mach/dma.h>
+
+/* M2P registers */
+#define M2P_CONTROL 0x0000
+#define M2P_CONTROL_STALLINT BIT(0)
+#define M2P_CONTROL_NFBINT BIT(1)
+#define M2P_CONTROL_CH_ERROR_INT BIT(3)
+#define M2P_CONTROL_ENABLE BIT(4)
+#define M2P_CONTROL_ICE BIT(6)
+
+#define M2P_INTERRUPT 0x0004
+#define M2P_INTERRUPT_STALL BIT(0)
+#define M2P_INTERRUPT_NFB BIT(1)
+#define M2P_INTERRUPT_ERROR BIT(3)
+
+#define M2P_PPALLOC 0x0008
+#define M2P_STATUS 0x000c
+
+#define M2P_MAXCNT0 0x0020
+#define M2P_BASE0 0x0024
+#define M2P_MAXCNT1 0x0030
+#define M2P_BASE1 0x0034
+
+#define M2P_STATE_IDLE 0
+#define M2P_STATE_STALL 1
+#define M2P_STATE_ON 2
+#define M2P_STATE_NEXT 3
+
+/* M2M registers */
+#define M2M_CONTROL 0x0000
+#define M2M_CONTROL_DONEINT BIT(2)
+#define M2M_CONTROL_ENABLE BIT(3)
+#define M2M_CONTROL_START BIT(4)
+#define M2M_CONTROL_DAH BIT(11)
+#define M2M_CONTROL_SAH BIT(12)
+#define M2M_CONTROL_PW_SHIFT 9
+#define M2M_CONTROL_PW_8 (0 << M2M_CONTROL_PW_SHIFT)
+#define M2M_CONTROL_PW_16 (1 << M2M_CONTROL_PW_SHIFT)
+#define M2M_CONTROL_PW_32 (2 << M2M_CONTROL_PW_SHIFT)
+#define M2M_CONTROL_PW_MASK (3 << M2M_CONTROL_PW_SHIFT)
+#define M2M_CONTROL_TM_SHIFT 13
+#define M2M_CONTROL_TM_TX (1 << M2M_CONTROL_TM_SHIFT)
+#define M2M_CONTROL_TM_RX (2 << M2M_CONTROL_TM_SHIFT)
+#define M2M_CONTROL_RSS_SHIFT 22
+#define M2M_CONTROL_RSS_SSPRX (1 << M2M_CONTROL_RSS_SHIFT)
+#define M2M_CONTROL_RSS_SSPTX (2 << M2M_CONTROL_RSS_SHIFT)
+#define M2M_CONTROL_RSS_IDE (3 << M2M_CONTROL_RSS_SHIFT)
+#define M2M_CONTROL_NO_HDSK BIT(24)
+#define M2M_CONTROL_PWSC_SHIFT 25
+
+#define M2M_INTERRUPT 0x0004
+#define M2M_INTERRUPT_DONEINT BIT(1)
+
+#define M2M_BCR0 0x0010
+#define M2M_BCR1 0x0014
+#define M2M_SAR_BASE0 0x0018
+#define M2M_SAR_BASE1 0x001c
+#define M2M_DAR_BASE0 0x002c
+#define M2M_DAR_BASE1 0x0030
+
+#define DMA_MAX_CHAN_BYTES 0xffff
+#define DMA_MAX_CHAN_DESCRIPTORS 32
+
+struct ep93xx_dma_engine;
+
+/**
+ * struct ep93xx_dma_desc - EP93xx specific transaction descriptor
+ * @src_addr: source address of the transaction
+ * @dst_addr: destination address of the transaction
+ * @size: size of the transaction (in bytes)
+ * @complete: this descriptor is completed
+ * @txd: dmaengine API descriptor
+ * @tx_list: list of linked descriptors
+ * @node: link used for putting this into a channel queue
+ */
+struct ep93xx_dma_desc {
+ u32 src_addr;
+ u32 dst_addr;
+ size_t size;
+ bool complete;
+ struct dma_async_tx_descriptor txd;
+ struct list_head tx_list;
+ struct list_head node;
+};
+
+/**
+ * struct ep93xx_dma_chan - an EP93xx DMA M2P/M2M channel
+ * @chan: dmaengine API channel
+ * @edma: pointer to to the engine device
+ * @regs: memory mapped registers
+ * @irq: interrupt number of the channel
+ * @clk: clock used by this channel
+ * @tasklet: channel specific tasklet used for callbacks
+ * @lock: lock protecting the fields following
+ * @flags: flags for the channel
+ * @buffer: which buffer to use next (0/1)
+ * @last_completed: last completed cookie value
+ * @active: flattened chain of descriptors currently being processed
+ * @queue: pending descriptors which are handled next
+ * @free_list: list of free descriptors which can be used
+ * @runtime_addr: physical address currently used as dest/src (M2M only). This
+ * is set via %DMA_SLAVE_CONFIG before slave operation is
+ * prepared
+ * @runtime_ctrl: M2M runtime values for the control register.
+ *
+ * As EP93xx DMA controller doesn't support real chained DMA descriptors we
+ * will have slightly different scheme here: @active points to a head of
+ * flattened DMA descriptor chain.
+ *
+ * @queue holds pending transactions. These are linked through the first
+ * descriptor in the chain. When a descriptor is moved to the @active queue,
+ * the first and chained descriptors are flattened into a single list.
+ *
+ * @chan.private holds pointer to &struct ep93xx_dma_data which contains
+ * necessary channel configuration information. For memcpy channels this must
+ * be %NULL.
+ */
+struct ep93xx_dma_chan {
+ struct dma_chan chan;
+ const struct ep93xx_dma_engine *edma;
+ void __iomem *regs;
+ int irq;
+ struct clk *clk;
+ struct tasklet_struct tasklet;
+ /* protects the fields following */
+ spinlock_t lock;
+ unsigned long flags;
+/* Channel is configured for cyclic transfers */
+#define EP93XX_DMA_IS_CYCLIC 0
+
+ int buffer;
+ dma_cookie_t last_completed;
+ struct list_head active;
+ struct list_head queue;
+ struct list_head free_list;
+ u32 runtime_addr;
+ u32 runtime_ctrl;
+};
+
+/**
+ * struct ep93xx_dma_engine - the EP93xx DMA engine instance
+ * @dma_dev: holds the dmaengine device
+ * @m2m: is this an M2M or M2P device
+ * @hw_setup: method which sets the channel up for operation
+ * @hw_shutdown: shuts the channel down and flushes whatever is left
+ * @hw_submit: pushes active descriptor(s) to the hardware
+ * @hw_interrupt: handle the interrupt
+ * @num_channels: number of channels for this instance
+ * @channels: array of channels
+ *
+ * There is one instance of this struct for the M2P channels and one for the
+ * M2M channels. hw_xxx() methods are used to perform operations which are
+ * different on M2M and M2P channels. These methods are called with channel
+ * lock held and interrupts disabled so they cannot sleep.
+ */
+struct ep93xx_dma_engine {
+ struct dma_device dma_dev;
+ bool m2m;
+ int (*hw_setup)(struct ep93xx_dma_chan *);
+ void (*hw_shutdown)(struct ep93xx_dma_chan *);
+ void (*hw_submit)(struct ep93xx_dma_chan *);
+ int (*hw_interrupt)(struct ep93xx_dma_chan *);
+#define INTERRUPT_UNKNOWN 0
+#define INTERRUPT_DONE 1
+#define INTERRUPT_NEXT_BUFFER 2
+
+ size_t num_channels;
+ struct ep93xx_dma_chan channels[];
+};
+
+static inline struct device *chan2dev(struct ep93xx_dma_chan *edmac)
+{
+ return &edmac->chan.dev->device;
+}
+
+static struct ep93xx_dma_chan *to_ep93xx_dma_chan(struct dma_chan *chan)
+{
+ return container_of(chan, struct ep93xx_dma_chan, chan);
+}
+
+/**
+ * ep93xx_dma_set_active - set new active descriptor chain
+ * @edmac: channel
+ * @desc: head of the new active descriptor chain
+ *
+ * Sets @desc to be the head of the new active descriptor chain. This is the
+ * chain which is processed next. The active list must be empty before calling
+ * this function.
+ *
+ * Called with @edmac->lock held and interrupts disabled.
+ */
+static void ep93xx_dma_set_active(struct ep93xx_dma_chan *edmac,
+ struct ep93xx_dma_desc *desc)
+{
+ BUG_ON(!list_empty(&edmac->active));
+
+ list_add_tail(&desc->node, &edmac->active);
+
+ /* Flatten the @desc->tx_list chain into @edmac->active list */
+ while (!list_empty(&desc->tx_list)) {
+ struct ep93xx_dma_desc *d = list_first_entry(&desc->tx_list,
+ struct ep93xx_dma_desc, node);
+
+ /*
+ * We copy the callback parameters from the first descriptor
+ * to all the chained descriptors. This way we can call the
+ * callback without having to find out the first descriptor in
+ * the chain. Useful for cyclic transfers.
+ */
+ d->txd.callback = desc->txd.callback;
+ d->txd.callback_param = desc->txd.callback_param;
+
+ list_move_tail(&d->node, &edmac->active);
+ }
+}
+
+/* Called with @edmac->lock held and interrupts disabled */
+static struct ep93xx_dma_desc *
+ep93xx_dma_get_active(struct ep93xx_dma_chan *edmac)
+{
+ return list_first_entry(&edmac->active, struct ep93xx_dma_desc, node);
+}
+
+/**
+ * ep93xx_dma_advance_active - advances to the next active descriptor
+ * @edmac: channel
+ *
+ * Function advances active descriptor to the next in the @edmac->active and
+ * returns %true if we still have descriptors in the chain to process.
+ * Otherwise returns %false.
+ *
+ * When the channel is in cyclic mode always returns %true.
+ *
+ * Called with @edmac->lock held and interrupts disabled.
+ */
+static bool ep93xx_dma_advance_active(struct ep93xx_dma_chan *edmac)
+{
+ list_rotate_left(&edmac->active);
+
+ if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags))
+ return true;
+
+ /*
+ * If txd.cookie is set it means that we are back in the first
+ * descriptor in the chain and hence done with it.
+ */
+ return !ep93xx_dma_get_active(edmac)->txd.cookie;
+}
+
+/*
+ * M2P DMA implementation
+ */
+
+static void m2p_set_control(struct ep93xx_dma_chan *edmac, u32 control)
+{
+ writel(control, edmac->regs + M2P_CONTROL);
+ /*
+ * EP93xx User's Guide states that we must perform a dummy read after
+ * write to the control register.
+ */
+ readl(edmac->regs + M2P_CONTROL);
+}
+
+static int m2p_hw_setup(struct ep93xx_dma_chan *edmac)
+{
+ struct ep93xx_dma_data *data = edmac->chan.private;
+ u32 control;
+
+ writel(data->port & 0xf, edmac->regs + M2P_PPALLOC);
+
+ control = M2P_CONTROL_CH_ERROR_INT | M2P_CONTROL_ICE
+ | M2P_CONTROL_ENABLE;
+ m2p_set_control(edmac, control);
+
+ return 0;
+}
+
+static inline u32 m2p_channel_state(struct ep93xx_dma_chan *edmac)
+{
+ return (readl(edmac->regs + M2P_STATUS) >> 4) & 0x3;
+}
+
+static void m2p_hw_shutdown(struct ep93xx_dma_chan *edmac)
+{
+ u32 control;
+
+ control = readl(edmac->regs + M2P_CONTROL);
+ control &= ~(M2P_CONTROL_STALLINT | M2P_CONTROL_NFBINT);
+ m2p_set_control(edmac, control);
+
+ while (m2p_channel_state(edmac) >= M2P_STATE_ON)
+ cpu_relax();
+
+ m2p_set_control(edmac, 0);
+
+ while (m2p_channel_state(edmac) == M2P_STATE_STALL)
+ cpu_relax();
+}
+
+static void m2p_fill_desc(struct ep93xx_dma_chan *edmac)
+{
+ struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac);
+ u32 bus_addr;
+
+ if (ep93xx_dma_chan_direction(&edmac->chan) == DMA_TO_DEVICE)
+ bus_addr = desc->src_addr;
+ else
+ bus_addr = desc->dst_addr;
+
+ if (edmac->buffer == 0) {
+ writel(desc->size, edmac->regs + M2P_MAXCNT0);
+ writel(bus_addr, edmac->regs + M2P_BASE0);
+ } else {
+ writel(desc->size, edmac->regs + M2P_MAXCNT1);
+ writel(bus_addr, edmac->regs + M2P_BASE1);
+ }
+
+ edmac->buffer ^= 1;
+}
+
+static void m2p_hw_submit(struct ep93xx_dma_chan *edmac)
+{
+ u32 control = readl(edmac->regs + M2P_CONTROL);
+
+ m2p_fill_desc(edmac);
+ control |= M2P_CONTROL_STALLINT;
+
+ if (ep93xx_dma_advance_active(edmac)) {
+ m2p_fill_desc(edmac);
+ control |= M2P_CONTROL_NFBINT;
+ }
+
+ m2p_set_control(edmac, control);
+}
+
+static int m2p_hw_interrupt(struct ep93xx_dma_chan *edmac)
+{
+ u32 irq_status = readl(edmac->regs + M2P_INTERRUPT);
+ u32 control;
+
+ if (irq_status & M2P_INTERRUPT_ERROR) {
+ struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac);
+
+ /* Clear the error interrupt */
+ writel(1, edmac->regs + M2P_INTERRUPT);
+
+ /*
+ * It seems that there is no easy way of reporting errors back
+ * to client so we just report the error here and continue as
+ * usual.
+ *
+ * Revisit this when there is a mechanism to report back the
+ * errors.
+ */
+ dev_err(chan2dev(edmac),
+ "DMA transfer failed! Details:\n"
+ "\tcookie : %d\n"
+ "\tsrc_addr : 0x%08x\n"
+ "\tdst_addr : 0x%08x\n"
+ "\tsize : %zu\n",
+ desc->txd.cookie, desc->src_addr, desc->dst_addr,
+ desc->size);
+ }
+
+ switch (irq_status & (M2P_INTERRUPT_STALL | M2P_INTERRUPT_NFB)) {
+ case M2P_INTERRUPT_STALL:
+ /* Disable interrupts */
+ control = readl(edmac->regs + M2P_CONTROL);
+ control &= ~(M2P_CONTROL_STALLINT | M2P_CONTROL_NFBINT);
+ m2p_set_control(edmac, control);
+
+ return INTERRUPT_DONE;
+
+ case M2P_INTERRUPT_NFB:
+ if (ep93xx_dma_advance_active(edmac))
+ m2p_fill_desc(edmac);
+
+ return INTERRUPT_NEXT_BUFFER;
+ }
+
+ return INTERRUPT_UNKNOWN;
+}
+
+/*
+ * M2M DMA implementation
+ *
+ * For the M2M transfers we don't use NFB at all. This is because it simply
+ * doesn't work well with memcpy transfers. When you submit both buffers it is
+ * extremely unlikely that you get an NFB interrupt, but it instead reports
+ * DONE interrupt and both buffers are already transferred which means that we
+ * weren't able to update the next buffer.
+ *
+ * So for now we "simulate" NFB by just submitting buffer after buffer
+ * without double buffering.
+ */
+
+static int m2m_hw_setup(struct ep93xx_dma_chan *edmac)
+{
+ const struct ep93xx_dma_data *data = edmac->chan.private;
+ u32 control = 0;
+
+ if (!data) {
+ /* This is memcpy channel, nothing to configure */
+ writel(control, edmac->regs + M2M_CONTROL);
+ return 0;
+ }
+
+ switch (data->port) {
+ case EP93XX_DMA_SSP:
+ /*
+ * This was found via experimenting - anything less than 5
+ * causes the channel to perform only a partial transfer which
+ * leads to problems since we don't get DONE interrupt then.
+ */
+ control = (5 << M2M_CONTROL_PWSC_SHIFT);
+ control |= M2M_CONTROL_NO_HDSK;
+
+ if (data->direction == DMA_TO_DEVICE) {
+ control |= M2M_CONTROL_DAH;
+ control |= M2M_CONTROL_TM_TX;
+ control |= M2M_CONTROL_RSS_SSPTX;
+ } else {
+ control |= M2M_CONTROL_SAH;
+ control |= M2M_CONTROL_TM_RX;
+ control |= M2M_CONTROL_RSS_SSPRX;
+ }
+ break;
+
+ case EP93XX_DMA_IDE:
+ /*
+ * This IDE part is totally untested. Values below are taken
+ * from the EP93xx Users's Guide and might not be correct.
+ */
+ control |= M2M_CONTROL_NO_HDSK;
+ control |= M2M_CONTROL_RSS_IDE;
+ control |= M2M_CONTROL_PW_16;
+
+ if (data->direction == DMA_TO_DEVICE) {
+ /* Worst case from the UG */
+ control = (3 << M2M_CONTROL_PWSC_SHIFT);
+ control |= M2M_CONTROL_DAH;
+ control |= M2M_CONTROL_TM_TX;
+ } else {
+ control = (2 << M2M_CONTROL_PWSC_SHIFT);
+ control |= M2M_CONTROL_SAH;
+ control |= M2M_CONTROL_TM_RX;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ writel(control, edmac->regs + M2M_CONTROL);
+ return 0;
+}
+
+static void m2m_hw_shutdown(struct ep93xx_dma_chan *edmac)
+{
+ /* Just disable the channel */
+ writel(0, edmac->regs + M2M_CONTROL);
+}
+
+static void m2m_fill_desc(struct ep93xx_dma_chan *edmac)
+{
+ struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac);
+
+ if (edmac->buffer == 0) {
+ writel(desc->src_addr, edmac->regs + M2M_SAR_BASE0);
+ writel(desc->dst_addr, edmac->regs + M2M_DAR_BASE0);
+ writel(desc->size, edmac->regs + M2M_BCR0);
+ } else {
+ writel(desc->src_addr, edmac->regs + M2M_SAR_BASE1);
+ writel(desc->dst_addr, edmac->regs + M2M_DAR_BASE1);
+ writel(desc->size, edmac->regs + M2M_BCR1);
+ }
+
+ edmac->buffer ^= 1;
+}
+
+static void m2m_hw_submit(struct ep93xx_dma_chan *edmac)
+{
+ struct ep93xx_dma_data *data = edmac->chan.private;
+ u32 control = readl(edmac->regs + M2M_CONTROL);
+
+ /*
+ * Since we allow clients to configure PW (peripheral width) we always
+ * clear PW bits here and then set them according what is given in
+ * the runtime configuration.
+ */
+ control &= ~M2M_CONTROL_PW_MASK;
+ control |= edmac->runtime_ctrl;
+
+ m2m_fill_desc(edmac);
+ control |= M2M_CONTROL_DONEINT;
+
+ /*
+ * Now we can finally enable the channel. For M2M channel this must be
+ * done _after_ the BCRx registers are programmed.
+ */
+ control |= M2M_CONTROL_ENABLE;
+ writel(control, edmac->regs + M2M_CONTROL);
+
+ if (!data) {
+ /*
+ * For memcpy channels the software trigger must be asserted
+ * in order to start the memcpy operation.
+ */
+ control |= M2M_CONTROL_START;
+ writel(control, edmac->regs + M2M_CONTROL);
+ }
+}
+
+static int m2m_hw_interrupt(struct ep93xx_dma_chan *edmac)
+{
+ u32 control;
+
+ if (!(readl(edmac->regs + M2M_INTERRUPT) & M2M_INTERRUPT_DONEINT))
+ return INTERRUPT_UNKNOWN;
+
+ /* Clear the DONE bit */
+ writel(0, edmac->regs + M2M_INTERRUPT);
+
+ /* Disable interrupts and the channel */
+ control = readl(edmac->regs + M2M_CONTROL);
+ control &= ~(M2M_CONTROL_DONEINT | M2M_CONTROL_ENABLE);
+ writel(control, edmac->regs + M2M_CONTROL);
+
+ /*
+ * Since we only get DONE interrupt we have to find out ourselves
+ * whether there still is something to process. So we try to advance
+ * the chain an see whether it succeeds.
+ */
+ if (ep93xx_dma_advance_active(edmac)) {
+ edmac->edma->hw_submit(edmac);
+ return INTERRUPT_NEXT_BUFFER;
+ }
+
+ return INTERRUPT_DONE;
+}
+
+/*
+ * DMA engine API implementation
+ */
+
+static struct ep93xx_dma_desc *
+ep93xx_dma_desc_get(struct ep93xx_dma_chan *edmac)
+{
+ struct ep93xx_dma_desc *desc, *_desc;
+ struct ep93xx_dma_desc *ret = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&edmac->lock, flags);
+ list_for_each_entry_safe(desc, _desc, &edmac->free_list, node) {
+ if (async_tx_test_ack(&desc->txd)) {
+ list_del_init(&desc->node);
+
+ /* Re-initialize the descriptor */
+ desc->src_addr = 0;
+ desc->dst_addr = 0;
+ desc->size = 0;
+ desc->complete = false;
+ desc->txd.cookie = 0;
+ desc->txd.callback = NULL;
+ desc->txd.callback_param = NULL;
+
+ ret = desc;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&edmac->lock, flags);
+ return ret;
+}
+
+static void ep93xx_dma_desc_put(struct ep93xx_dma_chan *edmac,
+ struct ep93xx_dma_desc *desc)
+{
+ if (desc) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&edmac->lock, flags);
+ list_splice_init(&desc->tx_list, &edmac->free_list);
+ list_add(&desc->node, &edmac->free_list);
+ spin_unlock_irqrestore(&edmac->lock, flags);
+ }
+}
+
+/**
+ * ep93xx_dma_advance_work - start processing the next pending transaction
+ * @edmac: channel
+ *
+ * If we have pending transactions queued and we are currently idling, this
+ * function takes the next queued transaction from the @edmac->queue and
+ * pushes it to the hardware for execution.
+ */
+static void ep93xx_dma_advance_work(struct ep93xx_dma_chan *edmac)
+{
+ struct ep93xx_dma_desc *new;
+ unsigned long flags;
+
+ spin_lock_irqsave(&edmac->lock, flags);
+ if (!list_empty(&edmac->active) || list_empty(&edmac->queue)) {
+ spin_unlock_irqrestore(&edmac->lock, flags);
+ return;
+ }
+
+ /* Take the next descriptor from the pending queue */
+ new = list_first_entry(&edmac->queue, struct ep93xx_dma_desc, node);
+ list_del_init(&new->node);
+
+ ep93xx_dma_set_active(edmac, new);
+
+ /* Push it to the hardware */
+ edmac->edma->hw_submit(edmac);
+ spin_unlock_irqrestore(&edmac->lock, flags);
+}
+
+static void ep93xx_dma_unmap_buffers(struct ep93xx_dma_desc *desc)
+{
+ struct device *dev = desc->txd.chan->device->dev;
+
+ if (!(desc->txd.flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
+ if (desc->txd.flags & DMA_COMPL_SRC_UNMAP_SINGLE)
+ dma_unmap_single(dev, desc->src_addr, desc->size,
+ DMA_TO_DEVICE);
+ else
+ dma_unmap_page(dev, desc->src_addr, desc->size,
+ DMA_TO_DEVICE);
+ }
+ if (!(desc->txd.flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
+ if (desc->txd.flags & DMA_COMPL_DEST_UNMAP_SINGLE)
+ dma_unmap_single(dev, desc->dst_addr, desc->size,
+ DMA_FROM_DEVICE);
+ else
+ dma_unmap_page(dev, desc->dst_addr, desc->size,
+ DMA_FROM_DEVICE);
+ }
+}
+
+static void ep93xx_dma_tasklet(unsigned long data)
+{
+ struct ep93xx_dma_chan *edmac = (struct ep93xx_dma_chan *)data;
+ struct ep93xx_dma_desc *desc, *d;
+ dma_async_tx_callback callback;
+ void *callback_param;
+ LIST_HEAD(list);
+
+ spin_lock_irq(&edmac->lock);
+ desc = ep93xx_dma_get_active(edmac);
+ if (desc->complete) {
+ edmac->last_completed = desc->txd.cookie;
+ list_splice_init(&edmac->active, &list);
+ }
+ spin_unlock_irq(&edmac->lock);
+
+ /* Pick up the next descriptor from the queue */
+ ep93xx_dma_advance_work(edmac);
+
+ callback = desc->txd.callback;
+ callback_param = desc->txd.callback_param;
+
+ /* Now we can release all the chained descriptors */
+ list_for_each_entry_safe(desc, d, &list, node) {
+ /*
+ * For the memcpy channels the API requires us to unmap the
+ * buffers unless requested otherwise.
+ */
+ if (!edmac->chan.private)
+ ep93xx_dma_unmap_buffers(desc);
+
+ ep93xx_dma_desc_put(edmac, desc);
+ }
+
+ if (callback)
+ callback(callback_param);
+}
+
+static irqreturn_t ep93xx_dma_interrupt(int irq, void *dev_id)
+{
+ struct ep93xx_dma_chan *edmac = dev_id;
+ irqreturn_t ret = IRQ_HANDLED;
+
+ spin_lock(&edmac->lock);
+
+ switch (edmac->edma->hw_interrupt(edmac)) {
+ case INTERRUPT_DONE:
+ ep93xx_dma_get_active(edmac)->complete = true;
+ tasklet_schedule(&edmac->tasklet);
+ break;
+
+ case INTERRUPT_NEXT_BUFFER:
+ if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags))
+ tasklet_schedule(&edmac->tasklet);
+ break;
+
+ default:
+ dev_warn(chan2dev(edmac), "unknown interrupt!\n");
+ ret = IRQ_NONE;
+ break;
+ }
+
+ spin_unlock(&edmac->lock);
+ return ret;
+}
+
+/**
+ * ep93xx_dma_tx_submit - set the prepared descriptor(s) to be executed
+ * @tx: descriptor to be executed
+ *
+ * Function will execute given descriptor on the hardware or if the hardware
+ * is busy, queue the descriptor to be executed later on. Returns cookie which
+ * can be used to poll the status of the descriptor.
+ */
+static dma_cookie_t ep93xx_dma_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(tx->chan);
+ struct ep93xx_dma_desc *desc;
+ dma_cookie_t cookie;
+ unsigned long flags;
+
+ spin_lock_irqsave(&edmac->lock, flags);
+
+ cookie = edmac->chan.cookie;
+
+ if (++cookie < 0)
+ cookie = 1;
+
+ desc = container_of(tx, struct ep93xx_dma_desc, txd);
+
+ edmac->chan.cookie = cookie;
+ desc->txd.cookie = cookie;
+
+ /*
+ * If nothing is currently prosessed, we push this descriptor
+ * directly to the hardware. Otherwise we put the descriptor
+ * to the pending queue.
+ */
+ if (list_empty(&edmac->active)) {
+ ep93xx_dma_set_active(edmac, desc);
+ edmac->edma->hw_submit(edmac);
+ } else {
+ list_add_tail(&desc->node, &edmac->queue);
+ }
+
+ spin_unlock_irqrestore(&edmac->lock, flags);
+ return cookie;
+}
+
+/**
+ * ep93xx_dma_alloc_chan_resources - allocate resources for the channel
+ * @chan: channel to allocate resources
+ *
+ * Function allocates necessary resources for the given DMA channel and
+ * returns number of allocated descriptors for the channel. Negative errno
+ * is returned in case of failure.
+ */
+static int ep93xx_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+ struct ep93xx_dma_data *data = chan->private;
+ const char *name = dma_chan_name(chan);
+ int ret, i;
+
+ /* Sanity check the channel parameters */
+ if (!edmac->edma->m2m) {
+ if (!data)
+ return -EINVAL;
+ if (data->port < EP93XX_DMA_I2S1 ||
+ data->port > EP93XX_DMA_IRDA)
+ return -EINVAL;
+ if (data->direction != ep93xx_dma_chan_direction(chan))
+ return -EINVAL;
+ } else {
+ if (data) {
+ switch (data->port) {
+ case EP93XX_DMA_SSP:
+ case EP93XX_DMA_IDE:
+ if (data->direction != DMA_TO_DEVICE &&
+ data->direction != DMA_FROM_DEVICE)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (data && data->name)
+ name = data->name;
+
+ ret = clk_enable(edmac->clk);
+ if (ret)
+ return ret;
+
+ ret = request_irq(edmac->irq, ep93xx_dma_interrupt, 0, name, edmac);
+ if (ret)
+ goto fail_clk_disable;
+
+ spin_lock_irq(&edmac->lock);
+ edmac->last_completed = 1;
+ edmac->chan.cookie = 1;
+ ret = edmac->edma->hw_setup(edmac);
+ spin_unlock_irq(&edmac->lock);
+
+ if (ret)
+ goto fail_free_irq;
+
+ for (i = 0; i < DMA_MAX_CHAN_DESCRIPTORS; i++) {
+ struct ep93xx_dma_desc *desc;
+
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc) {
+ dev_warn(chan2dev(edmac), "not enough descriptors\n");
+ break;
+ }
+
+ INIT_LIST_HEAD(&desc->tx_list);
+
+ dma_async_tx_descriptor_init(&desc->txd, chan);
+ desc->txd.flags = DMA_CTRL_ACK;
+ desc->txd.tx_submit = ep93xx_dma_tx_submit;
+
+ ep93xx_dma_desc_put(edmac, desc);
+ }
+
+ return i;
+
+fail_free_irq:
+ free_irq(edmac->irq, edmac);
+fail_clk_disable:
+ clk_disable(edmac->clk);
+
+ return ret;
+}
+
+/**
+ * ep93xx_dma_free_chan_resources - release resources for the channel
+ * @chan: channel
+ *
+ * Function releases all the resources allocated for the given channel.
+ * The channel must be idle when this is called.
+ */
+static void ep93xx_dma_free_chan_resources(struct dma_chan *chan)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+ struct ep93xx_dma_desc *desc, *d;
+ unsigned long flags;
+ LIST_HEAD(list);
+
+ BUG_ON(!list_empty(&edmac->active));
+ BUG_ON(!list_empty(&edmac->queue));
+
+ spin_lock_irqsave(&edmac->lock, flags);
+ edmac->edma->hw_shutdown(edmac);
+ edmac->runtime_addr = 0;
+ edmac->runtime_ctrl = 0;
+ edmac->buffer = 0;
+ list_splice_init(&edmac->free_list, &list);
+ spin_unlock_irqrestore(&edmac->lock, flags);
+
+ list_for_each_entry_safe(desc, d, &list, node)
+ kfree(desc);
+
+ clk_disable(edmac->clk);
+ free_irq(edmac->irq, edmac);
+}
+
+/**
+ * ep93xx_dma_prep_dma_memcpy - prepare a memcpy DMA operation
+ * @chan: channel
+ * @dest: destination bus address
+ * @src: source bus address
+ * @len: size of the transaction
+ * @flags: flags for the descriptor
+ *
+ * Returns a valid DMA descriptor or %NULL in case of failure.
+ */
+struct dma_async_tx_descriptor *
+ep93xx_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
+ dma_addr_t src, size_t len, unsigned long flags)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+ struct ep93xx_dma_desc *desc, *first;
+ size_t bytes, offset;
+
+ first = NULL;
+ for (offset = 0; offset < len; offset += bytes) {
+ desc = ep93xx_dma_desc_get(edmac);
+ if (!desc) {
+ dev_warn(chan2dev(edmac), "couln't get descriptor\n");
+ goto fail;
+ }
+
+ bytes = min_t(size_t, len - offset, DMA_MAX_CHAN_BYTES);
+
+ desc->src_addr = src + offset;
+ desc->dst_addr = dest + offset;
+ desc->size = bytes;
+
+ if (!first)
+ first = desc;
+ else
+ list_add_tail(&desc->node, &first->tx_list);
+ }
+
+ first->txd.cookie = -EBUSY;
+ first->txd.flags = flags;
+
+ return &first->txd;
+fail:
+ ep93xx_dma_desc_put(edmac, first);
+ return NULL;
+}
+
+/**
+ * ep93xx_dma_prep_slave_sg - prepare a slave DMA operation
+ * @chan: channel
+ * @sgl: list of buffers to transfer
+ * @sg_len: number of entries in @sgl
+ * @dir: direction of tha DMA transfer
+ * @flags: flags for the descriptor
+ *
+ * Returns a valid DMA descriptor or %NULL in case of failure.
+ */
+static struct dma_async_tx_descriptor *
+ep93xx_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_data_direction dir,
+ unsigned long flags)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+ struct ep93xx_dma_desc *desc, *first;
+ struct scatterlist *sg;
+ int i;
+
+ if (!edmac->edma->m2m && dir != ep93xx_dma_chan_direction(chan)) {
+ dev_warn(chan2dev(edmac),
+ "channel was configured with different direction\n");
+ return NULL;
+ }
+
+ if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags)) {
+ dev_warn(chan2dev(edmac),
+ "channel is already used for cyclic transfers\n");
+ return NULL;
+ }
+
+ first = NULL;
+ for_each_sg(sgl, sg, sg_len, i) {
+ size_t sg_len = sg_dma_len(sg);
+
+ if (sg_len > DMA_MAX_CHAN_BYTES) {
+ dev_warn(chan2dev(edmac), "too big transfer size %d\n",
+ sg_len);
+ goto fail;
+ }
+
+ desc = ep93xx_dma_desc_get(edmac);
+ if (!desc) {
+ dev_warn(chan2dev(edmac), "couln't get descriptor\n");
+ goto fail;
+ }
+
+ if (dir == DMA_TO_DEVICE) {
+ desc->src_addr = sg_dma_address(sg);
+ desc->dst_addr = edmac->runtime_addr;
+ } else {
+ desc->src_addr = edmac->runtime_addr;
+ desc->dst_addr = sg_dma_address(sg);
+ }
+ desc->size = sg_len;
+
+ if (!first)
+ first = desc;
+ else
+ list_add_tail(&desc->node, &first->tx_list);
+ }
+
+ first->txd.cookie = -EBUSY;
+ first->txd.flags = flags;
+
+ return &first->txd;
+
+fail:
+ ep93xx_dma_desc_put(edmac, first);
+ return NULL;
+}
+
+/**
+ * ep93xx_dma_prep_dma_cyclic - prepare a cyclic DMA operation
+ * @chan: channel
+ * @dma_addr: DMA mapped address of the buffer
+ * @buf_len: length of the buffer (in bytes)
+ * @period_len: lenght of a single period
+ * @dir: direction of the operation
+ *
+ * Prepares a descriptor for cyclic DMA operation. This means that once the
+ * descriptor is submitted, we will be submitting in a @period_len sized
+ * buffers and calling callback once the period has been elapsed. Transfer
+ * terminates only when client calls dmaengine_terminate_all() for this
+ * channel.
+ *
+ * Returns a valid DMA descriptor or %NULL in case of failure.
+ */
+static struct dma_async_tx_descriptor *
+ep93xx_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr,
+ size_t buf_len, size_t period_len,
+ enum dma_data_direction dir)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+ struct ep93xx_dma_desc *desc, *first;
+ size_t offset = 0;
+
+ if (!edmac->edma->m2m && dir != ep93xx_dma_chan_direction(chan)) {
+ dev_warn(chan2dev(edmac),
+ "channel was configured with different direction\n");
+ return NULL;
+ }
+
+ if (test_and_set_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags)) {
+ dev_warn(chan2dev(edmac),
+ "channel is already used for cyclic transfers\n");
+ return NULL;
+ }
+
+ if (period_len > DMA_MAX_CHAN_BYTES) {
+ dev_warn(chan2dev(edmac), "too big period length %d\n",
+ period_len);
+ return NULL;
+ }
+
+ /* Split the buffer into period size chunks */
+ first = NULL;
+ for (offset = 0; offset < buf_len; offset += period_len) {
+ desc = ep93xx_dma_desc_get(edmac);
+ if (!desc) {
+ dev_warn(chan2dev(edmac), "couln't get descriptor\n");
+ goto fail;
+ }
+
+ if (dir == DMA_TO_DEVICE) {
+ desc->src_addr = dma_addr + offset;
+ desc->dst_addr = edmac->runtime_addr;
+ } else {
+ desc->src_addr = edmac->runtime_addr;
+ desc->dst_addr = dma_addr + offset;
+ }
+
+ desc->size = period_len;
+
+ if (!first)
+ first = desc;
+ else
+ list_add_tail(&desc->node, &first->tx_list);
+ }
+
+ first->txd.cookie = -EBUSY;
+
+ return &first->txd;
+
+fail:
+ ep93xx_dma_desc_put(edmac, first);
+ return NULL;
+}
+
+/**
+ * ep93xx_dma_terminate_all - terminate all transactions
+ * @edmac: channel
+ *
+ * Stops all DMA transactions. All descriptors are put back to the
+ * @edmac->free_list and callbacks are _not_ called.
+ */
+static int ep93xx_dma_terminate_all(struct ep93xx_dma_chan *edmac)
+{
+ struct ep93xx_dma_desc *desc, *_d;
+ unsigned long flags;
+ LIST_HEAD(list);
+
+ spin_lock_irqsave(&edmac->lock, flags);
+ /* First we disable and flush the DMA channel */
+ edmac->edma->hw_shutdown(edmac);
+ clear_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags);
+ list_splice_init(&edmac->active, &list);
+ list_splice_init(&edmac->queue, &list);
+ /*
+ * We then re-enable the channel. This way we can continue submitting
+ * the descriptors by just calling ->hw_submit() again.
+ */
+ edmac->edma->hw_setup(edmac);
+ spin_unlock_irqrestore(&edmac->lock, flags);
+
+ list_for_each_entry_safe(desc, _d, &list, node)
+ ep93xx_dma_desc_put(edmac, desc);
+
+ return 0;
+}
+
+static int ep93xx_dma_slave_config(struct ep93xx_dma_chan *edmac,
+ struct dma_slave_config *config)
+{
+ enum dma_slave_buswidth width;
+ unsigned long flags;
+ u32 addr, ctrl;
+
+ if (!edmac->edma->m2m)
+ return -EINVAL;
+
+ switch (config->direction) {
+ case DMA_FROM_DEVICE:
+ width = config->src_addr_width;
+ addr = config->src_addr;
+ break;
+
+ case DMA_TO_DEVICE:
+ width = config->dst_addr_width;
+ addr = config->dst_addr;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ ctrl = 0;
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ ctrl = M2M_CONTROL_PW_16;
+ break;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ ctrl = M2M_CONTROL_PW_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&edmac->lock, flags);
+ edmac->runtime_addr = addr;
+ edmac->runtime_ctrl = ctrl;
+ spin_unlock_irqrestore(&edmac->lock, flags);
+
+ return 0;
+}
+
+/**
+ * ep93xx_dma_control - manipulate all pending operations on a channel
+ * @chan: channel
+ * @cmd: control command to perform
+ * @arg: optional argument
+ *
+ * Controls the channel. Function returns %0 in case of success or negative
+ * error in case of failure.
+ */
+static int ep93xx_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+ unsigned long arg)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+ struct dma_slave_config *config;
+
+ switch (cmd) {
+ case DMA_TERMINATE_ALL:
+ return ep93xx_dma_terminate_all(edmac);
+
+ case DMA_SLAVE_CONFIG:
+ config = (struct dma_slave_config *)arg;
+ return ep93xx_dma_slave_config(edmac, config);
+
+ default:
+ break;
+ }
+
+ return -ENOSYS;
+}
+
+/**
+ * ep93xx_dma_tx_status - check if a transaction is completed
+ * @chan: channel
+ * @cookie: transaction specific cookie
+ * @state: state of the transaction is stored here if given
+ *
+ * This function can be used to query state of a given transaction.
+ */
+static enum dma_status ep93xx_dma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *state)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+ dma_cookie_t last_used, last_completed;
+ enum dma_status ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&edmac->lock, flags);
+ last_used = chan->cookie;
+ last_completed = edmac->last_completed;
+ spin_unlock_irqrestore(&edmac->lock, flags);
+
+ ret = dma_async_is_complete(cookie, last_completed, last_used);
+ dma_set_tx_state(state, last_completed, last_used, 0);
+
+ return ret;
+}
+
+/**
+ * ep93xx_dma_issue_pending - push pending transactions to the hardware
+ * @chan: channel
+ *
+ * When this function is called, all pending transactions are pushed to the
+ * hardware and executed.
+ */
+static void ep93xx_dma_issue_pending(struct dma_chan *chan)
+{
+ ep93xx_dma_advance_work(to_ep93xx_dma_chan(chan));
+}
+
+static int __init ep93xx_dma_probe(struct platform_device *pdev)
+{
+ struct ep93xx_dma_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct ep93xx_dma_engine *edma;
+ struct dma_device *dma_dev;
+ size_t edma_size;
+ int ret, i;
+
+ edma_size = pdata->num_channels * sizeof(struct ep93xx_dma_chan);
+ edma = kzalloc(sizeof(*edma) + edma_size, GFP_KERNEL);
+ if (!edma)
+ return -ENOMEM;
+
+ dma_dev = &edma->dma_dev;
+ edma->m2m = platform_get_device_id(pdev)->driver_data;
+ edma->num_channels = pdata->num_channels;
+
+ INIT_LIST_HEAD(&dma_dev->channels);
+ for (i = 0; i < pdata->num_channels; i++) {
+ const struct ep93xx_dma_chan_data *cdata = &pdata->channels[i];
+ struct ep93xx_dma_chan *edmac = &edma->channels[i];
+
+ edmac->chan.device = dma_dev;
+ edmac->regs = cdata->base;
+ edmac->irq = cdata->irq;
+ edmac->edma = edma;
+
+ edmac->clk = clk_get(NULL, cdata->name);
+ if (IS_ERR(edmac->clk)) {
+ dev_warn(&pdev->dev, "failed to get clock for %s\n",
+ cdata->name);
+ continue;
+ }
+
+ spin_lock_init(&edmac->lock);
+ INIT_LIST_HEAD(&edmac->active);
+ INIT_LIST_HEAD(&edmac->queue);
+ INIT_LIST_HEAD(&edmac->free_list);
+ tasklet_init(&edmac->tasklet, ep93xx_dma_tasklet,
+ (unsigned long)edmac);
+
+ list_add_tail(&edmac->chan.device_node,
+ &dma_dev->channels);
+ }
+
+ dma_cap_zero(dma_dev->cap_mask);
+ dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
+ dma_cap_set(DMA_CYCLIC, dma_dev->cap_mask);
+
+ dma_dev->dev = &pdev->dev;
+ dma_dev->device_alloc_chan_resources = ep93xx_dma_alloc_chan_resources;
+ dma_dev->device_free_chan_resources = ep93xx_dma_free_chan_resources;
+ dma_dev->device_prep_slave_sg = ep93xx_dma_prep_slave_sg;
+ dma_dev->device_prep_dma_cyclic = ep93xx_dma_prep_dma_cyclic;
+ dma_dev->device_control = ep93xx_dma_control;
+ dma_dev->device_issue_pending = ep93xx_dma_issue_pending;
+ dma_dev->device_tx_status = ep93xx_dma_tx_status;
+
+ dma_set_max_seg_size(dma_dev->dev, DMA_MAX_CHAN_BYTES);
+
+ if (edma->m2m) {
+ dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask);
+ dma_dev->device_prep_dma_memcpy = ep93xx_dma_prep_dma_memcpy;
+
+ edma->hw_setup = m2m_hw_setup;
+ edma->hw_shutdown = m2m_hw_shutdown;
+ edma->hw_submit = m2m_hw_submit;
+ edma->hw_interrupt = m2m_hw_interrupt;
+ } else {
+ dma_cap_set(DMA_PRIVATE, dma_dev->cap_mask);
+
+ edma->hw_setup = m2p_hw_setup;
+ edma->hw_shutdown = m2p_hw_shutdown;
+ edma->hw_submit = m2p_hw_submit;
+ edma->hw_interrupt = m2p_hw_interrupt;
+ }
+
+ ret = dma_async_device_register(dma_dev);
+ if (unlikely(ret)) {
+ for (i = 0; i < edma->num_channels; i++) {
+ struct ep93xx_dma_chan *edmac = &edma->channels[i];
+ if (!IS_ERR_OR_NULL(edmac->clk))
+ clk_put(edmac->clk);
+ }
+ kfree(edma);
+ } else {
+ dev_info(dma_dev->dev, "EP93xx M2%s DMA ready\n",
+ edma->m2m ? "M" : "P");
+ }
+
+ return ret;
+}
+
+static struct platform_device_id ep93xx_dma_driver_ids[] = {
+ { "ep93xx-dma-m2p", 0 },
+ { "ep93xx-dma-m2m", 1 },
+ { },
+};
+
+static struct platform_driver ep93xx_dma_driver = {
+ .driver = {
+ .name = "ep93xx-dma",
+ },
+ .id_table = ep93xx_dma_driver_ids,
+};
+
+static int __init ep93xx_dma_module_init(void)
+{
+ return platform_driver_probe(&ep93xx_dma_driver, ep93xx_dma_probe);
+}
+subsys_initcall(ep93xx_dma_module_init);
+
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
+MODULE_DESCRIPTION("EP93xx DMA driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index de35c3ad8a69..52e2900d9d8e 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -86,9 +86,6 @@ config SPI_BFIN_SPORT
help
Enable support for a SPI bus via the Blackfin SPORT peripheral.
- This driver can also be built as a module. If so, the module
- will be called spi_bfin_sport.
-
config SPI_AU1550
tristate "Au1550/Au12x0 SPI Controller"
depends on (SOC_AU1550 || SOC_AU1200) && EXPERIMENTAL
@@ -97,9 +94,6 @@ config SPI_AU1550
If you say yes to this option, support will be included for the
Au1550 SPI controller (may also work with Au1200,Au1210,Au1250).
- This driver can also be built as a module. If so, the module
- will be called au1550_spi.
-
config SPI_BITBANG
tristate "Utilities for Bitbanging SPI masters"
help
@@ -130,9 +124,6 @@ config SPI_COLDFIRE_QSPI
This enables support for the Coldfire QSPI controller in master
mode.
- This driver can also be built as a module. If so, the module
- will be called coldfire_qspi.
-
config SPI_DAVINCI
tristate "Texas Instruments DaVinci/DA8x/OMAP-L/AM1x SoC SPI controller"
depends on SPI_MASTER && ARCH_DAVINCI
@@ -140,9 +131,6 @@ config SPI_DAVINCI
help
SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules.
- This driver can also be built as a module. The module will be called
- davinci_spi.
-
config SPI_EP93XX
tristate "Cirrus Logic EP93xx SPI controller"
depends on ARCH_EP93XX
@@ -150,9 +138,6 @@ config SPI_EP93XX
This enables using the Cirrus EP93xx SPI controller in master
mode.
- To compile this driver as a module, choose M here. The module will be
- called ep93xx_spi.
-
config SPI_GPIO
tristate "GPIO-based bitbanging SPI Master"
depends on GENERIC_GPIO
@@ -169,21 +154,6 @@ config SPI_GPIO
GPIO operations, you should be able to leverage that for better
speed with a custom version of this driver; see the source code.
-config SPI_IMX_VER_IMX1
- def_bool y if SOC_IMX1
-
-config SPI_IMX_VER_0_0
- def_bool y if SOC_IMX21 || SOC_IMX27
-
-config SPI_IMX_VER_0_4
- def_bool y if SOC_IMX31
-
-config SPI_IMX_VER_0_7
- def_bool y if ARCH_MX25 || SOC_IMX35 || SOC_IMX51 || SOC_IMX53
-
-config SPI_IMX_VER_2_3
- def_bool y if SOC_IMX51 || SOC_IMX53
-
config SPI_IMX
tristate "Freescale i.MX SPI controllers"
depends on ARCH_MXC
@@ -328,16 +298,6 @@ config SPI_S3C24XX_FIQ
no free DMA channels, or when doing transfers that required both
TX and RX data paths.
-config SPI_S3C24XX_GPIO
- tristate "Samsung S3C24XX series SPI by GPIO"
- depends on ARCH_S3C2410 && EXPERIMENTAL
- select SPI_BITBANG
- help
- SPI driver for Samsung S3C24XX series ARM SoCs using
- GPIO lines to provide the SPI bus. This can be used where
- the inbuilt hardware cannot provide the transfer mode, or
- where the board is using non hardware connected pins.
-
config SPI_S3C64XX
tristate "Samsung S3C64XX series type SPI"
depends on (ARCH_S3C64XX || ARCH_S5P64X0)
@@ -385,16 +345,16 @@ config SPI_TI_SSP
This selects an SPI master implementation using a TI sequencer
serial port.
- To compile this driver as a module, choose M here: the
- module will be called ti-ssp-spi.
-
config SPI_TOPCLIFF_PCH
- tristate "Topcliff PCH SPI Controller"
+ tristate "Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH SPI controller"
depends on PCI
help
SPI driver for the Topcliff PCH (Platform Controller Hub) SPI bus
used in some x86 embedded processors.
+ This driver also supports the ML7213, a companion chip for the
+ Atom E6xx series and compatible with the Intel EG20T PCH.
+
config SPI_TXX9
tristate "Toshiba TXx9 SPI controller"
depends on GENERIC_GPIO && CPU_TX49XX
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 0f8c69b6b19e..61c3261c388c 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -7,68 +7,55 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
# small core, mostly translating board-specific
# config declarations into driver model code
obj-$(CONFIG_SPI_MASTER) += spi.o
+obj-$(CONFIG_SPI_SPIDEV) += spidev.o
# SPI master controller drivers (bus)
-obj-$(CONFIG_SPI_ALTERA) += spi_altera.o
-obj-$(CONFIG_SPI_ATMEL) += atmel_spi.o
-obj-$(CONFIG_SPI_ATH79) += ath79_spi.o
-obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o
-obj-$(CONFIG_SPI_BFIN_SPORT) += spi_bfin_sport.o
-obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o
-obj-$(CONFIG_SPI_AU1550) += au1550_spi.o
-obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o
-obj-$(CONFIG_SPI_COLDFIRE_QSPI) += coldfire_qspi.o
-obj-$(CONFIG_SPI_DAVINCI) += davinci_spi.o
-obj-$(CONFIG_SPI_DESIGNWARE) += dw_spi.o
-obj-$(CONFIG_SPI_DW_PCI) += dw_spi_midpci.o
-dw_spi_midpci-objs := dw_spi_pci.o dw_spi_mid.o
-obj-$(CONFIG_SPI_DW_MMIO) += dw_spi_mmio.o
-obj-$(CONFIG_SPI_EP93XX) += ep93xx_spi.o
-obj-$(CONFIG_SPI_GPIO) += spi_gpio.o
-obj-$(CONFIG_SPI_IMX) += spi_imx.o
-obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o
-obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o
-obj-$(CONFIG_SPI_PXA2XX_PCI) += pxa2xx_spi_pci.o
-obj-$(CONFIG_SPI_OC_TINY) += spi_oc_tiny.o
-obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o
-obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o
-obj-$(CONFIG_SPI_OMAP_100K) += omap_spi_100k.o
-obj-$(CONFIG_SPI_ORION) += orion_spi.o
-obj-$(CONFIG_SPI_PL022) += amba-pl022.o
-obj-$(CONFIG_SPI_MPC512x_PSC) += mpc512x_psc_spi.o
-obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o
-obj-$(CONFIG_SPI_MPC52xx) += mpc52xx_spi.o
-obj-$(CONFIG_SPI_FSL_LIB) += spi_fsl_lib.o
-obj-$(CONFIG_SPI_FSL_ESPI) += spi_fsl_espi.o
-obj-$(CONFIG_SPI_FSL_SPI) += spi_fsl_spi.o
-obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o
-obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
-obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o
-obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o
-obj-$(CONFIG_SPI_TEGRA) += spi_tegra.o
-obj-$(CONFIG_SPI_TI_SSP) += ti-ssp-spi.o
-obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o
-obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
-obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o
-obj-$(CONFIG_SPI_SH) += spi_sh.o
-obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o
-obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o
-obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o
-obj-$(CONFIG_SPI_NUC900) += spi_nuc900.o
+obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
+obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
+obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
+obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
+obj-$(CONFIG_SPI_BFIN) += spi-bfin5xx.o
+obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o
+obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o
+obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o
+obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o
+obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o
+obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o
+obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o
+obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o
+spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o
+obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o
+obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o
+obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o
+obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o
+obj-$(CONFIG_SPI_GPIO) += spi-gpio.o
+obj-$(CONFIG_SPI_IMX) += spi-imx.o
+obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
+obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o
+obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
+obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o
+obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o
+obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o
+obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o
+obj-$(CONFIG_SPI_OMAP_100K) += spi-omap-100k.o
+obj-$(CONFIG_SPI_OMAP24XX) += spi-omap2-mcspi.o
+obj-$(CONFIG_SPI_ORION) += spi-orion.o
+obj-$(CONFIG_SPI_PL022) += spi-pl022.o
+obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o
+obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx.o
+obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
+obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
+spi-s3c24xx-hw-y := spi-s3c24xx.o
+spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
+obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o
+obj-$(CONFIG_SPI_SH) += spi-sh.o
+obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
+obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o
+obj-$(CONFIG_SPI_STMP3XXX) += spi-stmp.o
+obj-$(CONFIG_SPI_TEGRA) += spi-tegra.o
+obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o
+obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o
+obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
+obj-$(CONFIG_SPI_TXX9) += spi-txx9.o
+obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o
-# special build for s3c24xx spi driver with fiq support
-spi_s3c24xx_hw-y := spi_s3c24xx.o
-spi_s3c24xx_hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi_s3c24xx_fiq.o
-
-# ... add above this line ...
-
-# SPI protocol drivers (device/link on bus)
-obj-$(CONFIG_SPI_SPIDEV) += spidev.o
-obj-$(CONFIG_SPI_TLE62X0) += tle62x0.o
-# ... add above this line ...
-
-# SPI slave controller drivers (upstream link)
-# ... add above this line ...
-
-# SPI slave drivers (protocol for that link)
-# ... add above this line ...
diff --git a/drivers/spi/atmel_spi.h b/drivers/spi/atmel_spi.h
deleted file mode 100644
index 6e06b6ad3a45..000000000000
--- a/drivers/spi/atmel_spi.h
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Register definitions for Atmel Serial Peripheral Interface (SPI)
- *
- * Copyright (C) 2006 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#ifndef __ATMEL_SPI_H__
-#define __ATMEL_SPI_H__
-
-/* SPI register offsets */
-#define SPI_CR 0x0000
-#define SPI_MR 0x0004
-#define SPI_RDR 0x0008
-#define SPI_TDR 0x000c
-#define SPI_SR 0x0010
-#define SPI_IER 0x0014
-#define SPI_IDR 0x0018
-#define SPI_IMR 0x001c
-#define SPI_CSR0 0x0030
-#define SPI_CSR1 0x0034
-#define SPI_CSR2 0x0038
-#define SPI_CSR3 0x003c
-#define SPI_RPR 0x0100
-#define SPI_RCR 0x0104
-#define SPI_TPR 0x0108
-#define SPI_TCR 0x010c
-#define SPI_RNPR 0x0110
-#define SPI_RNCR 0x0114
-#define SPI_TNPR 0x0118
-#define SPI_TNCR 0x011c
-#define SPI_PTCR 0x0120
-#define SPI_PTSR 0x0124
-
-/* Bitfields in CR */
-#define SPI_SPIEN_OFFSET 0
-#define SPI_SPIEN_SIZE 1
-#define SPI_SPIDIS_OFFSET 1
-#define SPI_SPIDIS_SIZE 1
-#define SPI_SWRST_OFFSET 7
-#define SPI_SWRST_SIZE 1
-#define SPI_LASTXFER_OFFSET 24
-#define SPI_LASTXFER_SIZE 1
-
-/* Bitfields in MR */
-#define SPI_MSTR_OFFSET 0
-#define SPI_MSTR_SIZE 1
-#define SPI_PS_OFFSET 1
-#define SPI_PS_SIZE 1
-#define SPI_PCSDEC_OFFSET 2
-#define SPI_PCSDEC_SIZE 1
-#define SPI_FDIV_OFFSET 3
-#define SPI_FDIV_SIZE 1
-#define SPI_MODFDIS_OFFSET 4
-#define SPI_MODFDIS_SIZE 1
-#define SPI_LLB_OFFSET 7
-#define SPI_LLB_SIZE 1
-#define SPI_PCS_OFFSET 16
-#define SPI_PCS_SIZE 4
-#define SPI_DLYBCS_OFFSET 24
-#define SPI_DLYBCS_SIZE 8
-
-/* Bitfields in RDR */
-#define SPI_RD_OFFSET 0
-#define SPI_RD_SIZE 16
-
-/* Bitfields in TDR */
-#define SPI_TD_OFFSET 0
-#define SPI_TD_SIZE 16
-
-/* Bitfields in SR */
-#define SPI_RDRF_OFFSET 0
-#define SPI_RDRF_SIZE 1
-#define SPI_TDRE_OFFSET 1
-#define SPI_TDRE_SIZE 1
-#define SPI_MODF_OFFSET 2
-#define SPI_MODF_SIZE 1
-#define SPI_OVRES_OFFSET 3
-#define SPI_OVRES_SIZE 1
-#define SPI_ENDRX_OFFSET 4
-#define SPI_ENDRX_SIZE 1
-#define SPI_ENDTX_OFFSET 5
-#define SPI_ENDTX_SIZE 1
-#define SPI_RXBUFF_OFFSET 6
-#define SPI_RXBUFF_SIZE 1
-#define SPI_TXBUFE_OFFSET 7
-#define SPI_TXBUFE_SIZE 1
-#define SPI_NSSR_OFFSET 8
-#define SPI_NSSR_SIZE 1
-#define SPI_TXEMPTY_OFFSET 9
-#define SPI_TXEMPTY_SIZE 1
-#define SPI_SPIENS_OFFSET 16
-#define SPI_SPIENS_SIZE 1
-
-/* Bitfields in CSR0 */
-#define SPI_CPOL_OFFSET 0
-#define SPI_CPOL_SIZE 1
-#define SPI_NCPHA_OFFSET 1
-#define SPI_NCPHA_SIZE 1
-#define SPI_CSAAT_OFFSET 3
-#define SPI_CSAAT_SIZE 1
-#define SPI_BITS_OFFSET 4
-#define SPI_BITS_SIZE 4
-#define SPI_SCBR_OFFSET 8
-#define SPI_SCBR_SIZE 8
-#define SPI_DLYBS_OFFSET 16
-#define SPI_DLYBS_SIZE 8
-#define SPI_DLYBCT_OFFSET 24
-#define SPI_DLYBCT_SIZE 8
-
-/* Bitfields in RCR */
-#define SPI_RXCTR_OFFSET 0
-#define SPI_RXCTR_SIZE 16
-
-/* Bitfields in TCR */
-#define SPI_TXCTR_OFFSET 0
-#define SPI_TXCTR_SIZE 16
-
-/* Bitfields in RNCR */
-#define SPI_RXNCR_OFFSET 0
-#define SPI_RXNCR_SIZE 16
-
-/* Bitfields in TNCR */
-#define SPI_TXNCR_OFFSET 0
-#define SPI_TXNCR_SIZE 16
-
-/* Bitfields in PTCR */
-#define SPI_RXTEN_OFFSET 0
-#define SPI_RXTEN_SIZE 1
-#define SPI_RXTDIS_OFFSET 1
-#define SPI_RXTDIS_SIZE 1
-#define SPI_TXTEN_OFFSET 8
-#define SPI_TXTEN_SIZE 1
-#define SPI_TXTDIS_OFFSET 9
-#define SPI_TXTDIS_SIZE 1
-
-/* Constants for BITS */
-#define SPI_BITS_8_BPT 0
-#define SPI_BITS_9_BPT 1
-#define SPI_BITS_10_BPT 2
-#define SPI_BITS_11_BPT 3
-#define SPI_BITS_12_BPT 4
-#define SPI_BITS_13_BPT 5
-#define SPI_BITS_14_BPT 6
-#define SPI_BITS_15_BPT 7
-#define SPI_BITS_16_BPT 8
-
-/* Bit manipulation macros */
-#define SPI_BIT(name) \
- (1 << SPI_##name##_OFFSET)
-#define SPI_BF(name,value) \
- (((value) & ((1 << SPI_##name##_SIZE) - 1)) << SPI_##name##_OFFSET)
-#define SPI_BFEXT(name,value) \
- (((value) >> SPI_##name##_OFFSET) & ((1 << SPI_##name##_SIZE) - 1))
-#define SPI_BFINS(name,value,old) \
- ( ((old) & ~(((1 << SPI_##name##_SIZE) - 1) << SPI_##name##_OFFSET)) \
- | SPI_BF(name,value))
-
-/* Register access macros */
-#define spi_readl(port,reg) \
- __raw_readl((port)->regs + SPI_##reg)
-#define spi_writel(port,reg,value) \
- __raw_writel((value), (port)->regs + SPI_##reg)
-
-#endif /* __ATMEL_SPI_H__ */
diff --git a/drivers/spi/spi_altera.c b/drivers/spi/spi-altera.c
index 4813a63ce6fb..4813a63ce6fb 100644
--- a/drivers/spi/spi_altera.c
+++ b/drivers/spi/spi-altera.c
diff --git a/drivers/spi/ath79_spi.c b/drivers/spi/spi-ath79.c
index fcff810ea3b0..03019bf5a5e9 100644
--- a/drivers/spi/ath79_spi.c
+++ b/drivers/spi/spi-ath79.c
@@ -232,7 +232,7 @@ static __devinit int ath79_spi_probe(struct platform_device *pdev)
goto err_put_master;
}
- sp->base = ioremap(r->start, r->end - r->start + 1);
+ sp->base = ioremap(r->start, resource_size(r));
if (!sp->base) {
ret = -ENXIO;
goto err_put_master;
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/spi-atmel.c
index 08711e9202ab..82dee9a6c0de 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/spi-atmel.c
@@ -25,7 +25,160 @@
#include <mach/gpio.h>
#include <mach/cpu.h>
-#include "atmel_spi.h"
+/* SPI register offsets */
+#define SPI_CR 0x0000
+#define SPI_MR 0x0004
+#define SPI_RDR 0x0008
+#define SPI_TDR 0x000c
+#define SPI_SR 0x0010
+#define SPI_IER 0x0014
+#define SPI_IDR 0x0018
+#define SPI_IMR 0x001c
+#define SPI_CSR0 0x0030
+#define SPI_CSR1 0x0034
+#define SPI_CSR2 0x0038
+#define SPI_CSR3 0x003c
+#define SPI_RPR 0x0100
+#define SPI_RCR 0x0104
+#define SPI_TPR 0x0108
+#define SPI_TCR 0x010c
+#define SPI_RNPR 0x0110
+#define SPI_RNCR 0x0114
+#define SPI_TNPR 0x0118
+#define SPI_TNCR 0x011c
+#define SPI_PTCR 0x0120
+#define SPI_PTSR 0x0124
+
+/* Bitfields in CR */
+#define SPI_SPIEN_OFFSET 0
+#define SPI_SPIEN_SIZE 1
+#define SPI_SPIDIS_OFFSET 1
+#define SPI_SPIDIS_SIZE 1
+#define SPI_SWRST_OFFSET 7
+#define SPI_SWRST_SIZE 1
+#define SPI_LASTXFER_OFFSET 24
+#define SPI_LASTXFER_SIZE 1
+
+/* Bitfields in MR */
+#define SPI_MSTR_OFFSET 0
+#define SPI_MSTR_SIZE 1
+#define SPI_PS_OFFSET 1
+#define SPI_PS_SIZE 1
+#define SPI_PCSDEC_OFFSET 2
+#define SPI_PCSDEC_SIZE 1
+#define SPI_FDIV_OFFSET 3
+#define SPI_FDIV_SIZE 1
+#define SPI_MODFDIS_OFFSET 4
+#define SPI_MODFDIS_SIZE 1
+#define SPI_LLB_OFFSET 7
+#define SPI_LLB_SIZE 1
+#define SPI_PCS_OFFSET 16
+#define SPI_PCS_SIZE 4
+#define SPI_DLYBCS_OFFSET 24
+#define SPI_DLYBCS_SIZE 8
+
+/* Bitfields in RDR */
+#define SPI_RD_OFFSET 0
+#define SPI_RD_SIZE 16
+
+/* Bitfields in TDR */
+#define SPI_TD_OFFSET 0
+#define SPI_TD_SIZE 16
+
+/* Bitfields in SR */
+#define SPI_RDRF_OFFSET 0
+#define SPI_RDRF_SIZE 1
+#define SPI_TDRE_OFFSET 1
+#define SPI_TDRE_SIZE 1
+#define SPI_MODF_OFFSET 2
+#define SPI_MODF_SIZE 1
+#define SPI_OVRES_OFFSET 3
+#define SPI_OVRES_SIZE 1
+#define SPI_ENDRX_OFFSET 4
+#define SPI_ENDRX_SIZE 1
+#define SPI_ENDTX_OFFSET 5
+#define SPI_ENDTX_SIZE 1
+#define SPI_RXBUFF_OFFSET 6
+#define SPI_RXBUFF_SIZE 1
+#define SPI_TXBUFE_OFFSET 7
+#define SPI_TXBUFE_SIZE 1
+#define SPI_NSSR_OFFSET 8
+#define SPI_NSSR_SIZE 1
+#define SPI_TXEMPTY_OFFSET 9
+#define SPI_TXEMPTY_SIZE 1
+#define SPI_SPIENS_OFFSET 16
+#define SPI_SPIENS_SIZE 1
+
+/* Bitfields in CSR0 */
+#define SPI_CPOL_OFFSET 0
+#define SPI_CPOL_SIZE 1
+#define SPI_NCPHA_OFFSET 1
+#define SPI_NCPHA_SIZE 1
+#define SPI_CSAAT_OFFSET 3
+#define SPI_CSAAT_SIZE 1
+#define SPI_BITS_OFFSET 4
+#define SPI_BITS_SIZE 4
+#define SPI_SCBR_OFFSET 8
+#define SPI_SCBR_SIZE 8
+#define SPI_DLYBS_OFFSET 16
+#define SPI_DLYBS_SIZE 8
+#define SPI_DLYBCT_OFFSET 24
+#define SPI_DLYBCT_SIZE 8
+
+/* Bitfields in RCR */
+#define SPI_RXCTR_OFFSET 0
+#define SPI_RXCTR_SIZE 16
+
+/* Bitfields in TCR */
+#define SPI_TXCTR_OFFSET 0
+#define SPI_TXCTR_SIZE 16
+
+/* Bitfields in RNCR */
+#define SPI_RXNCR_OFFSET 0
+#define SPI_RXNCR_SIZE 16
+
+/* Bitfields in TNCR */
+#define SPI_TXNCR_OFFSET 0
+#define SPI_TXNCR_SIZE 16
+
+/* Bitfields in PTCR */
+#define SPI_RXTEN_OFFSET 0
+#define SPI_RXTEN_SIZE 1
+#define SPI_RXTDIS_OFFSET 1
+#define SPI_RXTDIS_SIZE 1
+#define SPI_TXTEN_OFFSET 8
+#define SPI_TXTEN_SIZE 1
+#define SPI_TXTDIS_OFFSET 9
+#define SPI_TXTDIS_SIZE 1
+
+/* Constants for BITS */
+#define SPI_BITS_8_BPT 0
+#define SPI_BITS_9_BPT 1
+#define SPI_BITS_10_BPT 2
+#define SPI_BITS_11_BPT 3
+#define SPI_BITS_12_BPT 4
+#define SPI_BITS_13_BPT 5
+#define SPI_BITS_14_BPT 6
+#define SPI_BITS_15_BPT 7
+#define SPI_BITS_16_BPT 8
+
+/* Bit manipulation macros */
+#define SPI_BIT(name) \
+ (1 << SPI_##name##_OFFSET)
+#define SPI_BF(name,value) \
+ (((value) & ((1 << SPI_##name##_SIZE) - 1)) << SPI_##name##_OFFSET)
+#define SPI_BFEXT(name,value) \
+ (((value) >> SPI_##name##_OFFSET) & ((1 << SPI_##name##_SIZE) - 1))
+#define SPI_BFINS(name,value,old) \
+ ( ((old) & ~(((1 << SPI_##name##_SIZE) - 1) << SPI_##name##_OFFSET)) \
+ | SPI_BF(name,value))
+
+/* Register access macros */
+#define spi_readl(port,reg) \
+ __raw_readl((port)->regs + SPI_##reg)
+#define spi_writel(port,reg,value) \
+ __raw_writel((value), (port)->regs + SPI_##reg)
+
/*
* The core SPI transfer engine just talks to a register bank to set up
diff --git a/drivers/spi/au1550_spi.c b/drivers/spi/spi-au1550.c
index b50563d320e1..bddee5f516b2 100644
--- a/drivers/spi/au1550_spi.c
+++ b/drivers/spi/spi-au1550.c
@@ -1,5 +1,5 @@
/*
- * au1550_spi.c - au1550 psc spi controller driver
+ * au1550 psc spi controller driver
* may work also with au1200, au1210, au1250
* will not work on au1000, au1100 and au1500 (no full spi controller there)
*
diff --git a/drivers/spi/spi_bfin_sport.c b/drivers/spi/spi-bfin-sport.c
index e557ff617b11..e557ff617b11 100644
--- a/drivers/spi/spi_bfin_sport.c
+++ b/drivers/spi/spi-bfin-sport.c
diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi-bfin5xx.c
index cc880c95e7de..b8d25f2b7038 100644
--- a/drivers/spi/spi_bfin5xx.c
+++ b/drivers/spi/spi-bfin5xx.c
@@ -58,7 +58,7 @@ struct bfin_spi_master_data {
struct spi_master *master;
/* Regs base of SPI controller */
- void __iomem *regs_base;
+ struct bfin_spi_regs __iomem *regs;
/* Pin request list */
u16 *pin_req;
@@ -122,34 +122,14 @@ struct bfin_spi_slave_data {
const struct bfin_spi_transfer_ops *ops;
};
-#define DEFINE_SPI_REG(reg, off) \
-static inline u16 read_##reg(struct bfin_spi_master_data *drv_data) \
- { return bfin_read16(drv_data->regs_base + off); } \
-static inline void write_##reg(struct bfin_spi_master_data *drv_data, u16 v) \
- { bfin_write16(drv_data->regs_base + off, v); }
-
-DEFINE_SPI_REG(CTRL, 0x00)
-DEFINE_SPI_REG(FLAG, 0x04)
-DEFINE_SPI_REG(STAT, 0x08)
-DEFINE_SPI_REG(TDBR, 0x0C)
-DEFINE_SPI_REG(RDBR, 0x10)
-DEFINE_SPI_REG(BAUD, 0x14)
-DEFINE_SPI_REG(SHAW, 0x18)
-
static void bfin_spi_enable(struct bfin_spi_master_data *drv_data)
{
- u16 cr;
-
- cr = read_CTRL(drv_data);
- write_CTRL(drv_data, (cr | BIT_CTL_ENABLE));
+ bfin_write_or(&drv_data->regs->ctl, BIT_CTL_ENABLE);
}
static void bfin_spi_disable(struct bfin_spi_master_data *drv_data)
{
- u16 cr;
-
- cr = read_CTRL(drv_data);
- write_CTRL(drv_data, (cr & (~BIT_CTL_ENABLE)));
+ bfin_write_and(&drv_data->regs->ctl, ~BIT_CTL_ENABLE);
}
/* Caculate the SPI_BAUD register value based on input HZ */
@@ -172,10 +152,10 @@ static int bfin_spi_flush(struct bfin_spi_master_data *drv_data)
unsigned long limit = loops_per_jiffy << 1;
/* wait for stop and clear stat */
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF) && --limit)
+ while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_SPIF) && --limit)
cpu_relax();
- write_STAT(drv_data, BIT_STAT_CLR);
+ bfin_write(&drv_data->regs->stat, BIT_STAT_CLR);
return limit;
}
@@ -183,29 +163,19 @@ static int bfin_spi_flush(struct bfin_spi_master_data *drv_data)
/* Chip select operation functions for cs_change flag */
static void bfin_spi_cs_active(struct bfin_spi_master_data *drv_data, struct bfin_spi_slave_data *chip)
{
- if (likely(chip->chip_select_num < MAX_CTRL_CS)) {
- u16 flag = read_FLAG(drv_data);
-
- flag &= ~chip->flag;
-
- write_FLAG(drv_data, flag);
- } else {
+ if (likely(chip->chip_select_num < MAX_CTRL_CS))
+ bfin_write_and(&drv_data->regs->flg, ~chip->flag);
+ else
gpio_set_value(chip->cs_gpio, 0);
- }
}
static void bfin_spi_cs_deactive(struct bfin_spi_master_data *drv_data,
struct bfin_spi_slave_data *chip)
{
- if (likely(chip->chip_select_num < MAX_CTRL_CS)) {
- u16 flag = read_FLAG(drv_data);
-
- flag |= chip->flag;
-
- write_FLAG(drv_data, flag);
- } else {
+ if (likely(chip->chip_select_num < MAX_CTRL_CS))
+ bfin_write_or(&drv_data->regs->flg, chip->flag);
+ else
gpio_set_value(chip->cs_gpio, 1);
- }
/* Move delay here for consistency */
if (chip->cs_chg_udelay)
@@ -216,25 +186,15 @@ static void bfin_spi_cs_deactive(struct bfin_spi_master_data *drv_data,
static inline void bfin_spi_cs_enable(struct bfin_spi_master_data *drv_data,
struct bfin_spi_slave_data *chip)
{
- if (chip->chip_select_num < MAX_CTRL_CS) {
- u16 flag = read_FLAG(drv_data);
-
- flag |= (chip->flag >> 8);
-
- write_FLAG(drv_data, flag);
- }
+ if (chip->chip_select_num < MAX_CTRL_CS)
+ bfin_write_or(&drv_data->regs->flg, chip->flag >> 8);
}
static inline void bfin_spi_cs_disable(struct bfin_spi_master_data *drv_data,
struct bfin_spi_slave_data *chip)
{
- if (chip->chip_select_num < MAX_CTRL_CS) {
- u16 flag = read_FLAG(drv_data);
-
- flag &= ~(chip->flag >> 8);
-
- write_FLAG(drv_data, flag);
- }
+ if (chip->chip_select_num < MAX_CTRL_CS)
+ bfin_write_and(&drv_data->regs->flg, ~(chip->flag >> 8));
}
/* stop controller and re-config current chip*/
@@ -243,15 +203,15 @@ static void bfin_spi_restore_state(struct bfin_spi_master_data *drv_data)
struct bfin_spi_slave_data *chip = drv_data->cur_chip;
/* Clear status and disable clock */
- write_STAT(drv_data, BIT_STAT_CLR);
+ bfin_write(&drv_data->regs->stat, BIT_STAT_CLR);
bfin_spi_disable(drv_data);
dev_dbg(&drv_data->pdev->dev, "restoring spi ctl state\n");
SSYNC();
/* Load the registers */
- write_CTRL(drv_data, chip->ctl_reg);
- write_BAUD(drv_data, chip->baud);
+ bfin_write(&drv_data->regs->ctl, chip->ctl_reg);
+ bfin_write(&drv_data->regs->baud, chip->baud);
bfin_spi_enable(drv_data);
bfin_spi_cs_active(drv_data, chip);
@@ -260,7 +220,7 @@ static void bfin_spi_restore_state(struct bfin_spi_master_data *drv_data)
/* used to kick off transfer in rx mode and read unwanted RX data */
static inline void bfin_spi_dummy_read(struct bfin_spi_master_data *drv_data)
{
- (void) read_RDBR(drv_data);
+ (void) bfin_read(&drv_data->regs->rdbr);
}
static void bfin_spi_u8_writer(struct bfin_spi_master_data *drv_data)
@@ -269,10 +229,10 @@ static void bfin_spi_u8_writer(struct bfin_spi_master_data *drv_data)
bfin_spi_dummy_read(drv_data);
while (drv_data->tx < drv_data->tx_end) {
- write_TDBR(drv_data, (*(u8 *) (drv_data->tx++)));
+ bfin_write(&drv_data->regs->tdbr, (*(u8 *) (drv_data->tx++)));
/* wait until transfer finished.
checking SPIF or TXS may not guarantee transfer completion */
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
+ while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
cpu_relax();
/* discard RX data and clear RXS */
bfin_spi_dummy_read(drv_data);
@@ -287,10 +247,10 @@ static void bfin_spi_u8_reader(struct bfin_spi_master_data *drv_data)
bfin_spi_dummy_read(drv_data);
while (drv_data->rx < drv_data->rx_end) {
- write_TDBR(drv_data, tx_val);
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
+ bfin_write(&drv_data->regs->tdbr, tx_val);
+ while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
cpu_relax();
- *(u8 *) (drv_data->rx++) = read_RDBR(drv_data);
+ *(u8 *) (drv_data->rx++) = bfin_read(&drv_data->regs->rdbr);
}
}
@@ -300,10 +260,10 @@ static void bfin_spi_u8_duplex(struct bfin_spi_master_data *drv_data)
bfin_spi_dummy_read(drv_data);
while (drv_data->rx < drv_data->rx_end) {
- write_TDBR(drv_data, (*(u8 *) (drv_data->tx++)));
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
+ bfin_write(&drv_data->regs->tdbr, (*(u8 *) (drv_data->tx++)));
+ while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
cpu_relax();
- *(u8 *) (drv_data->rx++) = read_RDBR(drv_data);
+ *(u8 *) (drv_data->rx++) = bfin_read(&drv_data->regs->rdbr);
}
}
@@ -319,11 +279,11 @@ static void bfin_spi_u16_writer(struct bfin_spi_master_data *drv_data)
bfin_spi_dummy_read(drv_data);
while (drv_data->tx < drv_data->tx_end) {
- write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
+ bfin_write(&drv_data->regs->tdbr, (*(u16 *) (drv_data->tx)));
drv_data->tx += 2;
/* wait until transfer finished.
checking SPIF or TXS may not guarantee transfer completion */
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
+ while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
cpu_relax();
/* discard RX data and clear RXS */
bfin_spi_dummy_read(drv_data);
@@ -338,10 +298,10 @@ static void bfin_spi_u16_reader(struct bfin_spi_master_data *drv_data)
bfin_spi_dummy_read(drv_data);
while (drv_data->rx < drv_data->rx_end) {
- write_TDBR(drv_data, tx_val);
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
+ bfin_write(&drv_data->regs->tdbr, tx_val);
+ while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
cpu_relax();
- *(u16 *) (drv_data->rx) = read_RDBR(drv_data);
+ *(u16 *) (drv_data->rx) = bfin_read(&drv_data->regs->rdbr);
drv_data->rx += 2;
}
}
@@ -352,11 +312,11 @@ static void bfin_spi_u16_duplex(struct bfin_spi_master_data *drv_data)
bfin_spi_dummy_read(drv_data);
while (drv_data->rx < drv_data->rx_end) {
- write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
+ bfin_write(&drv_data->regs->tdbr, (*(u16 *) (drv_data->tx)));
drv_data->tx += 2;
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
+ while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
cpu_relax();
- *(u16 *) (drv_data->rx) = read_RDBR(drv_data);
+ *(u16 *) (drv_data->rx) = bfin_read(&drv_data->regs->rdbr);
drv_data->rx += 2;
}
}
@@ -428,7 +388,7 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
int loop = 0;
/* wait until transfer finished. */
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
+ while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
cpu_relax();
if ((drv_data->tx && drv_data->tx >= drv_data->tx_end) ||
@@ -439,11 +399,11 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
if (n_bytes % 2) {
u16 *buf = (u16 *)drv_data->rx;
for (loop = 0; loop < n_bytes / 2; loop++)
- *buf++ = read_RDBR(drv_data);
+ *buf++ = bfin_read(&drv_data->regs->rdbr);
} else {
u8 *buf = (u8 *)drv_data->rx;
for (loop = 0; loop < n_bytes; loop++)
- *buf++ = read_RDBR(drv_data);
+ *buf++ = bfin_read(&drv_data->regs->rdbr);
}
drv_data->rx += n_bytes;
}
@@ -468,15 +428,15 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
u16 *buf = (u16 *)drv_data->rx;
u16 *buf2 = (u16 *)drv_data->tx;
for (loop = 0; loop < n_bytes / 2; loop++) {
- *buf++ = read_RDBR(drv_data);
- write_TDBR(drv_data, *buf2++);
+ *buf++ = bfin_read(&drv_data->regs->rdbr);
+ bfin_write(&drv_data->regs->tdbr, *buf2++);
}
} else {
u8 *buf = (u8 *)drv_data->rx;
u8 *buf2 = (u8 *)drv_data->tx;
for (loop = 0; loop < n_bytes; loop++) {
- *buf++ = read_RDBR(drv_data);
- write_TDBR(drv_data, *buf2++);
+ *buf++ = bfin_read(&drv_data->regs->rdbr);
+ bfin_write(&drv_data->regs->tdbr, *buf2++);
}
}
} else if (drv_data->rx) {
@@ -485,14 +445,14 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
if (n_bytes % 2) {
u16 *buf = (u16 *)drv_data->rx;
for (loop = 0; loop < n_bytes / 2; loop++) {
- *buf++ = read_RDBR(drv_data);
- write_TDBR(drv_data, chip->idle_tx_val);
+ *buf++ = bfin_read(&drv_data->regs->rdbr);
+ bfin_write(&drv_data->regs->tdbr, chip->idle_tx_val);
}
} else {
u8 *buf = (u8 *)drv_data->rx;
for (loop = 0; loop < n_bytes; loop++) {
- *buf++ = read_RDBR(drv_data);
- write_TDBR(drv_data, chip->idle_tx_val);
+ *buf++ = bfin_read(&drv_data->regs->rdbr);
+ bfin_write(&drv_data->regs->tdbr, chip->idle_tx_val);
}
}
} else if (drv_data->tx) {
@@ -501,14 +461,14 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
if (n_bytes % 2) {
u16 *buf = (u16 *)drv_data->tx;
for (loop = 0; loop < n_bytes / 2; loop++) {
- read_RDBR(drv_data);
- write_TDBR(drv_data, *buf++);
+ bfin_read(&drv_data->regs->rdbr);
+ bfin_write(&drv_data->regs->tdbr, *buf++);
}
} else {
u8 *buf = (u8 *)drv_data->tx;
for (loop = 0; loop < n_bytes; loop++) {
- read_RDBR(drv_data);
- write_TDBR(drv_data, *buf++);
+ bfin_read(&drv_data->regs->rdbr);
+ bfin_write(&drv_data->regs->tdbr, *buf++);
}
}
}
@@ -528,19 +488,19 @@ static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id)
struct spi_message *msg = drv_data->cur_msg;
unsigned long timeout;
unsigned short dmastat = get_dma_curr_irqstat(drv_data->dma_channel);
- u16 spistat = read_STAT(drv_data);
+ u16 spistat = bfin_read(&drv_data->regs->stat);
dev_dbg(&drv_data->pdev->dev,
"in dma_irq_handler dmastat:0x%x spistat:0x%x\n",
dmastat, spistat);
if (drv_data->rx != NULL) {
- u16 cr = read_CTRL(drv_data);
+ u16 cr = bfin_read(&drv_data->regs->ctl);
/* discard old RX data and clear RXS */
bfin_spi_dummy_read(drv_data);
- write_CTRL(drv_data, cr & ~BIT_CTL_ENABLE); /* Disable SPI */
- write_CTRL(drv_data, cr & ~BIT_CTL_TIMOD); /* Restore State */
- write_STAT(drv_data, BIT_STAT_CLR); /* Clear Status */
+ bfin_write(&drv_data->regs->ctl, cr & ~BIT_CTL_ENABLE); /* Disable SPI */
+ bfin_write(&drv_data->regs->ctl, cr & ~BIT_CTL_TIMOD); /* Restore State */
+ bfin_write(&drv_data->regs->stat, BIT_STAT_CLR); /* Clear Status */
}
clear_dma_irqstat(drv_data->dma_channel);
@@ -552,17 +512,17 @@ static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id)
* register until it goes low for 2 successive reads
*/
if (drv_data->tx != NULL) {
- while ((read_STAT(drv_data) & BIT_STAT_TXS) ||
- (read_STAT(drv_data) & BIT_STAT_TXS))
+ while ((bfin_read(&drv_data->regs->stat) & BIT_STAT_TXS) ||
+ (bfin_read(&drv_data->regs->stat) & BIT_STAT_TXS))
cpu_relax();
}
dev_dbg(&drv_data->pdev->dev,
"in dma_irq_handler dmastat:0x%x spistat:0x%x\n",
- dmastat, read_STAT(drv_data));
+ dmastat, bfin_read(&drv_data->regs->stat));
timeout = jiffies + HZ;
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
+ while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_SPIF))
if (!time_before(jiffies, timeout)) {
dev_warn(&drv_data->pdev->dev, "timeout waiting for SPIF");
break;
@@ -699,9 +659,9 @@ static void bfin_spi_pump_transfers(unsigned long data)
bfin_spi_giveback(drv_data);
return;
}
- cr = read_CTRL(drv_data) & ~(BIT_CTL_TIMOD | BIT_CTL_WORDSIZE);
+ cr = bfin_read(&drv_data->regs->ctl) & ~(BIT_CTL_TIMOD | BIT_CTL_WORDSIZE);
cr |= cr_width;
- write_CTRL(drv_data, cr);
+ bfin_write(&drv_data->regs->ctl, cr);
dev_dbg(&drv_data->pdev->dev,
"transfer: drv_data->ops is %p, chip->ops is %p, u8_ops is %p\n",
@@ -712,11 +672,11 @@ static void bfin_spi_pump_transfers(unsigned long data)
/* Speed setup (surely valid because already checked) */
if (transfer->speed_hz)
- write_BAUD(drv_data, hz_to_spi_baud(transfer->speed_hz));
+ bfin_write(&drv_data->regs->baud, hz_to_spi_baud(transfer->speed_hz));
else
- write_BAUD(drv_data, chip->baud);
+ bfin_write(&drv_data->regs->baud, chip->baud);
- write_STAT(drv_data, BIT_STAT_CLR);
+ bfin_write(&drv_data->regs->stat, BIT_STAT_CLR);
bfin_spi_cs_active(drv_data, chip);
dev_dbg(&drv_data->pdev->dev,
@@ -749,7 +709,7 @@ static void bfin_spi_pump_transfers(unsigned long data)
}
/* poll for SPI completion before start */
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
+ while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_SPIF))
cpu_relax();
/* dirty hack for autobuffer DMA mode */
@@ -766,7 +726,7 @@ static void bfin_spi_pump_transfers(unsigned long data)
enable_dma(drv_data->dma_channel);
/* start SPI transfer */
- write_CTRL(drv_data, cr | BIT_CTL_TIMOD_DMA_TX);
+ bfin_write(&drv_data->regs->ctl, cr | BIT_CTL_TIMOD_DMA_TX);
/* just return here, there can only be one transfer
* in this mode
@@ -821,7 +781,7 @@ static void bfin_spi_pump_transfers(unsigned long data)
set_dma_config(drv_data->dma_channel, dma_config);
local_irq_save(flags);
SSYNC();
- write_CTRL(drv_data, cr);
+ bfin_write(&drv_data->regs->ctl, cr);
enable_dma(drv_data->dma_channel);
dma_enable_irq(drv_data->dma_channel);
local_irq_restore(flags);
@@ -835,7 +795,7 @@ static void bfin_spi_pump_transfers(unsigned long data)
* problems with setting up the output value in TDBR prior to the
* start of the transfer.
*/
- write_CTRL(drv_data, cr | BIT_CTL_TXMOD);
+ bfin_write(&drv_data->regs->ctl, cr | BIT_CTL_TXMOD);
if (chip->pio_interrupt) {
/* SPI irq should have been disabled by now */
@@ -845,19 +805,19 @@ static void bfin_spi_pump_transfers(unsigned long data)
/* start transfer */
if (drv_data->tx == NULL)
- write_TDBR(drv_data, chip->idle_tx_val);
+ bfin_write(&drv_data->regs->tdbr, chip->idle_tx_val);
else {
int loop;
if (bits_per_word % 16 == 0) {
u16 *buf = (u16 *)drv_data->tx;
for (loop = 0; loop < bits_per_word / 16;
loop++) {
- write_TDBR(drv_data, *buf++);
+ bfin_write(&drv_data->regs->tdbr, *buf++);
}
} else if (bits_per_word % 8 == 0) {
u8 *buf = (u8 *)drv_data->tx;
for (loop = 0; loop < bits_per_word / 8; loop++)
- write_TDBR(drv_data, *buf++);
+ bfin_write(&drv_data->regs->tdbr, *buf++);
}
drv_data->tx += drv_data->n_bytes;
@@ -1005,7 +965,7 @@ static int bfin_spi_transfer(struct spi_device *spi, struct spi_message *msg)
#define MAX_SPI_SSEL 7
-static u16 ssel[][MAX_SPI_SSEL] = {
+static const u16 ssel[][MAX_SPI_SSEL] = {
{P_SPI0_SSEL1, P_SPI0_SSEL2, P_SPI0_SSEL3,
P_SPI0_SSEL4, P_SPI0_SSEL5,
P_SPI0_SSEL6, P_SPI0_SSEL7},
@@ -1226,7 +1186,7 @@ static void bfin_spi_cleanup(struct spi_device *spi)
spi_set_ctldata(spi, NULL);
}
-static inline int bfin_spi_init_queue(struct bfin_spi_master_data *drv_data)
+static int bfin_spi_init_queue(struct bfin_spi_master_data *drv_data)
{
INIT_LIST_HEAD(&drv_data->queue);
spin_lock_init(&drv_data->lock);
@@ -1248,7 +1208,7 @@ static inline int bfin_spi_init_queue(struct bfin_spi_master_data *drv_data)
return 0;
}
-static inline int bfin_spi_start_queue(struct bfin_spi_master_data *drv_data)
+static int bfin_spi_start_queue(struct bfin_spi_master_data *drv_data)
{
unsigned long flags;
@@ -1270,7 +1230,7 @@ static inline int bfin_spi_start_queue(struct bfin_spi_master_data *drv_data)
return 0;
}
-static inline int bfin_spi_stop_queue(struct bfin_spi_master_data *drv_data)
+static int bfin_spi_stop_queue(struct bfin_spi_master_data *drv_data)
{
unsigned long flags;
unsigned limit = 500;
@@ -1299,7 +1259,7 @@ static inline int bfin_spi_stop_queue(struct bfin_spi_master_data *drv_data)
return status;
}
-static inline int bfin_spi_destroy_queue(struct bfin_spi_master_data *drv_data)
+static int bfin_spi_destroy_queue(struct bfin_spi_master_data *drv_data)
{
int status;
@@ -1353,8 +1313,8 @@ static int __init bfin_spi_probe(struct platform_device *pdev)
goto out_error_get_res;
}
- drv_data->regs_base = ioremap(res->start, resource_size(res));
- if (drv_data->regs_base == NULL) {
+ drv_data->regs = ioremap(res->start, resource_size(res));
+ if (drv_data->regs == NULL) {
dev_err(dev, "Cannot map IO\n");
status = -ENXIO;
goto out_error_ioremap;
@@ -1397,8 +1357,8 @@ static int __init bfin_spi_probe(struct platform_device *pdev)
/* Reset SPI registers. If these registers were used by the boot loader,
* the sky may fall on your head if you enable the dma controller.
*/
- write_CTRL(drv_data, BIT_CTL_CPHA | BIT_CTL_MASTER);
- write_FLAG(drv_data, 0xFF00);
+ bfin_write(&drv_data->regs->ctl, BIT_CTL_CPHA | BIT_CTL_MASTER);
+ bfin_write(&drv_data->regs->flg, 0xFF00);
/* Register with the SPI framework */
platform_set_drvdata(pdev, drv_data);
@@ -1408,15 +1368,15 @@ static int __init bfin_spi_probe(struct platform_device *pdev)
goto out_error_queue_alloc;
}
- dev_info(dev, "%s, Version %s, regs_base@%p, dma channel@%d\n",
- DRV_DESC, DRV_VERSION, drv_data->regs_base,
+ dev_info(dev, "%s, Version %s, regs@%p, dma channel@%d\n",
+ DRV_DESC, DRV_VERSION, drv_data->regs,
drv_data->dma_channel);
return status;
out_error_queue_alloc:
bfin_spi_destroy_queue(drv_data);
out_error_free_io:
- iounmap((void *) drv_data->regs_base);
+ iounmap(drv_data->regs);
out_error_ioremap:
out_error_get_res:
spi_master_put(master);
@@ -1473,14 +1433,14 @@ static int bfin_spi_suspend(struct platform_device *pdev, pm_message_t state)
if (status != 0)
return status;
- drv_data->ctrl_reg = read_CTRL(drv_data);
- drv_data->flag_reg = read_FLAG(drv_data);
+ drv_data->ctrl_reg = bfin_read(&drv_data->regs->ctl);
+ drv_data->flag_reg = bfin_read(&drv_data->regs->flg);
/*
* reset SPI_CTL and SPI_FLG registers
*/
- write_CTRL(drv_data, BIT_CTL_CPHA | BIT_CTL_MASTER);
- write_FLAG(drv_data, 0xFF00);
+ bfin_write(&drv_data->regs->ctl, BIT_CTL_CPHA | BIT_CTL_MASTER);
+ bfin_write(&drv_data->regs->flg, 0xFF00);
return 0;
}
@@ -1490,8 +1450,8 @@ static int bfin_spi_resume(struct platform_device *pdev)
struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev);
int status = 0;
- write_CTRL(drv_data, drv_data->ctrl_reg);
- write_FLAG(drv_data, drv_data->flag_reg);
+ bfin_write(&drv_data->regs->ctl, drv_data->ctrl_reg);
+ bfin_write(&drv_data->regs->flg, drv_data->flag_reg);
/* Start the queue running */
status = bfin_spi_start_queue(drv_data);
diff --git a/drivers/spi/spi_bitbang_txrx.h b/drivers/spi/spi-bitbang-txrx.h
index c16bf853c3eb..c16bf853c3eb 100644
--- a/drivers/spi/spi_bitbang_txrx.h
+++ b/drivers/spi/spi-bitbang-txrx.h
diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi-bitbang.c
index 14a63f6010d1..02d57fbba295 100644
--- a/drivers/spi/spi_bitbang.c
+++ b/drivers/spi/spi-bitbang.c
@@ -1,5 +1,5 @@
/*
- * spi_bitbang.c - polling/bitbanging SPI master controller driver utilities
+ * polling/bitbanging SPI master controller driver utilities
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -68,7 +68,7 @@ static unsigned bitbang_txrx_8(
unsigned ns,
struct spi_transfer *t
) {
- unsigned bits = spi->bits_per_word;
+ unsigned bits = t->bits_per_word ? : spi->bits_per_word;
unsigned count = t->len;
const u8 *tx = t->tx_buf;
u8 *rx = t->rx_buf;
@@ -94,7 +94,7 @@ static unsigned bitbang_txrx_16(
unsigned ns,
struct spi_transfer *t
) {
- unsigned bits = spi->bits_per_word;
+ unsigned bits = t->bits_per_word ? : spi->bits_per_word;
unsigned count = t->len;
const u16 *tx = t->tx_buf;
u16 *rx = t->rx_buf;
@@ -120,7 +120,7 @@ static unsigned bitbang_txrx_32(
unsigned ns,
struct spi_transfer *t
) {
- unsigned bits = spi->bits_per_word;
+ unsigned bits = t->bits_per_word ? : spi->bits_per_word;
unsigned count = t->len;
const u32 *tx = t->tx_buf;
u32 *rx = t->rx_buf;
diff --git a/drivers/spi/spi_butterfly.c b/drivers/spi/spi-butterfly.c
index 0d4ceba3b590..9f907ec52def 100644
--- a/drivers/spi/spi_butterfly.c
+++ b/drivers/spi/spi-butterfly.c
@@ -1,5 +1,5 @@
/*
- * spi_butterfly.c - parport-to-butterfly adapter
+ * parport-to-butterfly adapter
*
* Copyright (C) 2005 David Brownell
*
@@ -149,7 +149,7 @@ static void butterfly_chipselect(struct spi_device *spi, int value)
#define spidelay(X) do{}while(0)
//#define spidelay ndelay
-#include "spi_bitbang_txrx.h"
+#include "spi-bitbang-txrx.h"
static u32
butterfly_txrx_word_mode0(struct spi_device *spi,
diff --git a/drivers/spi/coldfire_qspi.c b/drivers/spi/spi-coldfire-qspi.c
index ae2cd1c1fda8..ae2cd1c1fda8 100644
--- a/drivers/spi/coldfire_qspi.c
+++ b/drivers/spi/spi-coldfire-qspi.c
diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/spi-davinci.c
index 1f0ed8005c91..1f0ed8005c91 100644
--- a/drivers/spi/davinci_spi.c
+++ b/drivers/spi/spi-davinci.c
diff --git a/drivers/spi/dw_spi_mid.c b/drivers/spi/spi-dw-mid.c
index 489178243d88..130e55537db6 100644
--- a/drivers/spi/dw_spi_mid.c
+++ b/drivers/spi/spi-dw-mid.c
@@ -1,5 +1,5 @@
/*
- * dw_spi_mid.c - special handling for DW core on Intel MID platform
+ * Special handling for DW core on Intel MID platform
*
* Copyright (c) 2009, Intel Corporation.
*
@@ -23,7 +23,7 @@
#include <linux/slab.h>
#include <linux/spi/spi.h>
-#include "dw_spi.h"
+#include "spi-dw.h"
#ifdef CONFIG_SPI_DW_MID_DMA
#include <linux/intel_mid_dma.h>
diff --git a/drivers/spi/dw_spi_mmio.c b/drivers/spi/spi-dw-mmio.c
index e0e813dad150..34eb66501dbf 100644
--- a/drivers/spi/dw_spi_mmio.c
+++ b/drivers/spi/spi-dw-mmio.c
@@ -1,5 +1,5 @@
/*
- * dw_spi_mmio.c - Memory-mapped interface driver for DW SPI Core
+ * Memory-mapped interface driver for DW SPI Core
*
* Copyright (c) 2010, Octasic semiconductor.
*
@@ -16,7 +16,7 @@
#include <linux/spi/spi.h>
#include <linux/scatterlist.h>
-#include "dw_spi.h"
+#include "spi-dw.h"
#define DRIVER_NAME "dw_spi_mmio"
diff --git a/drivers/spi/dw_spi_pci.c b/drivers/spi/spi-dw-pci.c
index ad260aa5e526..c5f37f03ac8b 100644
--- a/drivers/spi/dw_spi_pci.c
+++ b/drivers/spi/spi-dw-pci.c
@@ -1,5 +1,5 @@
/*
- * dw_spi_pci.c - PCI interface driver for DW SPI Core
+ * PCI interface driver for DW SPI Core
*
* Copyright (c) 2009, Intel Corporation.
*
@@ -22,7 +22,7 @@
#include <linux/slab.h>
#include <linux/spi/spi.h>
-#include "dw_spi.h"
+#include "spi-dw.h"
#define DRIVER_NAME "dw_spi_pci"
diff --git a/drivers/spi/dw_spi.c b/drivers/spi/spi-dw.c
index 919fa9d9e16b..857cd30b44bb 100644
--- a/drivers/spi/dw_spi.c
+++ b/drivers/spi/spi-dw.c
@@ -1,5 +1,5 @@
/*
- * dw_spi.c - Designware SPI core controller driver (refer pxa2xx_spi.c)
+ * Designware SPI core controller driver (refer pxa2xx_spi.c)
*
* Copyright (c) 2009, Intel Corporation.
*
@@ -24,7 +24,7 @@
#include <linux/slab.h>
#include <linux/spi/spi.h>
-#include "dw_spi.h"
+#include "spi-dw.h"
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
@@ -818,9 +818,11 @@ int __devinit dw_spi_add_host(struct dw_spi *dws)
dws->prev_chip = NULL;
dws->dma_inited = 0;
dws->dma_addr = (dma_addr_t)(dws->paddr + 0x60);
+ snprintf(dws->name, sizeof(dws->name), "dw_spi%d",
+ dws->bus_num);
ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED,
- "dw_spi", dws);
+ dws->name, dws);
if (ret < 0) {
dev_err(&master->dev, "can not get IRQ\n");
goto err_free_master;
diff --git a/drivers/spi/dw_spi.h b/drivers/spi/spi-dw.h
index 7a5e78d2a5cb..8b7b07bf6c3f 100644
--- a/drivers/spi/dw_spi.h
+++ b/drivers/spi/spi-dw.h
@@ -96,6 +96,7 @@ struct dw_spi {
struct spi_device *cur_dev;
struct device *parent_dev;
enum dw_ssi_type type;
+ char name[16];
void __iomem *regs;
unsigned long paddr;
diff --git a/drivers/spi/ep93xx_spi.c b/drivers/spi/spi-ep93xx.c
index d3570071e98f..1cf645479bfe 100644
--- a/drivers/spi/ep93xx_spi.c
+++ b/drivers/spi/spi-ep93xx.c
@@ -1,7 +1,7 @@
/*
* Driver for Cirrus Logic EP93xx SPI controller.
*
- * Copyright (c) 2010 Mika Westerberg
+ * Copyright (C) 2010-2011 Mika Westerberg
*
* Explicit FIFO handling code was inspired by amba-pl022 driver.
*
@@ -21,13 +21,16 @@
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/dmaengine.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
+#include <linux/scatterlist.h>
#include <linux/spi/spi.h>
+#include <mach/dma.h>
#include <mach/ep93xx_spi.h>
#define SSPCR0 0x0000
@@ -71,6 +74,7 @@
* @pdev: pointer to platform device
* @clk: clock for the controller
* @regs_base: pointer to ioremap()'d registers
+ * @sspdr_phys: physical address of the SSPDR register
* @irq: IRQ number used by the driver
* @min_rate: minimum clock rate (in Hz) supported by the controller
* @max_rate: maximum clock rate (in Hz) supported by the controller
@@ -84,6 +88,14 @@
* @rx: current byte in transfer to receive
* @fifo_level: how full is FIFO (%0..%SPI_FIFO_SIZE - %1). Receiving one
* frame decreases this level and sending one frame increases it.
+ * @dma_rx: RX DMA channel
+ * @dma_tx: TX DMA channel
+ * @dma_rx_data: RX parameters passed to the DMA engine
+ * @dma_tx_data: TX parameters passed to the DMA engine
+ * @rx_sgt: sg table for RX transfers
+ * @tx_sgt: sg table for TX transfers
+ * @zeropage: dummy page used as RX buffer when only TX buffer is passed in by
+ * the client
*
* This structure holds EP93xx SPI controller specific information. When
* @running is %true, driver accepts transfer requests from protocol drivers.
@@ -100,6 +112,7 @@ struct ep93xx_spi {
const struct platform_device *pdev;
struct clk *clk;
void __iomem *regs_base;
+ unsigned long sspdr_phys;
int irq;
unsigned long min_rate;
unsigned long max_rate;
@@ -112,6 +125,13 @@ struct ep93xx_spi {
size_t tx;
size_t rx;
size_t fifo_level;
+ struct dma_chan *dma_rx;
+ struct dma_chan *dma_tx;
+ struct ep93xx_dma_data dma_rx_data;
+ struct ep93xx_dma_data dma_tx_data;
+ struct sg_table rx_sgt;
+ struct sg_table tx_sgt;
+ void *zeropage;
};
/**
@@ -496,14 +516,195 @@ static int ep93xx_spi_read_write(struct ep93xx_spi *espi)
espi->fifo_level++;
}
- if (espi->rx == t->len) {
- msg->actual_length += t->len;
+ if (espi->rx == t->len)
return 0;
- }
return -EINPROGRESS;
}
+static void ep93xx_spi_pio_transfer(struct ep93xx_spi *espi)
+{
+ /*
+ * Now everything is set up for the current transfer. We prime the TX
+ * FIFO, enable interrupts, and wait for the transfer to complete.
+ */
+ if (ep93xx_spi_read_write(espi)) {
+ ep93xx_spi_enable_interrupts(espi);
+ wait_for_completion(&espi->wait);
+ }
+}
+
+/**
+ * ep93xx_spi_dma_prepare() - prepares a DMA transfer
+ * @espi: ep93xx SPI controller struct
+ * @dir: DMA transfer direction
+ *
+ * Function configures the DMA, maps the buffer and prepares the DMA
+ * descriptor. Returns a valid DMA descriptor in case of success and ERR_PTR
+ * in case of failure.
+ */
+static struct dma_async_tx_descriptor *
+ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_data_direction dir)
+{
+ struct spi_transfer *t = espi->current_msg->state;
+ struct dma_async_tx_descriptor *txd;
+ enum dma_slave_buswidth buswidth;
+ struct dma_slave_config conf;
+ struct scatterlist *sg;
+ struct sg_table *sgt;
+ struct dma_chan *chan;
+ const void *buf, *pbuf;
+ size_t len = t->len;
+ int i, ret, nents;
+
+ if (bits_per_word(espi) > 8)
+ buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ else
+ buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
+
+ memset(&conf, 0, sizeof(conf));
+ conf.direction = dir;
+
+ if (dir == DMA_FROM_DEVICE) {
+ chan = espi->dma_rx;
+ buf = t->rx_buf;
+ sgt = &espi->rx_sgt;
+
+ conf.src_addr = espi->sspdr_phys;
+ conf.src_addr_width = buswidth;
+ } else {
+ chan = espi->dma_tx;
+ buf = t->tx_buf;
+ sgt = &espi->tx_sgt;
+
+ conf.dst_addr = espi->sspdr_phys;
+ conf.dst_addr_width = buswidth;
+ }
+
+ ret = dmaengine_slave_config(chan, &conf);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /*
+ * We need to split the transfer into PAGE_SIZE'd chunks. This is
+ * because we are using @espi->zeropage to provide a zero RX buffer
+ * for the TX transfers and we have only allocated one page for that.
+ *
+ * For performance reasons we allocate a new sg_table only when
+ * needed. Otherwise we will re-use the current one. Eventually the
+ * last sg_table is released in ep93xx_spi_release_dma().
+ */
+
+ nents = DIV_ROUND_UP(len, PAGE_SIZE);
+ if (nents != sgt->nents) {
+ sg_free_table(sgt);
+
+ ret = sg_alloc_table(sgt, nents, GFP_KERNEL);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ pbuf = buf;
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ size_t bytes = min_t(size_t, len, PAGE_SIZE);
+
+ if (buf) {
+ sg_set_page(sg, virt_to_page(pbuf), bytes,
+ offset_in_page(pbuf));
+ } else {
+ sg_set_page(sg, virt_to_page(espi->zeropage),
+ bytes, 0);
+ }
+
+ pbuf += bytes;
+ len -= bytes;
+ }
+
+ if (WARN_ON(len)) {
+ dev_warn(&espi->pdev->dev, "len = %d expected 0!", len);
+ return ERR_PTR(-EINVAL);
+ }
+
+ nents = dma_map_sg(chan->device->dev, sgt->sgl, sgt->nents, dir);
+ if (!nents)
+ return ERR_PTR(-ENOMEM);
+
+ txd = chan->device->device_prep_slave_sg(chan, sgt->sgl, nents,
+ dir, DMA_CTRL_ACK);
+ if (!txd) {
+ dma_unmap_sg(chan->device->dev, sgt->sgl, sgt->nents, dir);
+ return ERR_PTR(-ENOMEM);
+ }
+ return txd;
+}
+
+/**
+ * ep93xx_spi_dma_finish() - finishes with a DMA transfer
+ * @espi: ep93xx SPI controller struct
+ * @dir: DMA transfer direction
+ *
+ * Function finishes with the DMA transfer. After this, the DMA buffer is
+ * unmapped.
+ */
+static void ep93xx_spi_dma_finish(struct ep93xx_spi *espi,
+ enum dma_data_direction dir)
+{
+ struct dma_chan *chan;
+ struct sg_table *sgt;
+
+ if (dir == DMA_FROM_DEVICE) {
+ chan = espi->dma_rx;
+ sgt = &espi->rx_sgt;
+ } else {
+ chan = espi->dma_tx;
+ sgt = &espi->tx_sgt;
+ }
+
+ dma_unmap_sg(chan->device->dev, sgt->sgl, sgt->nents, dir);
+}
+
+static void ep93xx_spi_dma_callback(void *callback_param)
+{
+ complete(callback_param);
+}
+
+static void ep93xx_spi_dma_transfer(struct ep93xx_spi *espi)
+{
+ struct spi_message *msg = espi->current_msg;
+ struct dma_async_tx_descriptor *rxd, *txd;
+
+ rxd = ep93xx_spi_dma_prepare(espi, DMA_FROM_DEVICE);
+ if (IS_ERR(rxd)) {
+ dev_err(&espi->pdev->dev, "DMA RX failed: %ld\n", PTR_ERR(rxd));
+ msg->status = PTR_ERR(rxd);
+ return;
+ }
+
+ txd = ep93xx_spi_dma_prepare(espi, DMA_TO_DEVICE);
+ if (IS_ERR(txd)) {
+ ep93xx_spi_dma_finish(espi, DMA_FROM_DEVICE);
+ dev_err(&espi->pdev->dev, "DMA TX failed: %ld\n", PTR_ERR(rxd));
+ msg->status = PTR_ERR(txd);
+ return;
+ }
+
+ /* We are ready when RX is done */
+ rxd->callback = ep93xx_spi_dma_callback;
+ rxd->callback_param = &espi->wait;
+
+ /* Now submit both descriptors and wait while they finish */
+ dmaengine_submit(rxd);
+ dmaengine_submit(txd);
+
+ dma_async_issue_pending(espi->dma_rx);
+ dma_async_issue_pending(espi->dma_tx);
+
+ wait_for_completion(&espi->wait);
+
+ ep93xx_spi_dma_finish(espi, DMA_TO_DEVICE);
+ ep93xx_spi_dma_finish(espi, DMA_FROM_DEVICE);
+}
+
/**
* ep93xx_spi_process_transfer() - processes one SPI transfer
* @espi: ep93xx SPI controller struct
@@ -556,13 +757,14 @@ static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi,
espi->tx = 0;
/*
- * Now everything is set up for the current transfer. We prime the TX
- * FIFO, enable interrupts, and wait for the transfer to complete.
+ * There is no point of setting up DMA for the transfers which will
+ * fit into the FIFO and can be transferred with a single interrupt.
+ * So in these cases we will be using PIO and don't bother for DMA.
*/
- if (ep93xx_spi_read_write(espi)) {
- ep93xx_spi_enable_interrupts(espi);
- wait_for_completion(&espi->wait);
- }
+ if (espi->dma_rx && t->len > SPI_FIFO_SIZE)
+ ep93xx_spi_dma_transfer(espi);
+ else
+ ep93xx_spi_pio_transfer(espi);
/*
* In case of error during transmit, we bail out from processing
@@ -571,6 +773,8 @@ static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi,
if (msg->status)
return;
+ msg->actual_length += t->len;
+
/*
* After this transfer is finished, perform any possible
* post-transfer actions requested by the protocol driver.
@@ -752,6 +956,75 @@ static irqreturn_t ep93xx_spi_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static bool ep93xx_spi_dma_filter(struct dma_chan *chan, void *filter_param)
+{
+ if (ep93xx_dma_chan_is_m2p(chan))
+ return false;
+
+ chan->private = filter_param;
+ return true;
+}
+
+static int ep93xx_spi_setup_dma(struct ep93xx_spi *espi)
+{
+ dma_cap_mask_t mask;
+ int ret;
+
+ espi->zeropage = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!espi->zeropage)
+ return -ENOMEM;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ espi->dma_rx_data.port = EP93XX_DMA_SSP;
+ espi->dma_rx_data.direction = DMA_FROM_DEVICE;
+ espi->dma_rx_data.name = "ep93xx-spi-rx";
+
+ espi->dma_rx = dma_request_channel(mask, ep93xx_spi_dma_filter,
+ &espi->dma_rx_data);
+ if (!espi->dma_rx) {
+ ret = -ENODEV;
+ goto fail_free_page;
+ }
+
+ espi->dma_tx_data.port = EP93XX_DMA_SSP;
+ espi->dma_tx_data.direction = DMA_TO_DEVICE;
+ espi->dma_tx_data.name = "ep93xx-spi-tx";
+
+ espi->dma_tx = dma_request_channel(mask, ep93xx_spi_dma_filter,
+ &espi->dma_tx_data);
+ if (!espi->dma_tx) {
+ ret = -ENODEV;
+ goto fail_release_rx;
+ }
+
+ return 0;
+
+fail_release_rx:
+ dma_release_channel(espi->dma_rx);
+ espi->dma_rx = NULL;
+fail_free_page:
+ free_page((unsigned long)espi->zeropage);
+
+ return ret;
+}
+
+static void ep93xx_spi_release_dma(struct ep93xx_spi *espi)
+{
+ if (espi->dma_rx) {
+ dma_release_channel(espi->dma_rx);
+ sg_free_table(&espi->rx_sgt);
+ }
+ if (espi->dma_tx) {
+ dma_release_channel(espi->dma_tx);
+ sg_free_table(&espi->tx_sgt);
+ }
+
+ if (espi->zeropage)
+ free_page((unsigned long)espi->zeropage);
+}
+
static int __init ep93xx_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
@@ -818,6 +1091,7 @@ static int __init ep93xx_spi_probe(struct platform_device *pdev)
goto fail_put_clock;
}
+ espi->sspdr_phys = res->start + SSPDR;
espi->regs_base = ioremap(res->start, resource_size(res));
if (!espi->regs_base) {
dev_err(&pdev->dev, "failed to map resources\n");
@@ -832,10 +1106,13 @@ static int __init ep93xx_spi_probe(struct platform_device *pdev)
goto fail_unmap_regs;
}
+ if (info->use_dma && ep93xx_spi_setup_dma(espi))
+ dev_warn(&pdev->dev, "DMA setup failed. Falling back to PIO\n");
+
espi->wq = create_singlethread_workqueue("ep93xx_spid");
if (!espi->wq) {
dev_err(&pdev->dev, "unable to create workqueue\n");
- goto fail_free_irq;
+ goto fail_free_dma;
}
INIT_WORK(&espi->msg_work, ep93xx_spi_work);
INIT_LIST_HEAD(&espi->msg_queue);
@@ -857,7 +1134,8 @@ static int __init ep93xx_spi_probe(struct platform_device *pdev)
fail_free_queue:
destroy_workqueue(espi->wq);
-fail_free_irq:
+fail_free_dma:
+ ep93xx_spi_release_dma(espi);
free_irq(espi->irq, espi);
fail_unmap_regs:
iounmap(espi->regs_base);
@@ -901,6 +1179,7 @@ static int __exit ep93xx_spi_remove(struct platform_device *pdev)
}
spin_unlock_irq(&espi->lock);
+ ep93xx_spi_release_dma(espi);
free_irq(espi->irq, espi);
iounmap(espi->regs_base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/spi/spi_fsl_espi.c b/drivers/spi/spi-fsl-espi.c
index 496f895a0024..54e499d5f92c 100644
--- a/drivers/spi/spi_fsl_espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -22,7 +22,7 @@
#include <linux/err.h>
#include <sysdev/fsl_soc.h>
-#include "spi_fsl_lib.h"
+#include "spi-fsl-lib.h"
/* eSPI Controller registers */
struct fsl_espi_reg {
diff --git a/drivers/spi/spi_fsl_lib.c b/drivers/spi/spi-fsl-lib.c
index ff59f42ae990..2674fad7f68a 100644
--- a/drivers/spi/spi_fsl_lib.c
+++ b/drivers/spi/spi-fsl-lib.c
@@ -25,7 +25,7 @@
#include <linux/of_spi.h>
#include <sysdev/fsl_soc.h>
-#include "spi_fsl_lib.h"
+#include "spi-fsl-lib.h"
#define MPC8XXX_SPI_RX_BUF(type) \
void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \
diff --git a/drivers/spi/spi_fsl_lib.h b/drivers/spi/spi-fsl-lib.h
index cbe881b9ea76..cbe881b9ea76 100644
--- a/drivers/spi/spi_fsl_lib.h
+++ b/drivers/spi/spi-fsl-lib.h
diff --git a/drivers/spi/spi_fsl_spi.c b/drivers/spi/spi-fsl-spi.c
index 7963c9b49566..d2407558773f 100644
--- a/drivers/spi/spi_fsl_spi.c
+++ b/drivers/spi/spi-fsl-spi.c
@@ -37,7 +37,7 @@
#include <asm/cpm.h>
#include <asm/qe.h>
-#include "spi_fsl_lib.h"
+#include "spi-fsl-lib.h"
/* CPM1 and CPM2 are mutually exclusive. */
#ifdef CONFIG_CPM1
@@ -684,7 +684,7 @@ static unsigned long fsl_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
struct device_node *np = dev->of_node;
const u32 *iprop;
int size;
- unsigned long spi_base_ofs;
+ void __iomem *spi_base;
unsigned long pram_ofs = -ENOMEM;
/* Can't use of_address_to_resource(), QE muram isn't at 0. */
@@ -702,33 +702,27 @@ static unsigned long fsl_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
return pram_ofs;
}
- /* CPM1 and CPM2 pram must be at a fixed addr. */
- if (!iprop || size != sizeof(*iprop) * 4)
- return -ENOMEM;
-
- spi_base_ofs = cpm_muram_alloc_fixed(iprop[2], 2);
- if (IS_ERR_VALUE(spi_base_ofs))
- return -ENOMEM;
+ spi_base = of_iomap(np, 1);
+ if (spi_base == NULL)
+ return -EINVAL;
if (mspi->flags & SPI_CPM2) {
pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
- if (!IS_ERR_VALUE(pram_ofs)) {
- u16 __iomem *spi_base = cpm_muram_addr(spi_base_ofs);
-
- out_be16(spi_base, pram_ofs);
- }
+ out_be16(spi_base, pram_ofs);
} else {
- struct spi_pram __iomem *pram = cpm_muram_addr(spi_base_ofs);
+ struct spi_pram __iomem *pram = spi_base;
u16 rpbase = in_be16(&pram->rpbase);
/* Microcode relocation patch applied? */
if (rpbase)
pram_ofs = rpbase;
- else
- return spi_base_ofs;
+ else {
+ pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
+ out_be16(spi_base, pram_ofs);
+ }
}
- cpm_muram_free(spi_base_ofs);
+ iounmap(spi_base);
return pram_ofs;
}
diff --git a/drivers/spi/spi_gpio.c b/drivers/spi/spi-gpio.c
index 63e51b011d50..0e88ab745490 100644
--- a/drivers/spi/spi_gpio.c
+++ b/drivers/spi/spi-gpio.c
@@ -1,5 +1,5 @@
/*
- * spi_gpio.c - SPI master driver using generic bitbanged GPIO
+ * SPI master driver using generic bitbanged GPIO
*
* Copyright (C) 2006,2008 David Brownell
*
@@ -69,7 +69,7 @@ struct spi_gpio {
* #define SPI_MOSI_GPIO 120
* #define SPI_SCK_GPIO 121
* #define SPI_N_CHIPSEL 4
- * #include "spi_gpio.c"
+ * #include "spi-gpio.c"
*/
#ifndef DRIVER_NAME
@@ -127,7 +127,7 @@ static inline int getmiso(const struct spi_device *spi)
*/
#define spidelay(nsecs) do {} while (0)
-#include "spi_bitbang_txrx.h"
+#include "spi-bitbang-txrx.h"
/*
* These functions can leverage inline expansion of GPIO calls to shrink
diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi-imx.c
index 69d6dba67c19..8ac6542aedcd 100644
--- a/drivers/spi/spi_imx.c
+++ b/drivers/spi/spi-imx.c
@@ -34,6 +34,9 @@
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/types.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <mach/spi.h>
@@ -45,9 +48,6 @@
#define MXC_CSPIINT 0x0c
#define MXC_RESET 0x1c
-#define MX3_CSPISTAT 0x14
-#define MX3_CSPISTAT_RR (1 << 3)
-
/* generic defines to abstract from the different register layouts */
#define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */
#define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */
@@ -60,12 +60,12 @@ struct spi_imx_config {
};
enum spi_imx_devtype {
- SPI_IMX_VER_IMX1,
- SPI_IMX_VER_0_0,
- SPI_IMX_VER_0_4,
- SPI_IMX_VER_0_5,
- SPI_IMX_VER_0_7,
- SPI_IMX_VER_2_3,
+ IMX1_CSPI,
+ IMX21_CSPI,
+ IMX27_CSPI,
+ IMX31_CSPI,
+ IMX35_CSPI, /* CSPI on all i.mx except above */
+ IMX51_ECSPI, /* ECSPI on i.mx51 and later */
};
struct spi_imx_data;
@@ -76,7 +76,7 @@ struct spi_imx_devtype_data {
void (*trigger)(struct spi_imx_data *);
int (*rx_available)(struct spi_imx_data *);
void (*reset)(struct spi_imx_data *);
- unsigned int fifosize;
+ enum spi_imx_devtype devtype;
};
struct spi_imx_data {
@@ -87,7 +87,6 @@ struct spi_imx_data {
int irq;
struct clk *clk;
unsigned long spi_clk;
- int *chipselect;
unsigned int count;
void (*tx)(struct spi_imx_data *);
@@ -96,9 +95,25 @@ struct spi_imx_data {
const void *tx_buf;
unsigned int txfifo; /* number of words pushed in tx FIFO */
- struct spi_imx_devtype_data devtype_data;
+ struct spi_imx_devtype_data *devtype_data;
+ int chipselect[0];
};
+static inline int is_imx27_cspi(struct spi_imx_data *d)
+{
+ return d->devtype_data->devtype == IMX27_CSPI;
+}
+
+static inline int is_imx35_cspi(struct spi_imx_data *d)
+{
+ return d->devtype_data->devtype == IMX35_CSPI;
+}
+
+static inline unsigned spi_imx_get_fifosize(struct spi_imx_data *d)
+{
+ return (d->devtype_data->devtype == IMX51_ECSPI) ? 64 : 8;
+}
+
#define MXC_SPI_BUF_RX(type) \
static void spi_imx_buf_rx_##type(struct spi_imx_data *spi_imx) \
{ \
@@ -140,14 +155,9 @@ static int mxc_clkdivs[] = {0, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192,
/* MX21, MX27 */
static unsigned int spi_imx_clkdiv_1(unsigned int fin,
- unsigned int fspi)
+ unsigned int fspi, unsigned int max)
{
- int i, max;
-
- if (cpu_is_mx21())
- max = 18;
- else
- max = 16;
+ int i;
for (i = 2; i < max; i++)
if (fspi * mxc_clkdivs[i] >= fin)
@@ -171,30 +181,30 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,
return 7;
}
-#define SPI_IMX2_3_CTRL 0x08
-#define SPI_IMX2_3_CTRL_ENABLE (1 << 0)
-#define SPI_IMX2_3_CTRL_XCH (1 << 2)
-#define SPI_IMX2_3_CTRL_MODE_MASK (0xf << 4)
-#define SPI_IMX2_3_CTRL_POSTDIV_OFFSET 8
-#define SPI_IMX2_3_CTRL_PREDIV_OFFSET 12
-#define SPI_IMX2_3_CTRL_CS(cs) ((cs) << 18)
-#define SPI_IMX2_3_CTRL_BL_OFFSET 20
+#define MX51_ECSPI_CTRL 0x08
+#define MX51_ECSPI_CTRL_ENABLE (1 << 0)
+#define MX51_ECSPI_CTRL_XCH (1 << 2)
+#define MX51_ECSPI_CTRL_MODE_MASK (0xf << 4)
+#define MX51_ECSPI_CTRL_POSTDIV_OFFSET 8
+#define MX51_ECSPI_CTRL_PREDIV_OFFSET 12
+#define MX51_ECSPI_CTRL_CS(cs) ((cs) << 18)
+#define MX51_ECSPI_CTRL_BL_OFFSET 20
-#define SPI_IMX2_3_CONFIG 0x0c
-#define SPI_IMX2_3_CONFIG_SCLKPHA(cs) (1 << ((cs) + 0))
-#define SPI_IMX2_3_CONFIG_SCLKPOL(cs) (1 << ((cs) + 4))
-#define SPI_IMX2_3_CONFIG_SBBCTRL(cs) (1 << ((cs) + 8))
-#define SPI_IMX2_3_CONFIG_SSBPOL(cs) (1 << ((cs) + 12))
+#define MX51_ECSPI_CONFIG 0x0c
+#define MX51_ECSPI_CONFIG_SCLKPHA(cs) (1 << ((cs) + 0))
+#define MX51_ECSPI_CONFIG_SCLKPOL(cs) (1 << ((cs) + 4))
+#define MX51_ECSPI_CONFIG_SBBCTRL(cs) (1 << ((cs) + 8))
+#define MX51_ECSPI_CONFIG_SSBPOL(cs) (1 << ((cs) + 12))
-#define SPI_IMX2_3_INT 0x10
-#define SPI_IMX2_3_INT_TEEN (1 << 0)
-#define SPI_IMX2_3_INT_RREN (1 << 3)
+#define MX51_ECSPI_INT 0x10
+#define MX51_ECSPI_INT_TEEN (1 << 0)
+#define MX51_ECSPI_INT_RREN (1 << 3)
-#define SPI_IMX2_3_STAT 0x18
-#define SPI_IMX2_3_STAT_RR (1 << 3)
+#define MX51_ECSPI_STAT 0x18
+#define MX51_ECSPI_STAT_RR (1 << 3)
/* MX51 eCSPI */
-static unsigned int spi_imx2_3_clkdiv(unsigned int fin, unsigned int fspi)
+static unsigned int mx51_ecspi_clkdiv(unsigned int fin, unsigned int fspi)
{
/*
* there are two 4-bit dividers, the pre-divider divides by
@@ -222,36 +232,36 @@ static unsigned int spi_imx2_3_clkdiv(unsigned int fin, unsigned int fspi)
pr_debug("%s: fin: %u, fspi: %u, post: %u, pre: %u\n",
__func__, fin, fspi, post, pre);
- return (pre << SPI_IMX2_3_CTRL_PREDIV_OFFSET) |
- (post << SPI_IMX2_3_CTRL_POSTDIV_OFFSET);
+ return (pre << MX51_ECSPI_CTRL_PREDIV_OFFSET) |
+ (post << MX51_ECSPI_CTRL_POSTDIV_OFFSET);
}
-static void __maybe_unused spi_imx2_3_intctrl(struct spi_imx_data *spi_imx, int enable)
+static void __maybe_unused mx51_ecspi_intctrl(struct spi_imx_data *spi_imx, int enable)
{
unsigned val = 0;
if (enable & MXC_INT_TE)
- val |= SPI_IMX2_3_INT_TEEN;
+ val |= MX51_ECSPI_INT_TEEN;
if (enable & MXC_INT_RR)
- val |= SPI_IMX2_3_INT_RREN;
+ val |= MX51_ECSPI_INT_RREN;
- writel(val, spi_imx->base + SPI_IMX2_3_INT);
+ writel(val, spi_imx->base + MX51_ECSPI_INT);
}
-static void __maybe_unused spi_imx2_3_trigger(struct spi_imx_data *spi_imx)
+static void __maybe_unused mx51_ecspi_trigger(struct spi_imx_data *spi_imx)
{
u32 reg;
- reg = readl(spi_imx->base + SPI_IMX2_3_CTRL);
- reg |= SPI_IMX2_3_CTRL_XCH;
- writel(reg, spi_imx->base + SPI_IMX2_3_CTRL);
+ reg = readl(spi_imx->base + MX51_ECSPI_CTRL);
+ reg |= MX51_ECSPI_CTRL_XCH;
+ writel(reg, spi_imx->base + MX51_ECSPI_CTRL);
}
-static int __maybe_unused spi_imx2_3_config(struct spi_imx_data *spi_imx,
+static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
struct spi_imx_config *config)
{
- u32 ctrl = SPI_IMX2_3_CTRL_ENABLE, cfg = 0;
+ u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0;
/*
* The hardware seems to have a race condition when changing modes. The
@@ -260,42 +270,42 @@ static int __maybe_unused spi_imx2_3_config(struct spi_imx_data *spi_imx,
* the same time.
* So set master mode for all channels as we do not support slave mode.
*/
- ctrl |= SPI_IMX2_3_CTRL_MODE_MASK;
+ ctrl |= MX51_ECSPI_CTRL_MODE_MASK;
/* set clock speed */
- ctrl |= spi_imx2_3_clkdiv(spi_imx->spi_clk, config->speed_hz);
+ ctrl |= mx51_ecspi_clkdiv(spi_imx->spi_clk, config->speed_hz);
/* set chip select to use */
- ctrl |= SPI_IMX2_3_CTRL_CS(config->cs);
+ ctrl |= MX51_ECSPI_CTRL_CS(config->cs);
- ctrl |= (config->bpw - 1) << SPI_IMX2_3_CTRL_BL_OFFSET;
+ ctrl |= (config->bpw - 1) << MX51_ECSPI_CTRL_BL_OFFSET;
- cfg |= SPI_IMX2_3_CONFIG_SBBCTRL(config->cs);
+ cfg |= MX51_ECSPI_CONFIG_SBBCTRL(config->cs);
if (config->mode & SPI_CPHA)
- cfg |= SPI_IMX2_3_CONFIG_SCLKPHA(config->cs);
+ cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs);
if (config->mode & SPI_CPOL)
- cfg |= SPI_IMX2_3_CONFIG_SCLKPOL(config->cs);
+ cfg |= MX51_ECSPI_CONFIG_SCLKPOL(config->cs);
if (config->mode & SPI_CS_HIGH)
- cfg |= SPI_IMX2_3_CONFIG_SSBPOL(config->cs);
+ cfg |= MX51_ECSPI_CONFIG_SSBPOL(config->cs);
- writel(ctrl, spi_imx->base + SPI_IMX2_3_CTRL);
- writel(cfg, spi_imx->base + SPI_IMX2_3_CONFIG);
+ writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
+ writel(cfg, spi_imx->base + MX51_ECSPI_CONFIG);
return 0;
}
-static int __maybe_unused spi_imx2_3_rx_available(struct spi_imx_data *spi_imx)
+static int __maybe_unused mx51_ecspi_rx_available(struct spi_imx_data *spi_imx)
{
- return readl(spi_imx->base + SPI_IMX2_3_STAT) & SPI_IMX2_3_STAT_RR;
+ return readl(spi_imx->base + MX51_ECSPI_STAT) & MX51_ECSPI_STAT_RR;
}
-static void __maybe_unused spi_imx2_3_reset(struct spi_imx_data *spi_imx)
+static void __maybe_unused mx51_ecspi_reset(struct spi_imx_data *spi_imx)
{
/* drain receive buffer */
- while (spi_imx2_3_rx_available(spi_imx))
+ while (mx51_ecspi_rx_available(spi_imx))
readl(spi_imx->base + MXC_CSPIRXDATA);
}
@@ -343,32 +353,7 @@ static void __maybe_unused mx31_trigger(struct spi_imx_data *spi_imx)
writel(reg, spi_imx->base + MXC_CSPICTRL);
}
-static int __maybe_unused spi_imx0_4_config(struct spi_imx_data *spi_imx,
- struct spi_imx_config *config)
-{
- unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER;
- int cs = spi_imx->chipselect[config->cs];
-
- reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) <<
- MX31_CSPICTRL_DR_SHIFT;
-
- reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT;
-
- if (config->mode & SPI_CPHA)
- reg |= MX31_CSPICTRL_PHA;
- if (config->mode & SPI_CPOL)
- reg |= MX31_CSPICTRL_POL;
- if (config->mode & SPI_CS_HIGH)
- reg |= MX31_CSPICTRL_SSPOL;
- if (cs < 0)
- reg |= (cs + 32) << MX31_CSPICTRL_CS_SHIFT;
-
- writel(reg, spi_imx->base + MXC_CSPICTRL);
-
- return 0;
-}
-
-static int __maybe_unused spi_imx0_7_config(struct spi_imx_data *spi_imx,
+static int __maybe_unused mx31_config(struct spi_imx_data *spi_imx,
struct spi_imx_config *config)
{
unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER;
@@ -377,8 +362,12 @@ static int __maybe_unused spi_imx0_7_config(struct spi_imx_data *spi_imx,
reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) <<
MX31_CSPICTRL_DR_SHIFT;
- reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT;
- reg |= MX31_CSPICTRL_SSCTL;
+ if (is_imx35_cspi(spi_imx)) {
+ reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT;
+ reg |= MX31_CSPICTRL_SSCTL;
+ } else {
+ reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT;
+ }
if (config->mode & SPI_CPHA)
reg |= MX31_CSPICTRL_PHA;
@@ -387,7 +376,9 @@ static int __maybe_unused spi_imx0_7_config(struct spi_imx_data *spi_imx,
if (config->mode & SPI_CS_HIGH)
reg |= MX31_CSPICTRL_SSPOL;
if (cs < 0)
- reg |= (cs + 32) << MX35_CSPICTRL_CS_SHIFT;
+ reg |= (cs + 32) <<
+ (is_imx35_cspi(spi_imx) ? MX35_CSPICTRL_CS_SHIFT :
+ MX31_CSPICTRL_CS_SHIFT);
writel(reg, spi_imx->base + MXC_CSPICTRL);
@@ -399,77 +390,78 @@ static int __maybe_unused mx31_rx_available(struct spi_imx_data *spi_imx)
return readl(spi_imx->base + MX31_CSPISTATUS) & MX31_STATUS_RR;
}
-static void __maybe_unused spi_imx0_4_reset(struct spi_imx_data *spi_imx)
+static void __maybe_unused mx31_reset(struct spi_imx_data *spi_imx)
{
/* drain receive buffer */
- while (readl(spi_imx->base + MX3_CSPISTAT) & MX3_CSPISTAT_RR)
+ while (readl(spi_imx->base + MX31_CSPISTATUS) & MX31_STATUS_RR)
readl(spi_imx->base + MXC_CSPIRXDATA);
}
-#define MX27_INTREG_RR (1 << 4)
-#define MX27_INTREG_TEEN (1 << 9)
-#define MX27_INTREG_RREN (1 << 13)
+#define MX21_INTREG_RR (1 << 4)
+#define MX21_INTREG_TEEN (1 << 9)
+#define MX21_INTREG_RREN (1 << 13)
-#define MX27_CSPICTRL_POL (1 << 5)
-#define MX27_CSPICTRL_PHA (1 << 6)
-#define MX27_CSPICTRL_SSPOL (1 << 8)
-#define MX27_CSPICTRL_XCH (1 << 9)
-#define MX27_CSPICTRL_ENABLE (1 << 10)
-#define MX27_CSPICTRL_MASTER (1 << 11)
-#define MX27_CSPICTRL_DR_SHIFT 14
-#define MX27_CSPICTRL_CS_SHIFT 19
+#define MX21_CSPICTRL_POL (1 << 5)
+#define MX21_CSPICTRL_PHA (1 << 6)
+#define MX21_CSPICTRL_SSPOL (1 << 8)
+#define MX21_CSPICTRL_XCH (1 << 9)
+#define MX21_CSPICTRL_ENABLE (1 << 10)
+#define MX21_CSPICTRL_MASTER (1 << 11)
+#define MX21_CSPICTRL_DR_SHIFT 14
+#define MX21_CSPICTRL_CS_SHIFT 19
-static void __maybe_unused mx27_intctrl(struct spi_imx_data *spi_imx, int enable)
+static void __maybe_unused mx21_intctrl(struct spi_imx_data *spi_imx, int enable)
{
unsigned int val = 0;
if (enable & MXC_INT_TE)
- val |= MX27_INTREG_TEEN;
+ val |= MX21_INTREG_TEEN;
if (enable & MXC_INT_RR)
- val |= MX27_INTREG_RREN;
+ val |= MX21_INTREG_RREN;
writel(val, spi_imx->base + MXC_CSPIINT);
}
-static void __maybe_unused mx27_trigger(struct spi_imx_data *spi_imx)
+static void __maybe_unused mx21_trigger(struct spi_imx_data *spi_imx)
{
unsigned int reg;
reg = readl(spi_imx->base + MXC_CSPICTRL);
- reg |= MX27_CSPICTRL_XCH;
+ reg |= MX21_CSPICTRL_XCH;
writel(reg, spi_imx->base + MXC_CSPICTRL);
}
-static int __maybe_unused mx27_config(struct spi_imx_data *spi_imx,
+static int __maybe_unused mx21_config(struct spi_imx_data *spi_imx,
struct spi_imx_config *config)
{
- unsigned int reg = MX27_CSPICTRL_ENABLE | MX27_CSPICTRL_MASTER;
+ unsigned int reg = MX21_CSPICTRL_ENABLE | MX21_CSPICTRL_MASTER;
int cs = spi_imx->chipselect[config->cs];
+ unsigned int max = is_imx27_cspi(spi_imx) ? 16 : 18;
- reg |= spi_imx_clkdiv_1(spi_imx->spi_clk, config->speed_hz) <<
- MX27_CSPICTRL_DR_SHIFT;
+ reg |= spi_imx_clkdiv_1(spi_imx->spi_clk, config->speed_hz, max) <<
+ MX21_CSPICTRL_DR_SHIFT;
reg |= config->bpw - 1;
if (config->mode & SPI_CPHA)
- reg |= MX27_CSPICTRL_PHA;
+ reg |= MX21_CSPICTRL_PHA;
if (config->mode & SPI_CPOL)
- reg |= MX27_CSPICTRL_POL;
+ reg |= MX21_CSPICTRL_POL;
if (config->mode & SPI_CS_HIGH)
- reg |= MX27_CSPICTRL_SSPOL;
+ reg |= MX21_CSPICTRL_SSPOL;
if (cs < 0)
- reg |= (cs + 32) << MX27_CSPICTRL_CS_SHIFT;
+ reg |= (cs + 32) << MX21_CSPICTRL_CS_SHIFT;
writel(reg, spi_imx->base + MXC_CSPICTRL);
return 0;
}
-static int __maybe_unused mx27_rx_available(struct spi_imx_data *spi_imx)
+static int __maybe_unused mx21_rx_available(struct spi_imx_data *spi_imx)
{
- return readl(spi_imx->base + MXC_CSPIINT) & MX27_INTREG_RR;
+ return readl(spi_imx->base + MXC_CSPIINT) & MX21_INTREG_RR;
}
-static void __maybe_unused spi_imx0_0_reset(struct spi_imx_data *spi_imx)
+static void __maybe_unused mx21_reset(struct spi_imx_data *spi_imx)
{
writel(1, spi_imx->base + MXC_RESET);
}
@@ -535,61 +527,94 @@ static void __maybe_unused mx1_reset(struct spi_imx_data *spi_imx)
writel(1, spi_imx->base + MXC_RESET);
}
-/*
- * These version numbers are taken from the Freescale driver. Unfortunately it
- * doesn't support i.MX1, so this entry doesn't match the scheme. :-(
- */
-static struct spi_imx_devtype_data spi_imx_devtype_data[] __devinitdata = {
-#ifdef CONFIG_SPI_IMX_VER_IMX1
- [SPI_IMX_VER_IMX1] = {
- .intctrl = mx1_intctrl,
- .config = mx1_config,
- .trigger = mx1_trigger,
- .rx_available = mx1_rx_available,
- .reset = mx1_reset,
- .fifosize = 8,
- },
-#endif
-#ifdef CONFIG_SPI_IMX_VER_0_0
- [SPI_IMX_VER_0_0] = {
- .intctrl = mx27_intctrl,
- .config = mx27_config,
- .trigger = mx27_trigger,
- .rx_available = mx27_rx_available,
- .reset = spi_imx0_0_reset,
- .fifosize = 8,
- },
-#endif
-#ifdef CONFIG_SPI_IMX_VER_0_4
- [SPI_IMX_VER_0_4] = {
- .intctrl = mx31_intctrl,
- .config = spi_imx0_4_config,
- .trigger = mx31_trigger,
- .rx_available = mx31_rx_available,
- .reset = spi_imx0_4_reset,
- .fifosize = 8,
- },
-#endif
-#ifdef CONFIG_SPI_IMX_VER_0_7
- [SPI_IMX_VER_0_7] = {
- .intctrl = mx31_intctrl,
- .config = spi_imx0_7_config,
- .trigger = mx31_trigger,
- .rx_available = mx31_rx_available,
- .reset = spi_imx0_4_reset,
- .fifosize = 8,
- },
-#endif
-#ifdef CONFIG_SPI_IMX_VER_2_3
- [SPI_IMX_VER_2_3] = {
- .intctrl = spi_imx2_3_intctrl,
- .config = spi_imx2_3_config,
- .trigger = spi_imx2_3_trigger,
- .rx_available = spi_imx2_3_rx_available,
- .reset = spi_imx2_3_reset,
- .fifosize = 64,
- },
-#endif
+static struct spi_imx_devtype_data imx1_cspi_devtype_data = {
+ .intctrl = mx1_intctrl,
+ .config = mx1_config,
+ .trigger = mx1_trigger,
+ .rx_available = mx1_rx_available,
+ .reset = mx1_reset,
+ .devtype = IMX1_CSPI,
+};
+
+static struct spi_imx_devtype_data imx21_cspi_devtype_data = {
+ .intctrl = mx21_intctrl,
+ .config = mx21_config,
+ .trigger = mx21_trigger,
+ .rx_available = mx21_rx_available,
+ .reset = mx21_reset,
+ .devtype = IMX21_CSPI,
+};
+
+static struct spi_imx_devtype_data imx27_cspi_devtype_data = {
+ /* i.mx27 cspi shares the functions with i.mx21 one */
+ .intctrl = mx21_intctrl,
+ .config = mx21_config,
+ .trigger = mx21_trigger,
+ .rx_available = mx21_rx_available,
+ .reset = mx21_reset,
+ .devtype = IMX27_CSPI,
+};
+
+static struct spi_imx_devtype_data imx31_cspi_devtype_data = {
+ .intctrl = mx31_intctrl,
+ .config = mx31_config,
+ .trigger = mx31_trigger,
+ .rx_available = mx31_rx_available,
+ .reset = mx31_reset,
+ .devtype = IMX31_CSPI,
+};
+
+static struct spi_imx_devtype_data imx35_cspi_devtype_data = {
+ /* i.mx35 and later cspi shares the functions with i.mx31 one */
+ .intctrl = mx31_intctrl,
+ .config = mx31_config,
+ .trigger = mx31_trigger,
+ .rx_available = mx31_rx_available,
+ .reset = mx31_reset,
+ .devtype = IMX35_CSPI,
+};
+
+static struct spi_imx_devtype_data imx51_ecspi_devtype_data = {
+ .intctrl = mx51_ecspi_intctrl,
+ .config = mx51_ecspi_config,
+ .trigger = mx51_ecspi_trigger,
+ .rx_available = mx51_ecspi_rx_available,
+ .reset = mx51_ecspi_reset,
+ .devtype = IMX51_ECSPI,
+};
+
+static struct platform_device_id spi_imx_devtype[] = {
+ {
+ .name = "imx1-cspi",
+ .driver_data = (kernel_ulong_t) &imx1_cspi_devtype_data,
+ }, {
+ .name = "imx21-cspi",
+ .driver_data = (kernel_ulong_t) &imx21_cspi_devtype_data,
+ }, {
+ .name = "imx27-cspi",
+ .driver_data = (kernel_ulong_t) &imx27_cspi_devtype_data,
+ }, {
+ .name = "imx31-cspi",
+ .driver_data = (kernel_ulong_t) &imx31_cspi_devtype_data,
+ }, {
+ .name = "imx35-cspi",
+ .driver_data = (kernel_ulong_t) &imx35_cspi_devtype_data,
+ }, {
+ .name = "imx51-ecspi",
+ .driver_data = (kernel_ulong_t) &imx51_ecspi_devtype_data,
+ }, {
+ /* sentinel */
+ }
+};
+
+static const struct of_device_id spi_imx_dt_ids[] = {
+ { .compatible = "fsl,imx1-cspi", .data = &imx1_cspi_devtype_data, },
+ { .compatible = "fsl,imx21-cspi", .data = &imx21_cspi_devtype_data, },
+ { .compatible = "fsl,imx27-cspi", .data = &imx27_cspi_devtype_data, },
+ { .compatible = "fsl,imx31-cspi", .data = &imx31_cspi_devtype_data, },
+ { .compatible = "fsl,imx35-cspi", .data = &imx35_cspi_devtype_data, },
+ { .compatible = "fsl,imx51-ecspi", .data = &imx51_ecspi_devtype_data, },
+ { /* sentinel */ }
};
static void spi_imx_chipselect(struct spi_device *spi, int is_active)
@@ -607,21 +632,21 @@ static void spi_imx_chipselect(struct spi_device *spi, int is_active)
static void spi_imx_push(struct spi_imx_data *spi_imx)
{
- while (spi_imx->txfifo < spi_imx->devtype_data.fifosize) {
+ while (spi_imx->txfifo < spi_imx_get_fifosize(spi_imx)) {
if (!spi_imx->count)
break;
spi_imx->tx(spi_imx);
spi_imx->txfifo++;
}
- spi_imx->devtype_data.trigger(spi_imx);
+ spi_imx->devtype_data->trigger(spi_imx);
}
static irqreturn_t spi_imx_isr(int irq, void *dev_id)
{
struct spi_imx_data *spi_imx = dev_id;
- while (spi_imx->devtype_data.rx_available(spi_imx)) {
+ while (spi_imx->devtype_data->rx_available(spi_imx)) {
spi_imx->rx(spi_imx);
spi_imx->txfifo--;
}
@@ -635,12 +660,12 @@ static irqreturn_t spi_imx_isr(int irq, void *dev_id)
/* No data left to push, but still waiting for rx data,
* enable receive data available interrupt.
*/
- spi_imx->devtype_data.intctrl(
+ spi_imx->devtype_data->intctrl(
spi_imx, MXC_INT_RR);
return IRQ_HANDLED;
}
- spi_imx->devtype_data.intctrl(spi_imx, 0);
+ spi_imx->devtype_data->intctrl(spi_imx, 0);
complete(&spi_imx->xfer_done);
return IRQ_HANDLED;
@@ -677,7 +702,7 @@ static int spi_imx_setupxfer(struct spi_device *spi,
} else
BUG();
- spi_imx->devtype_data.config(spi_imx, &config);
+ spi_imx->devtype_data->config(spi_imx, &config);
return 0;
}
@@ -696,7 +721,7 @@ static int spi_imx_transfer(struct spi_device *spi,
spi_imx_push(spi_imx);
- spi_imx->devtype_data.intctrl(spi_imx, MXC_INT_TE);
+ spi_imx->devtype_data->intctrl(spi_imx, MXC_INT_TE);
wait_for_completion(&spi_imx->xfer_done);
@@ -723,72 +748,47 @@ static void spi_imx_cleanup(struct spi_device *spi)
{
}
-static struct platform_device_id spi_imx_devtype[] = {
- {
- .name = "imx1-cspi",
- .driver_data = SPI_IMX_VER_IMX1,
- }, {
- .name = "imx21-cspi",
- .driver_data = SPI_IMX_VER_0_0,
- }, {
- .name = "imx25-cspi",
- .driver_data = SPI_IMX_VER_0_7,
- }, {
- .name = "imx27-cspi",
- .driver_data = SPI_IMX_VER_0_0,
- }, {
- .name = "imx31-cspi",
- .driver_data = SPI_IMX_VER_0_4,
- }, {
- .name = "imx35-cspi",
- .driver_data = SPI_IMX_VER_0_7,
- }, {
- .name = "imx51-cspi",
- .driver_data = SPI_IMX_VER_0_7,
- }, {
- .name = "imx51-ecspi",
- .driver_data = SPI_IMX_VER_2_3,
- }, {
- .name = "imx53-cspi",
- .driver_data = SPI_IMX_VER_0_7,
- }, {
- .name = "imx53-ecspi",
- .driver_data = SPI_IMX_VER_2_3,
- }, {
- /* sentinel */
- }
-};
-
static int __devinit spi_imx_probe(struct platform_device *pdev)
{
- struct spi_imx_master *mxc_platform_info;
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *of_id =
+ of_match_device(spi_imx_dt_ids, &pdev->dev);
+ struct spi_imx_master *mxc_platform_info =
+ dev_get_platdata(&pdev->dev);
struct spi_master *master;
struct spi_imx_data *spi_imx;
struct resource *res;
- int i, ret;
+ int i, ret, num_cs;
- mxc_platform_info = dev_get_platdata(&pdev->dev);
- if (!mxc_platform_info) {
+ if (!np && !mxc_platform_info) {
dev_err(&pdev->dev, "can't get the platform data\n");
return -EINVAL;
}
- master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data));
+ ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs);
+ if (ret < 0)
+ num_cs = mxc_platform_info->num_chipselect;
+
+ master = spi_alloc_master(&pdev->dev,
+ sizeof(struct spi_imx_data) + sizeof(int) * num_cs);
if (!master)
return -ENOMEM;
platform_set_drvdata(pdev, master);
master->bus_num = pdev->id;
- master->num_chipselect = mxc_platform_info->num_chipselect;
+ master->num_chipselect = num_cs;
spi_imx = spi_master_get_devdata(master);
spi_imx->bitbang.master = spi_master_get(master);
- spi_imx->chipselect = mxc_platform_info->chipselect;
for (i = 0; i < master->num_chipselect; i++) {
- if (spi_imx->chipselect[i] < 0)
+ int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
+ if (cs_gpio < 0)
+ cs_gpio = mxc_platform_info->chipselect[i];
+ if (cs_gpio < 0)
continue;
+ spi_imx->chipselect[i] = cs_gpio;
ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME);
if (ret) {
while (i > 0) {
@@ -810,8 +810,8 @@ static int __devinit spi_imx_probe(struct platform_device *pdev)
init_completion(&spi_imx->xfer_done);
- spi_imx->devtype_data =
- spi_imx_devtype_data[pdev->id_entry->driver_data];
+ spi_imx->devtype_data = of_id ? of_id->data :
+ (struct spi_imx_devtype_data *) pdev->id_entry->driver_data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
@@ -854,10 +854,11 @@ static int __devinit spi_imx_probe(struct platform_device *pdev)
clk_enable(spi_imx->clk);
spi_imx->spi_clk = clk_get_rate(spi_imx->clk);
- spi_imx->devtype_data.reset(spi_imx);
+ spi_imx->devtype_data->reset(spi_imx);
- spi_imx->devtype_data.intctrl(spi_imx, 0);
+ spi_imx->devtype_data->intctrl(spi_imx, 0);
+ master->dev.of_node = pdev->dev.of_node;
ret = spi_bitbang_start(&spi_imx->bitbang);
if (ret) {
dev_err(&pdev->dev, "bitbang start failed with %d\n", ret);
@@ -920,6 +921,7 @@ static struct platform_driver spi_imx_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .of_match_table = spi_imx_dt_ids,
},
.id_table = spi_imx_devtype,
.probe = spi_imx_probe,
diff --git a/drivers/spi/spi_lm70llp.c b/drivers/spi/spi-lm70llp.c
index 7746a41ab6d6..933eb9d9ddd4 100644
--- a/drivers/spi/spi_lm70llp.c
+++ b/drivers/spi/spi-lm70llp.c
@@ -1,5 +1,5 @@
/*
- * spi_lm70llp.c - driver for LM70EVAL-LLP board for the LM70 sensor
+ * Driver for LM70EVAL-LLP board for the LM70 sensor
*
* Copyright (C) 2006 Kaiwan N Billimoria <kaiwan@designergraphix.com>
*
@@ -174,7 +174,7 @@ static inline int getmiso(struct spi_device *s)
}
/*--------------------------------------------------------------------*/
-#include "spi_bitbang_txrx.h"
+#include "spi-bitbang-txrx.h"
static void lm70_chipselect(struct spi_device *spi, int value)
{
diff --git a/drivers/spi/mpc512x_psc_spi.c b/drivers/spi/spi-mpc512x-psc.c
index 6a5b4238fb6b..6a5b4238fb6b 100644
--- a/drivers/spi/mpc512x_psc_spi.c
+++ b/drivers/spi/spi-mpc512x-psc.c
diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/spi-mpc52xx-psc.c
index e30baf0852ac..e30baf0852ac 100644
--- a/drivers/spi/mpc52xx_psc_spi.c
+++ b/drivers/spi/spi-mpc52xx-psc.c
diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/spi-mpc52xx.c
index 015a974bed72..015a974bed72 100644
--- a/drivers/spi/mpc52xx_spi.c
+++ b/drivers/spi/spi-mpc52xx.c
diff --git a/drivers/spi/spi_nuc900.c b/drivers/spi/spi-nuc900.c
index 3cd15f690f16..c0a6ce81f9c0 100644
--- a/drivers/spi/spi_nuc900.c
+++ b/drivers/spi/spi-nuc900.c
@@ -1,5 +1,4 @@
-/* linux/drivers/spi/spi_nuc900.c
- *
+/*
* Copyright (c) 2009 Nuvoton technology.
* Wan ZongShun <mcuos.com@gmail.com>
*
@@ -7,7 +6,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
-*/
+ */
#include <linux/init.h>
#include <linux/spinlock.h>
diff --git a/drivers/spi/spi_oc_tiny.c b/drivers/spi/spi-oc-tiny.c
index f1bde66cea19..f1bde66cea19 100644
--- a/drivers/spi/spi_oc_tiny.c
+++ b/drivers/spi/spi-oc-tiny.c
diff --git a/drivers/spi/omap_spi_100k.c b/drivers/spi/spi-omap-100k.c
index 9bd1c92ad96e..9bd1c92ad96e 100644
--- a/drivers/spi/omap_spi_100k.c
+++ b/drivers/spi/spi-omap-100k.c
diff --git a/drivers/spi/omap_uwire.c b/drivers/spi/spi-omap-uwire.c
index 160d3266205f..00a8e9d7dbe4 100644
--- a/drivers/spi/omap_uwire.c
+++ b/drivers/spi/spi-omap-uwire.c
@@ -1,5 +1,5 @@
/*
- * omap_uwire.c -- MicroWire interface driver for OMAP
+ * MicroWire interface driver for OMAP
*
* Copyright 2003 MontaVista Software Inc. <source@mvista.com>
*
diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index 969cdd2fe124..fde3a2d4f120 100644
--- a/drivers/spi/omap2_mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -1116,8 +1116,8 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev)
status = -ENODEV;
goto err1;
}
- if (!request_mem_region(r->start, (r->end - r->start) + 1,
- dev_name(&pdev->dev))) {
+ if (!request_mem_region(r->start, resource_size(r),
+ dev_name(&pdev->dev))) {
status = -EBUSY;
goto err1;
}
@@ -1125,7 +1125,7 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev)
r->start += pdata->regs_offset;
r->end += pdata->regs_offset;
mcspi->phys = r->start;
- mcspi->base = ioremap(r->start, r->end - r->start + 1);
+ mcspi->base = ioremap(r->start, resource_size(r));
if (!mcspi->base) {
dev_dbg(&pdev->dev, "can't ioremap MCSPI\n");
status = -ENOMEM;
@@ -1190,7 +1190,7 @@ err4:
err3:
kfree(mcspi->dma_channels);
err2:
- release_mem_region(r->start, (r->end - r->start) + 1);
+ release_mem_region(r->start, resource_size(r));
iounmap(mcspi->base);
err1:
return status;
@@ -1210,7 +1210,7 @@ static int __exit omap2_mcspi_remove(struct platform_device *pdev)
omap2_mcspi_disable_clocks(mcspi);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(r->start, (r->end - r->start) + 1);
+ release_mem_region(r->start, resource_size(r));
base = mcspi->base;
spi_unregister_master(master);
diff --git a/drivers/spi/orion_spi.c b/drivers/spi/spi-orion.c
index 0b677dc041ad..9421a390a5e3 100644
--- a/drivers/spi/orion_spi.c
+++ b/drivers/spi/spi-orion.c
@@ -1,5 +1,5 @@
/*
- * orion_spi.c -- Marvell Orion SPI controller driver
+ * Marvell Orion SPI controller driver
*
* Author: Shadi Ammouri <shadi@marvell.com>
* Copyright (C) 2007-2008 Marvell Ltd.
@@ -489,7 +489,7 @@ static int __init orion_spi_probe(struct platform_device *pdev)
goto out;
}
- if (!request_mem_region(r->start, (r->end - r->start) + 1,
+ if (!request_mem_region(r->start, resource_size(r),
dev_name(&pdev->dev))) {
status = -EBUSY;
goto out;
@@ -511,7 +511,7 @@ static int __init orion_spi_probe(struct platform_device *pdev)
return status;
out_rel_mem:
- release_mem_region(r->start, (r->end - r->start) + 1);
+ release_mem_region(r->start, resource_size(r));
out:
spi_master_put(master);
@@ -531,7 +531,7 @@ static int __exit orion_spi_remove(struct platform_device *pdev)
cancel_work_sync(&spi->work);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(r->start, (r->end - r->start) + 1);
+ release_mem_region(r->start, resource_size(r));
spi_unregister_master(master);
diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/spi-pl022.c
index d18ce9e946d8..eba88c749fb1 100644
--- a/drivers/spi/amba-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -1,6 +1,4 @@
/*
- * drivers/spi/amba-pl022.c
- *
* A driver for the ARM PL022 PrimeCell SSP/SPI bus master.
*
* Copyright (C) 2008-2009 ST-Ericsson AB
@@ -42,6 +40,7 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
+#include <linux/pm_runtime.h>
/*
* This macro is used to define some register default values.
@@ -383,6 +382,8 @@ struct pl022 {
enum ssp_reading read;
enum ssp_writing write;
u32 exp_fifo_level;
+ enum ssp_rx_level_trig rx_lev_trig;
+ enum ssp_tx_level_trig tx_lev_trig;
/* DMA settings */
#ifdef CONFIG_DMA_ENGINE
struct dma_chan *dma_rx_channel;
@@ -517,6 +518,7 @@ static void giveback(struct pl022 *pl022)
clk_disable(pl022->clk);
amba_pclk_disable(pl022->adev);
amba_vcore_disable(pl022->adev);
+ pm_runtime_put(&pl022->adev->dev);
}
/**
@@ -909,12 +911,10 @@ static int configure_dma(struct pl022 *pl022)
struct dma_slave_config rx_conf = {
.src_addr = SSP_DR(pl022->phybase),
.direction = DMA_FROM_DEVICE,
- .src_maxburst = pl022->vendor->fifodepth >> 1,
};
struct dma_slave_config tx_conf = {
.dst_addr = SSP_DR(pl022->phybase),
.direction = DMA_TO_DEVICE,
- .dst_maxburst = pl022->vendor->fifodepth >> 1,
};
unsigned int pages;
int ret;
@@ -928,6 +928,54 @@ static int configure_dma(struct pl022 *pl022)
if (!rxchan || !txchan)
return -ENODEV;
+ /*
+ * If supplied, the DMA burstsize should equal the FIFO trigger level.
+ * Notice that the DMA engine uses one-to-one mapping. Since we can
+ * not trigger on 2 elements this needs explicit mapping rather than
+ * calculation.
+ */
+ switch (pl022->rx_lev_trig) {
+ case SSP_RX_1_OR_MORE_ELEM:
+ rx_conf.src_maxburst = 1;
+ break;
+ case SSP_RX_4_OR_MORE_ELEM:
+ rx_conf.src_maxburst = 4;
+ break;
+ case SSP_RX_8_OR_MORE_ELEM:
+ rx_conf.src_maxburst = 8;
+ break;
+ case SSP_RX_16_OR_MORE_ELEM:
+ rx_conf.src_maxburst = 16;
+ break;
+ case SSP_RX_32_OR_MORE_ELEM:
+ rx_conf.src_maxburst = 32;
+ break;
+ default:
+ rx_conf.src_maxburst = pl022->vendor->fifodepth >> 1;
+ break;
+ }
+
+ switch (pl022->tx_lev_trig) {
+ case SSP_TX_1_OR_MORE_EMPTY_LOC:
+ tx_conf.dst_maxburst = 1;
+ break;
+ case SSP_TX_4_OR_MORE_EMPTY_LOC:
+ tx_conf.dst_maxburst = 4;
+ break;
+ case SSP_TX_8_OR_MORE_EMPTY_LOC:
+ tx_conf.dst_maxburst = 8;
+ break;
+ case SSP_TX_16_OR_MORE_EMPTY_LOC:
+ tx_conf.dst_maxburst = 16;
+ break;
+ case SSP_TX_32_OR_MORE_EMPTY_LOC:
+ tx_conf.dst_maxburst = 32;
+ break;
+ default:
+ tx_conf.dst_maxburst = pl022->vendor->fifodepth >> 1;
+ break;
+ }
+
switch (pl022->read) {
case READING_NULL:
/* Use the same as for writing */
@@ -1496,6 +1544,7 @@ static void pump_messages(struct work_struct *work)
* and core will be disabled when giveback() is called in each method
* (poll/interrupt/DMA)
*/
+ pm_runtime_get_sync(&pl022->adev->dev);
amba_vcore_enable(pl022->adev);
amba_pclk_enable(pl022->adev);
clk_enable(pl022->clk);
@@ -1629,17 +1678,57 @@ static int verify_controller_parameters(struct pl022 *pl022,
"Communication mode is configured incorrectly\n");
return -EINVAL;
}
- if ((chip_info->rx_lev_trig < SSP_RX_1_OR_MORE_ELEM)
- || (chip_info->rx_lev_trig > SSP_RX_32_OR_MORE_ELEM)) {
+ switch (chip_info->rx_lev_trig) {
+ case SSP_RX_1_OR_MORE_ELEM:
+ case SSP_RX_4_OR_MORE_ELEM:
+ case SSP_RX_8_OR_MORE_ELEM:
+ /* These are always OK, all variants can handle this */
+ break;
+ case SSP_RX_16_OR_MORE_ELEM:
+ if (pl022->vendor->fifodepth < 16) {
+ dev_err(&pl022->adev->dev,
+ "RX FIFO Trigger Level is configured incorrectly\n");
+ return -EINVAL;
+ }
+ break;
+ case SSP_RX_32_OR_MORE_ELEM:
+ if (pl022->vendor->fifodepth < 32) {
+ dev_err(&pl022->adev->dev,
+ "RX FIFO Trigger Level is configured incorrectly\n");
+ return -EINVAL;
+ }
+ break;
+ default:
dev_err(&pl022->adev->dev,
"RX FIFO Trigger Level is configured incorrectly\n");
return -EINVAL;
+ break;
}
- if ((chip_info->tx_lev_trig < SSP_TX_1_OR_MORE_EMPTY_LOC)
- || (chip_info->tx_lev_trig > SSP_TX_32_OR_MORE_EMPTY_LOC)) {
+ switch (chip_info->tx_lev_trig) {
+ case SSP_TX_1_OR_MORE_EMPTY_LOC:
+ case SSP_TX_4_OR_MORE_EMPTY_LOC:
+ case SSP_TX_8_OR_MORE_EMPTY_LOC:
+ /* These are always OK, all variants can handle this */
+ break;
+ case SSP_TX_16_OR_MORE_EMPTY_LOC:
+ if (pl022->vendor->fifodepth < 16) {
+ dev_err(&pl022->adev->dev,
+ "TX FIFO Trigger Level is configured incorrectly\n");
+ return -EINVAL;
+ }
+ break;
+ case SSP_TX_32_OR_MORE_EMPTY_LOC:
+ if (pl022->vendor->fifodepth < 32) {
+ dev_err(&pl022->adev->dev,
+ "TX FIFO Trigger Level is configured incorrectly\n");
+ return -EINVAL;
+ }
+ break;
+ default:
dev_err(&pl022->adev->dev,
"TX FIFO Trigger Level is configured incorrectly\n");
return -EINVAL;
+ break;
}
if (chip_info->iface == SSP_INTERFACE_NATIONAL_MICROWIRE) {
if ((chip_info->ctrl_len < SSP_BITS_4)
@@ -1874,6 +1963,9 @@ static int pl022_setup(struct spi_device *spi)
goto err_config_params;
}
+ pl022->rx_lev_trig = chip_info->rx_lev_trig;
+ pl022->tx_lev_trig = chip_info->tx_lev_trig;
+
/* Now set controller state based on controller data */
chip->xfer_type = chip_info->com_mode;
if (!chip_info->cs_control) {
@@ -2094,6 +2186,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
}
printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n",
adev->res.start, pl022->virtbase);
+ pm_runtime_enable(dev);
+ pm_runtime_resume(dev);
pl022->clk = clk_get(&adev->dev, NULL);
if (IS_ERR(pl022->clk)) {
@@ -2155,6 +2249,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
destroy_queue(pl022);
pl022_dma_remove(pl022);
free_irq(adev->irq[0], pl022);
+ pm_runtime_disable(&adev->dev);
err_no_irq:
clk_put(pl022->clk);
err_no_clk:
diff --git a/drivers/spi/spi_ppc4xx.c b/drivers/spi/spi-ppc4xx.c
index 2a298c029194..b267fd901e54 100644
--- a/drivers/spi/spi_ppc4xx.c
+++ b/drivers/spi/spi-ppc4xx.c
@@ -502,7 +502,7 @@ static int __init spi_ppc4xx_of_probe(struct platform_device *op)
goto free_gpios;
}
hw->mapbase = resource.start;
- hw->mapsize = resource.end - resource.start + 1;
+ hw->mapsize = resource_size(&resource);
/* Sanity check */
if (hw->mapsize < sizeof(struct spi_ppc4xx_regs)) {
diff --git a/drivers/spi/pxa2xx_spi_pci.c b/drivers/spi/spi-pxa2xx-pci.c
index 378e504f89eb..378e504f89eb 100644
--- a/drivers/spi/pxa2xx_spi_pci.c
+++ b/drivers/spi/spi-pxa2xx-pci.c
diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/spi-pxa2xx.c
index dc25bee8d33f..dc25bee8d33f 100644
--- a/drivers/spi/pxa2xx_spi.c
+++ b/drivers/spi/spi-pxa2xx.c
diff --git a/drivers/spi/spi_s3c24xx_fiq.S b/drivers/spi/spi-s3c24xx-fiq.S
index 3793cae361db..059f2dc1fda2 100644
--- a/drivers/spi/spi_s3c24xx_fiq.S
+++ b/drivers/spi/spi-s3c24xx-fiq.S
@@ -17,7 +17,7 @@
#include <mach/regs-irq.h>
#include <plat/regs-spi.h>
-#include "spi_s3c24xx_fiq.h"
+#include "spi-s3c24xx-fiq.h"
.text
diff --git a/drivers/spi/spi_s3c24xx_fiq.h b/drivers/spi/spi-s3c24xx-fiq.h
index a5950bb25b51..a5950bb25b51 100644
--- a/drivers/spi/spi_s3c24xx_fiq.h
+++ b/drivers/spi/spi-s3c24xx-fiq.h
diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi-s3c24xx.c
index 1a5fcabfd565..1996ac57ef91 100644
--- a/drivers/spi/spi_s3c24xx.c
+++ b/drivers/spi/spi-s3c24xx.c
@@ -1,5 +1,4 @@
-/* linux/drivers/spi/spi_s3c24xx.c
- *
+/*
* Copyright (c) 2006 Ben Dooks
* Copyright 2006-2009 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
@@ -32,7 +31,7 @@
#include <plat/fiq.h>
#include <asm/fiq.h>
-#include "spi_s3c24xx_fiq.h"
+#include "spi-s3c24xx-fiq.h"
/**
* s3c24xx_spi_devstate - per device data
diff --git a/drivers/spi/spi_s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 8945e201e42e..595dacc7645f 100644
--- a/drivers/spi/spi_s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -1,5 +1,4 @@
-/* linux/drivers/spi/spi_s3c64xx.c
- *
+/*
* Copyright (C) 2009 Samsung Electronics Ltd.
* Jaswinder Singh <jassi.brar@samsung.com>
*
diff --git a/drivers/spi/spi_sh_msiof.c b/drivers/spi/spi-sh-msiof.c
index e00d94b22250..e00d94b22250 100644
--- a/drivers/spi/spi_sh_msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
diff --git a/drivers/spi/spi_sh_sci.c b/drivers/spi/spi-sh-sci.c
index 5c6439161199..e7779c09f6ef 100644
--- a/drivers/spi/spi_sh_sci.c
+++ b/drivers/spi/spi-sh-sci.c
@@ -78,7 +78,7 @@ static inline u32 getmiso(struct spi_device *dev)
#define spidelay(x) ndelay(x)
-#include "spi_bitbang_txrx.h"
+#include "spi-bitbang-txrx.h"
static u32 sh_sci_spi_txrx_mode0(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
diff --git a/drivers/spi/spi_sh.c b/drivers/spi/spi-sh.c
index 9eedd71ad898..9eedd71ad898 100644
--- a/drivers/spi/spi_sh.c
+++ b/drivers/spi/spi-sh.c
diff --git a/drivers/spi/spi_stmp.c b/drivers/spi/spi-stmp.c
index fadff76eb7e0..fadff76eb7e0 100644
--- a/drivers/spi/spi_stmp.c
+++ b/drivers/spi/spi-stmp.c
diff --git a/drivers/spi/spi_tegra.c b/drivers/spi/spi-tegra.c
index 6c3aa6ecaade..a5a6302dc8e0 100644
--- a/drivers/spi/spi_tegra.c
+++ b/drivers/spi/spi-tegra.c
@@ -498,14 +498,14 @@ static int __init spi_tegra_probe(struct platform_device *pdev)
goto err0;
}
- if (!request_mem_region(r->start, (r->end - r->start) + 1,
+ if (!request_mem_region(r->start, resource_size(r),
dev_name(&pdev->dev))) {
ret = -EBUSY;
goto err0;
}
tspi->phys = r->start;
- tspi->base = ioremap(r->start, r->end - r->start + 1);
+ tspi->base = ioremap(r->start, resource_size(r));
if (!tspi->base) {
dev_err(&pdev->dev, "can't ioremap iomem\n");
ret = -ENOMEM;
@@ -546,6 +546,7 @@ static int __init spi_tegra_probe(struct platform_device *pdev)
tspi->rx_dma_req.req_sel = spi_tegra_req_sels[pdev->id];
tspi->rx_dma_req.dev = tspi;
+ master->dev.of_node = pdev->dev.of_node;
ret = spi_register_master(master);
if (ret < 0)
@@ -563,7 +564,7 @@ err3:
err2:
iounmap(tspi->base);
err1:
- release_mem_region(r->start, (r->end - r->start) + 1);
+ release_mem_region(r->start, resource_size(r));
err0:
spi_master_put(master);
return ret;
@@ -588,17 +589,28 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev)
iounmap(tspi->base);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(r->start, (r->end - r->start) + 1);
+ release_mem_region(r->start, resource_size(r));
return 0;
}
MODULE_ALIAS("platform:spi_tegra");
+#ifdef CONFIG_OF
+static struct of_device_id spi_tegra_of_match_table[] __devinitdata = {
+ { .compatible = "nvidia,tegra20-spi", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, spi_tegra_of_match_table);
+#else /* CONFIG_OF */
+#define spi_tegra_of_match_table NULL
+#endif /* CONFIG_OF */
+
static struct platform_driver spi_tegra_driver = {
.driver = {
.name = "spi_tegra",
.owner = THIS_MODULE,
+ .of_match_table = spi_tegra_of_match_table,
},
.remove = __devexit_p(spi_tegra_remove),
};
diff --git a/drivers/spi/ti-ssp-spi.c b/drivers/spi/spi-ti-ssp.c
index ee22795c7973..ee22795c7973 100644
--- a/drivers/spi/ti-ssp-spi.c
+++ b/drivers/spi/spi-ti-ssp.c
diff --git a/drivers/spi/tle62x0.c b/drivers/spi/spi-tle62x0.c
index 32a40876532f..940e73d1cf09 100644
--- a/drivers/spi/tle62x0.c
+++ b/drivers/spi/spi-tle62x0.c
@@ -1,5 +1,5 @@
/*
- * tle62x0.c -- support Infineon TLE62x0 driver chips
+ * Support Infineon TLE62x0 driver chips
*
* Copyright (c) 2007 Simtec Electronics
* Ben Dooks, <ben@simtec.co.uk>
diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi-topcliff-pch.c
index 79e48d451137..1d23f3831866 100644
--- a/drivers/spi/spi_topcliff_pch.c
+++ b/drivers/spi/spi-topcliff-pch.c
@@ -26,6 +26,10 @@
#include <linux/spi/spidev.h>
#include <linux/module.h>
#include <linux/device.h>
+#include <linux/platform_device.h>
+
+#include <linux/dmaengine.h>
+#include <linux/pch_dma.h>
/* Register offsets */
#define PCH_SPCR 0x00 /* SPI control register */
@@ -35,6 +39,7 @@
#define PCH_SPDRR 0x10 /* SPI read data register */
#define PCH_SSNXCR 0x18 /* SSN Expand Control Register */
#define PCH_SRST 0x1C /* SPI reset register */
+#define PCH_ADDRESS_SIZE 0x20
#define PCH_SPSR_TFD 0x000007C0
#define PCH_SPSR_RFD 0x0000F800
@@ -52,8 +57,6 @@
#define STATUS_EXITING 2
#define PCH_SLEEP_TIME 10
-#define PCH_ADDRESS_SIZE 0x20
-
#define SSN_LOW 0x02U
#define SSN_NO_CONTROL 0x00U
#define PCH_MAX_CS 0xFF
@@ -73,22 +76,57 @@
#define SPSR_TFI_BIT (1 << 0)
#define SPSR_RFI_BIT (1 << 1)
#define SPSR_FI_BIT (1 << 2)
+#define SPSR_ORF_BIT (1 << 3)
#define SPBRR_SIZE_BIT (1 << 10)
-#define PCH_ALL (SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|SPCR_ORIE_BIT|SPCR_MDFIE_BIT)
+#define PCH_ALL (SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|\
+ SPCR_ORIE_BIT|SPCR_MDFIE_BIT)
#define SPCR_RFIC_FIELD 20
#define SPCR_TFIC_FIELD 16
-#define SPSR_INT_BITS 0x1F
-#define MASK_SPBRR_SPBR_BITS (~((1 << 10) - 1))
-#define MASK_RFIC_SPCR_BITS (~(0xf << 20))
-#define MASK_TFIC_SPCR_BITS (~(0xf000f << 12))
+#define MASK_SPBRR_SPBR_BITS ((1 << 10) - 1)
+#define MASK_RFIC_SPCR_BITS (0xf << SPCR_RFIC_FIELD)
+#define MASK_TFIC_SPCR_BITS (0xf << SPCR_TFIC_FIELD)
#define PCH_CLOCK_HZ 50000000
#define PCH_MAX_SPBR 1023
+/* Definition for ML7213 by OKI SEMICONDUCTOR */
+#define PCI_VENDOR_ID_ROHM 0x10DB
+#define PCI_DEVICE_ID_ML7213_SPI 0x802c
+#define PCI_DEVICE_ID_ML7223_SPI 0x800F
+/*
+ * Set the number of SPI instance max
+ * Intel EG20T PCH : 1ch
+ * OKI SEMICONDUCTOR ML7213 IOH : 2ch
+ * OKI SEMICONDUCTOR ML7223 IOH : 1ch
+*/
+#define PCH_SPI_MAX_DEV 2
+
+#define PCH_BUF_SIZE 4096
+#define PCH_DMA_TRANS_SIZE 12
+
+static int use_dma = 1;
+
+struct pch_spi_dma_ctrl {
+ struct dma_async_tx_descriptor *desc_tx;
+ struct dma_async_tx_descriptor *desc_rx;
+ struct pch_dma_slave param_tx;
+ struct pch_dma_slave param_rx;
+ struct dma_chan *chan_tx;
+ struct dma_chan *chan_rx;
+ struct scatterlist *sg_tx_p;
+ struct scatterlist *sg_rx_p;
+ struct scatterlist sg_tx;
+ struct scatterlist sg_rx;
+ int nent;
+ void *tx_buf_virt;
+ void *rx_buf_virt;
+ dma_addr_t tx_buf_dma;
+ dma_addr_t rx_buf_dma;
+};
/**
* struct pch_spi_data - Holds the SPI channel specific details
* @io_remap_addr: The remapped PCI base address
@@ -121,9 +159,13 @@
* @cur_trans: The current transfer that this SPI driver is
* handling
* @board_dat: Reference to the SPI device data structure
+ * @plat_dev: platform_device structure
+ * @ch: SPI channel number
+ * @irq_reg_sts: Status of IRQ registration
*/
struct pch_spi_data {
void __iomem *io_remap_addr;
+ unsigned long io_base_addr;
struct spi_master *master;
struct work_struct work;
struct workqueue_struct *wk;
@@ -144,27 +186,36 @@ struct pch_spi_data {
struct spi_message *current_msg;
struct spi_transfer *cur_trans;
struct pch_spi_board_data *board_dat;
+ struct platform_device *plat_dev;
+ int ch;
+ struct pch_spi_dma_ctrl dma;
+ int use_dma;
+ u8 irq_reg_sts;
};
/**
* struct pch_spi_board_data - Holds the SPI device specific details
* @pdev: Pointer to the PCI device
- * @irq_reg_sts: Status of IRQ registration
- * @pci_req_sts: Status of pci_request_regions
* @suspend_sts: Status of suspend
- * @data: Pointer to SPI channel data structure
+ * @num: The number of SPI device instance
*/
struct pch_spi_board_data {
struct pci_dev *pdev;
- u8 irq_reg_sts;
- u8 pci_req_sts;
u8 suspend_sts;
- struct pch_spi_data *data;
+ int num;
+};
+
+struct pch_pd_dev_save {
+ int num;
+ struct platform_device *pd_save[PCH_SPI_MAX_DEV];
+ struct pch_spi_board_data *board_dat;
};
static struct pci_device_id pch_spi_pcidev_id[] = {
- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_GE_SPI)},
- {0,}
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_GE_SPI), 1, },
+ { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_SPI), 2, },
+ { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_SPI), 1, },
+ { }
};
/**
@@ -251,10 +302,10 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
reg_spcr_val &= ~SPCR_RFIE_BIT; /* disable RFI */
/* reset rx threshold */
- reg_spcr_val &= MASK_RFIC_SPCR_BITS;
+ reg_spcr_val &= ~MASK_RFIC_SPCR_BITS;
reg_spcr_val |= (PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD);
- iowrite32(((reg_spcr_val) &= (~(SPCR_RFIE_BIT))),
- (io_remap_addr + PCH_SPCR));
+
+ iowrite32(reg_spcr_val, (io_remap_addr + PCH_SPCR));
}
/* update counts */
@@ -265,12 +316,15 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
/* if transfer complete interrupt */
if (reg_spsr_val & SPSR_FI_BIT) {
- /* disable FI & RFI interrupts */
- pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
- SPCR_FIE_BIT | SPCR_RFIE_BIT);
+ if (tx_index < bpw_len)
+ dev_err(&data->master->dev,
+ "%s : Transfer is not completed", __func__);
+ /* disable interrupts */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
/* transfer is completed;inform pch_spi_process_messages */
data->transfer_complete = true;
+ data->transfer_active = false;
wake_up(&data->wait);
}
}
@@ -283,24 +337,28 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
static irqreturn_t pch_spi_handler(int irq, void *dev_id)
{
u32 reg_spsr_val;
- struct pch_spi_data *data;
void __iomem *spsr;
void __iomem *io_remap_addr;
irqreturn_t ret = IRQ_NONE;
- struct pch_spi_board_data *board_dat = dev_id;
+ struct pch_spi_data *data = dev_id;
+ struct pch_spi_board_data *board_dat = data->board_dat;
if (board_dat->suspend_sts) {
dev_dbg(&board_dat->pdev->dev,
"%s returning due to suspend\n", __func__);
return IRQ_NONE;
}
+ if (data->use_dma)
+ return IRQ_NONE;
- data = board_dat->data;
io_remap_addr = data->io_remap_addr;
spsr = io_remap_addr + PCH_SPSR;
reg_spsr_val = ioread32(spsr);
+ if (reg_spsr_val & SPSR_ORF_BIT)
+ dev_err(&board_dat->pdev->dev, "%s Over run error", __func__);
+
/* Check if the interrupt is for SPI device */
if (reg_spsr_val & (SPSR_FI_BIT | SPSR_RFI_BIT)) {
pch_spi_handler_sub(data, reg_spsr_val, io_remap_addr);
@@ -326,7 +384,7 @@ static void pch_spi_set_baud_rate(struct spi_master *master, u32 speed_hz)
if (n_spbr > PCH_MAX_SPBR)
n_spbr = PCH_MAX_SPBR;
- pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, ~MASK_SPBRR_SPBR_BITS);
+ pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, MASK_SPBRR_SPBR_BITS);
}
/**
@@ -435,26 +493,27 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
dev_dbg(&pspi->dev, "%s Transfer List not empty. "
"Transfer Speed is set.\n", __func__);
+ spin_lock_irqsave(&data->lock, flags);
/* validate Tx/Rx buffers and Transfer length */
list_for_each_entry(transfer, &pmsg->transfers, transfer_list) {
if (!transfer->tx_buf && !transfer->rx_buf) {
dev_err(&pspi->dev,
"%s Tx and Rx buffer NULL\n", __func__);
retval = -EINVAL;
- goto err_out;
+ goto err_return_spinlock;
}
if (!transfer->len) {
dev_err(&pspi->dev, "%s Transfer length invalid\n",
__func__);
retval = -EINVAL;
- goto err_out;
+ goto err_return_spinlock;
}
dev_dbg(&pspi->dev, "%s Tx/Rx buffer valid. Transfer length"
" valid\n", __func__);
- /* if baud rate hs been specified validate the same */
+ /* if baud rate has been specified validate the same */
if (transfer->speed_hz > PCH_MAX_BAUDRATE)
transfer->speed_hz = PCH_MAX_BAUDRATE;
@@ -465,25 +524,24 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
retval = -EINVAL;
dev_err(&pspi->dev,
"%s Invalid bits per word\n", __func__);
- goto err_out;
+ goto err_return_spinlock;
}
}
}
-
- spin_lock_irqsave(&data->lock, flags);
+ spin_unlock_irqrestore(&data->lock, flags);
/* We won't process any messages if we have been asked to terminate */
if (data->status == STATUS_EXITING) {
dev_err(&pspi->dev, "%s status = STATUS_EXITING.\n", __func__);
retval = -ESHUTDOWN;
- goto err_return_spinlock;
+ goto err_out;
}
/* If suspended ,return -EINVAL */
if (data->board_dat->suspend_sts) {
dev_err(&pspi->dev, "%s suspend; returning EINVAL\n", __func__);
retval = -EINVAL;
- goto err_return_spinlock;
+ goto err_out;
}
/* set status of message */
@@ -491,9 +549,11 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
dev_dbg(&pspi->dev, "%s - pmsg->status =%d\n", __func__, pmsg->status);
pmsg->status = -EINPROGRESS;
-
+ spin_lock_irqsave(&data->lock, flags);
/* add message to queue */
list_add_tail(&pmsg->queue, &data->queue);
+ spin_unlock_irqrestore(&data->lock, flags);
+
dev_dbg(&pspi->dev, "%s - Invoked list_add_tail\n", __func__);
/* schedule work queue to run */
@@ -502,11 +562,13 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
retval = 0;
-err_return_spinlock:
- spin_unlock_irqrestore(&data->lock, flags);
err_out:
dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval);
return retval;
+err_return_spinlock:
+ dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval);
+ spin_unlock_irqrestore(&data->lock, flags);
+ return retval;
}
static inline void pch_spi_select_chip(struct pch_spi_data *data,
@@ -527,8 +589,7 @@ static inline void pch_spi_select_chip(struct pch_spi_data *data,
pch_spi_setup_transfer(pspi);
}
-static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
- struct spi_message **ppmsg)
+static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw)
{
int size;
u32 n_writes;
@@ -537,8 +598,6 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
const u8 *tx_buf;
const u16 *tx_sbuf;
- pmsg = *ppmsg;
-
/* set baud rate if needed */
if (data->cur_trans->speed_hz) {
dev_dbg(&data->master->dev, "%s:setting baud rate\n", __func__);
@@ -621,10 +680,9 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
data->transfer_active = true;
}
-
-static void pch_spi_nomore_transfer(struct pch_spi_data *data,
- struct spi_message *pmsg)
+static void pch_spi_nomore_transfer(struct pch_spi_data *data)
{
+ struct spi_message *pmsg;
dev_dbg(&data->master->dev, "%s called\n", __func__);
/* Invoke complete callback
* [To the spi core..indicating end of transfer] */
@@ -675,29 +733,21 @@ static void pch_spi_nomore_transfer(struct pch_spi_data *data,
static void pch_spi_set_ir(struct pch_spi_data *data)
{
- /* enable interrupts */
- if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH) {
+ /* enable interrupts, set threshold, enable SPI */
+ if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH)
/* set receive threshold to PCH_RX_THOLD */
pch_spi_setclr_reg(data->master, PCH_SPCR,
- PCH_RX_THOLD << SPCR_RFIC_FIELD,
- ~MASK_RFIC_SPCR_BITS);
- /* enable FI and RFI interrupts */
- pch_spi_setclr_reg(data->master, PCH_SPCR,
- SPCR_RFIE_BIT | SPCR_FIE_BIT, 0);
- } else {
+ PCH_RX_THOLD << SPCR_RFIC_FIELD |
+ SPCR_FIE_BIT | SPCR_RFIE_BIT |
+ SPCR_ORIE_BIT | SPCR_SPE_BIT,
+ MASK_RFIC_SPCR_BITS | PCH_ALL);
+ else
/* set receive threshold to maximum */
pch_spi_setclr_reg(data->master, PCH_SPCR,
- PCH_RX_THOLD_MAX << SPCR_TFIC_FIELD,
- ~MASK_TFIC_SPCR_BITS);
- /* enable FI interrupt */
- pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_FIE_BIT, 0);
- }
-
- dev_dbg(&data->master->dev,
- "%s:invoking pch_spi_set_enable to enable SPI\n", __func__);
-
- /* SPI set enable */
- pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT, 0);
+ PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD |
+ SPCR_FIE_BIT | SPCR_ORIE_BIT |
+ SPCR_SPE_BIT,
+ MASK_RFIC_SPCR_BITS | PCH_ALL);
/* Wait until the transfer completes; go to sleep after
initiating the transfer. */
@@ -710,15 +760,13 @@ static void pch_spi_set_ir(struct pch_spi_data *data)
dev_dbg(&data->master->dev,
"%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
- data->transfer_active = false;
- dev_dbg(&data->master->dev,
- "%s set data->transfer_active = false\n", __func__);
-
/* clear all interrupts */
pch_spi_writereg(data->master, PCH_SPSR,
pch_spi_readreg(data->master, PCH_SPSR));
- /* disable interrupts */
- pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
+ /* Disable interrupts and SPI transfer */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL | SPCR_SPE_BIT);
+ /* clear FIFO */
+ pch_spi_clear_fifo(data->master);
}
static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
@@ -742,6 +790,327 @@ static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
}
}
+static void pch_spi_copy_rx_data_for_dma(struct pch_spi_data *data, int bpw)
+{
+ int j;
+ u8 *rx_buf;
+ u16 *rx_sbuf;
+ const u8 *rx_dma_buf;
+ const u16 *rx_dma_sbuf;
+
+ /* copy Rx Data */
+ if (!data->cur_trans->rx_buf)
+ return;
+
+ if (bpw == 8) {
+ rx_buf = data->cur_trans->rx_buf;
+ rx_dma_buf = data->dma.rx_buf_virt;
+ for (j = 0; j < data->bpw_len; j++)
+ *rx_buf++ = *rx_dma_buf++ & 0xFF;
+ } else {
+ rx_sbuf = data->cur_trans->rx_buf;
+ rx_dma_sbuf = data->dma.rx_buf_virt;
+ for (j = 0; j < data->bpw_len; j++)
+ *rx_sbuf++ = *rx_dma_sbuf++;
+ }
+}
+
+static void pch_spi_start_transfer(struct pch_spi_data *data)
+{
+ struct pch_spi_dma_ctrl *dma;
+ unsigned long flags;
+
+ dma = &data->dma;
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ /* disable interrupts, SPI set enable */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_SPE_BIT, PCH_ALL);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ /* Wait until the transfer completes; go to sleep after
+ initiating the transfer. */
+ dev_dbg(&data->master->dev,
+ "%s:waiting for transfer to get over\n", __func__);
+ wait_event_interruptible(data->wait, data->transfer_complete);
+
+ dma_sync_sg_for_cpu(&data->master->dev, dma->sg_rx_p, dma->nent,
+ DMA_FROM_DEVICE);
+ async_tx_ack(dma->desc_rx);
+ async_tx_ack(dma->desc_tx);
+ kfree(dma->sg_tx_p);
+ kfree(dma->sg_rx_p);
+
+ spin_lock_irqsave(&data->lock, flags);
+ pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL);
+ dev_dbg(&data->master->dev,
+ "%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
+
+ /* clear fifo threshold, disable interrupts, disable SPI transfer */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
+ MASK_RFIC_SPCR_BITS | MASK_TFIC_SPCR_BITS | PCH_ALL |
+ SPCR_SPE_BIT);
+ /* clear all interrupts */
+ pch_spi_writereg(data->master, PCH_SPSR,
+ pch_spi_readreg(data->master, PCH_SPSR));
+ /* clear FIFO */
+ pch_spi_clear_fifo(data->master);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static void pch_dma_rx_complete(void *arg)
+{
+ struct pch_spi_data *data = arg;
+
+ /* transfer is completed;inform pch_spi_process_messages_dma */
+ data->transfer_complete = true;
+ wake_up_interruptible(&data->wait);
+}
+
+static bool pch_spi_filter(struct dma_chan *chan, void *slave)
+{
+ struct pch_dma_slave *param = slave;
+
+ if ((chan->chan_id == param->chan_id) &&
+ (param->dma_dev == chan->device->dev)) {
+ chan->private = param;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static void pch_spi_request_dma(struct pch_spi_data *data, int bpw)
+{
+ dma_cap_mask_t mask;
+ struct dma_chan *chan;
+ struct pci_dev *dma_dev;
+ struct pch_dma_slave *param;
+ struct pch_spi_dma_ctrl *dma;
+ unsigned int width;
+
+ if (bpw == 8)
+ width = PCH_DMA_WIDTH_1_BYTE;
+ else
+ width = PCH_DMA_WIDTH_2_BYTES;
+
+ dma = &data->dma;
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ /* Get DMA's dev information */
+ dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(12, 0));
+
+ /* Set Tx DMA */
+ param = &dma->param_tx;
+ param->dma_dev = &dma_dev->dev;
+ param->chan_id = data->master->bus_num * 2; /* Tx = 0, 2 */
+ param->tx_reg = data->io_base_addr + PCH_SPDWR;
+ param->width = width;
+ chan = dma_request_channel(mask, pch_spi_filter, param);
+ if (!chan) {
+ dev_err(&data->master->dev,
+ "ERROR: dma_request_channel FAILS(Tx)\n");
+ data->use_dma = 0;
+ return;
+ }
+ dma->chan_tx = chan;
+
+ /* Set Rx DMA */
+ param = &dma->param_rx;
+ param->dma_dev = &dma_dev->dev;
+ param->chan_id = data->master->bus_num * 2 + 1; /* Rx = Tx + 1 */
+ param->rx_reg = data->io_base_addr + PCH_SPDRR;
+ param->width = width;
+ chan = dma_request_channel(mask, pch_spi_filter, param);
+ if (!chan) {
+ dev_err(&data->master->dev,
+ "ERROR: dma_request_channel FAILS(Rx)\n");
+ dma_release_channel(dma->chan_tx);
+ dma->chan_tx = NULL;
+ data->use_dma = 0;
+ return;
+ }
+ dma->chan_rx = chan;
+}
+
+static void pch_spi_release_dma(struct pch_spi_data *data)
+{
+ struct pch_spi_dma_ctrl *dma;
+
+ dma = &data->dma;
+ if (dma->chan_tx) {
+ dma_release_channel(dma->chan_tx);
+ dma->chan_tx = NULL;
+ }
+ if (dma->chan_rx) {
+ dma_release_channel(dma->chan_rx);
+ dma->chan_rx = NULL;
+ }
+ return;
+}
+
+static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
+{
+ const u8 *tx_buf;
+ const u16 *tx_sbuf;
+ u8 *tx_dma_buf;
+ u16 *tx_dma_sbuf;
+ struct scatterlist *sg;
+ struct dma_async_tx_descriptor *desc_tx;
+ struct dma_async_tx_descriptor *desc_rx;
+ int num;
+ int i;
+ int size;
+ int rem;
+ unsigned long flags;
+ struct pch_spi_dma_ctrl *dma;
+
+ dma = &data->dma;
+
+ /* set baud rate if needed */
+ if (data->cur_trans->speed_hz) {
+ dev_dbg(&data->master->dev, "%s:setting baud rate\n", __func__);
+ spin_lock_irqsave(&data->lock, flags);
+ pch_spi_set_baud_rate(data->master, data->cur_trans->speed_hz);
+ spin_unlock_irqrestore(&data->lock, flags);
+ }
+
+ /* set bits per word if needed */
+ if (data->cur_trans->bits_per_word &&
+ (data->current_msg->spi->bits_per_word !=
+ data->cur_trans->bits_per_word)) {
+ dev_dbg(&data->master->dev, "%s:set bits per word\n", __func__);
+ spin_lock_irqsave(&data->lock, flags);
+ pch_spi_set_bits_per_word(data->master,
+ data->cur_trans->bits_per_word);
+ spin_unlock_irqrestore(&data->lock, flags);
+ *bpw = data->cur_trans->bits_per_word;
+ } else {
+ *bpw = data->current_msg->spi->bits_per_word;
+ }
+ data->bpw_len = data->cur_trans->len / (*bpw / 8);
+
+ /* copy Tx Data */
+ if (data->cur_trans->tx_buf != NULL) {
+ if (*bpw == 8) {
+ tx_buf = data->cur_trans->tx_buf;
+ tx_dma_buf = dma->tx_buf_virt;
+ for (i = 0; i < data->bpw_len; i++)
+ *tx_dma_buf++ = *tx_buf++;
+ } else {
+ tx_sbuf = data->cur_trans->tx_buf;
+ tx_dma_sbuf = dma->tx_buf_virt;
+ for (i = 0; i < data->bpw_len; i++)
+ *tx_dma_sbuf++ = *tx_sbuf++;
+ }
+ }
+ if (data->bpw_len > PCH_DMA_TRANS_SIZE) {
+ num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1;
+ size = PCH_DMA_TRANS_SIZE;
+ rem = data->bpw_len % PCH_DMA_TRANS_SIZE;
+ } else {
+ num = 1;
+ size = data->bpw_len;
+ rem = data->bpw_len;
+ }
+ dev_dbg(&data->master->dev, "%s num=%d size=%d rem=%d\n",
+ __func__, num, size, rem);
+ spin_lock_irqsave(&data->lock, flags);
+
+ /* set receive fifo threshold and transmit fifo threshold */
+ pch_spi_setclr_reg(data->master, PCH_SPCR,
+ ((size - 1) << SPCR_RFIC_FIELD) |
+ ((PCH_MAX_FIFO_DEPTH - PCH_DMA_TRANS_SIZE) <<
+ SPCR_TFIC_FIELD),
+ MASK_RFIC_SPCR_BITS | MASK_TFIC_SPCR_BITS);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ /* RX */
+ dma->sg_rx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
+ sg_init_table(dma->sg_rx_p, num); /* Initialize SG table */
+ /* offset, length setting */
+ sg = dma->sg_rx_p;
+ for (i = 0; i < num; i++, sg++) {
+ if (i == 0) {
+ sg->offset = 0;
+ sg_set_page(sg, virt_to_page(dma->rx_buf_virt), rem,
+ sg->offset);
+ sg_dma_len(sg) = rem;
+ } else {
+ sg->offset = rem + size * (i - 1);
+ sg->offset = sg->offset * (*bpw / 8);
+ sg_set_page(sg, virt_to_page(dma->rx_buf_virt), size,
+ sg->offset);
+ sg_dma_len(sg) = size;
+ }
+ sg_dma_address(sg) = dma->rx_buf_dma + sg->offset;
+ }
+ sg = dma->sg_rx_p;
+ desc_rx = dma->chan_rx->device->device_prep_slave_sg(dma->chan_rx, sg,
+ num, DMA_FROM_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc_rx) {
+ dev_err(&data->master->dev, "%s:device_prep_slave_sg Failed\n",
+ __func__);
+ return;
+ }
+ dma_sync_sg_for_device(&data->master->dev, sg, num, DMA_FROM_DEVICE);
+ desc_rx->callback = pch_dma_rx_complete;
+ desc_rx->callback_param = data;
+ dma->nent = num;
+ dma->desc_rx = desc_rx;
+
+ /* TX */
+ dma->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
+ sg_init_table(dma->sg_tx_p, num); /* Initialize SG table */
+ /* offset, length setting */
+ sg = dma->sg_tx_p;
+ for (i = 0; i < num; i++, sg++) {
+ if (i == 0) {
+ sg->offset = 0;
+ sg_set_page(sg, virt_to_page(dma->tx_buf_virt), rem,
+ sg->offset);
+ sg_dma_len(sg) = rem;
+ } else {
+ sg->offset = rem + size * (i - 1);
+ sg->offset = sg->offset * (*bpw / 8);
+ sg_set_page(sg, virt_to_page(dma->tx_buf_virt), size,
+ sg->offset);
+ sg_dma_len(sg) = size;
+ }
+ sg_dma_address(sg) = dma->tx_buf_dma + sg->offset;
+ }
+ sg = dma->sg_tx_p;
+ desc_tx = dma->chan_tx->device->device_prep_slave_sg(dma->chan_tx,
+ sg, num, DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc_tx) {
+ dev_err(&data->master->dev, "%s:device_prep_slave_sg Failed\n",
+ __func__);
+ return;
+ }
+ dma_sync_sg_for_device(&data->master->dev, sg, num, DMA_TO_DEVICE);
+ desc_tx->callback = NULL;
+ desc_tx->callback_param = data;
+ dma->nent = num;
+ dma->desc_tx = desc_tx;
+
+ dev_dbg(&data->master->dev, "\n%s:Pulling down SSN low - writing "
+ "0x2 to SSNXCR\n", __func__);
+
+ spin_lock_irqsave(&data->lock, flags);
+ pch_spi_writereg(data->master, PCH_SSNXCR, SSN_LOW);
+ desc_rx->tx_submit(desc_rx);
+ desc_tx->tx_submit(desc_tx);
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ /* reset transfer complete flag */
+ data->transfer_complete = false;
+}
static void pch_spi_process_messages(struct work_struct *pwork)
{
@@ -753,13 +1122,10 @@ static void pch_spi_process_messages(struct work_struct *pwork)
dev_dbg(&data->master->dev, "%s data initialized\n", __func__);
spin_lock(&data->lock);
-
/* check if suspend has been initiated;if yes flush queue */
if (data->board_dat->suspend_sts || (data->status == STATUS_EXITING)) {
- dev_dbg(&data->master->dev,
- "%s suspend/remove initiated,flushing queue\n",
- __func__);
-
+ dev_dbg(&data->master->dev, "%s suspend/remove initiated,"
+ "flushing queue\n", __func__);
list_for_each_entry(pmsg, data->queue.next, queue) {
pmsg->status = -EIO;
@@ -793,53 +1159,42 @@ static void pch_spi_process_messages(struct work_struct *pwork)
spin_unlock(&data->lock);
+ if (data->use_dma)
+ pch_spi_request_dma(data,
+ data->current_msg->spi->bits_per_word);
do {
/* If we are already processing a message get the next
transfer structure from the message otherwise retrieve
the 1st transfer request from the message. */
spin_lock(&data->lock);
-
if (data->cur_trans == NULL) {
data->cur_trans =
- list_entry(data->current_msg->transfers.
- next, struct spi_transfer,
- transfer_list);
- dev_dbg(&data->master->dev,
- "%s :Getting 1st transfer message\n", __func__);
+ list_entry(data->current_msg->transfers.next,
+ struct spi_transfer, transfer_list);
+ dev_dbg(&data->master->dev, "%s "
+ ":Getting 1st transfer message\n", __func__);
} else {
data->cur_trans =
- list_entry(data->cur_trans->transfer_list.next,
- struct spi_transfer,
- transfer_list);
- dev_dbg(&data->master->dev,
- "%s :Getting next transfer message\n",
- __func__);
+ list_entry(data->cur_trans->transfer_list.next,
+ struct spi_transfer, transfer_list);
+ dev_dbg(&data->master->dev, "%s "
+ ":Getting next transfer message\n", __func__);
}
-
spin_unlock(&data->lock);
- pch_spi_set_tx(data, &bpw, &pmsg);
-
- /* Control interrupt*/
- pch_spi_set_ir(data);
-
- /* Disable SPI transfer */
- pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, 0,
- SPCR_SPE_BIT);
-
- /* clear FIFO */
- pch_spi_clear_fifo(data->master);
-
- /* copy Rx Data */
- pch_spi_copy_rx_data(data, bpw);
-
- /* free memory */
- kfree(data->pkt_rx_buff);
- data->pkt_rx_buff = NULL;
-
- kfree(data->pkt_tx_buff);
- data->pkt_tx_buff = NULL;
-
+ if (data->use_dma) {
+ pch_spi_handle_dma(data, &bpw);
+ pch_spi_start_transfer(data);
+ pch_spi_copy_rx_data_for_dma(data, bpw);
+ } else {
+ pch_spi_set_tx(data, &bpw);
+ pch_spi_set_ir(data);
+ pch_spi_copy_rx_data(data, bpw);
+ kfree(data->pkt_rx_buff);
+ data->pkt_rx_buff = NULL;
+ kfree(data->pkt_tx_buff);
+ data->pkt_tx_buff = NULL;
+ }
/* increment message count */
data->current_msg->actual_length += data->cur_trans->len;
@@ -860,125 +1215,60 @@ static void pch_spi_process_messages(struct work_struct *pwork)
/* No more transfer in this message. */
if ((data->cur_trans->transfer_list.next) ==
&(data->current_msg->transfers)) {
- pch_spi_nomore_transfer(data, pmsg);
+ pch_spi_nomore_transfer(data);
}
spin_unlock(&data->lock);
} while (data->cur_trans != NULL);
+
+ if (data->use_dma)
+ pch_spi_release_dma(data);
}
-static void pch_spi_free_resources(struct pch_spi_board_data *board_dat)
+static void pch_spi_free_resources(struct pch_spi_board_data *board_dat,
+ struct pch_spi_data *data)
{
dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__);
/* free workqueue */
- if (board_dat->data->wk != NULL) {
- destroy_workqueue(board_dat->data->wk);
- board_dat->data->wk = NULL;
+ if (data->wk != NULL) {
+ destroy_workqueue(data->wk);
+ data->wk = NULL;
dev_dbg(&board_dat->pdev->dev,
"%s destroy_workqueue invoked successfully\n",
__func__);
}
-
- /* disable interrupts & free IRQ */
- if (board_dat->irq_reg_sts) {
- /* disable interrupts */
- pch_spi_setclr_reg(board_dat->data->master, PCH_SPCR, 0,
- PCH_ALL);
-
- /* free IRQ */
- free_irq(board_dat->pdev->irq, board_dat);
-
- dev_dbg(&board_dat->pdev->dev,
- "%s free_irq invoked successfully\n", __func__);
-
- board_dat->irq_reg_sts = false;
- }
-
- /* unmap PCI base address */
- if (board_dat->data->io_remap_addr != 0) {
- pci_iounmap(board_dat->pdev, board_dat->data->io_remap_addr);
-
- board_dat->data->io_remap_addr = 0;
-
- dev_dbg(&board_dat->pdev->dev,
- "%s pci_iounmap invoked successfully\n", __func__);
- }
-
- /* release PCI region */
- if (board_dat->pci_req_sts) {
- pci_release_regions(board_dat->pdev);
- dev_dbg(&board_dat->pdev->dev,
- "%s pci_release_regions invoked successfully\n",
- __func__);
- board_dat->pci_req_sts = false;
- }
}
-static int pch_spi_get_resources(struct pch_spi_board_data *board_dat)
+static int pch_spi_get_resources(struct pch_spi_board_data *board_dat,
+ struct pch_spi_data *data)
{
- void __iomem *io_remap_addr;
- int retval;
+ int retval = 0;
+
dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__);
/* create workqueue */
- board_dat->data->wk = create_singlethread_workqueue(KBUILD_MODNAME);
- if (!board_dat->data->wk) {
+ data->wk = create_singlethread_workqueue(KBUILD_MODNAME);
+ if (!data->wk) {
dev_err(&board_dat->pdev->dev,
"%s create_singlet hread_workqueue failed\n", __func__);
retval = -EBUSY;
goto err_return;
}
- dev_dbg(&board_dat->pdev->dev,
- "%s create_singlethread_workqueue success\n", __func__);
-
- retval = pci_request_regions(board_dat->pdev, KBUILD_MODNAME);
- if (retval != 0) {
- dev_err(&board_dat->pdev->dev,
- "%s request_region failed\n", __func__);
- goto err_return;
- }
-
- board_dat->pci_req_sts = true;
-
- io_remap_addr = pci_iomap(board_dat->pdev, 1, 0);
- if (io_remap_addr == 0) {
- dev_err(&board_dat->pdev->dev,
- "%s pci_iomap failed\n", __func__);
- retval = -ENOMEM;
- goto err_return;
- }
-
- /* calculate base address for all channels */
- board_dat->data->io_remap_addr = io_remap_addr;
-
/* reset PCH SPI h/w */
- pch_spi_reset(board_dat->data->master);
+ pch_spi_reset(data->master);
dev_dbg(&board_dat->pdev->dev,
"%s pch_spi_reset invoked successfully\n", __func__);
- /* register IRQ */
- retval = request_irq(board_dat->pdev->irq, pch_spi_handler,
- IRQF_SHARED, KBUILD_MODNAME, board_dat);
- if (retval != 0) {
- dev_err(&board_dat->pdev->dev,
- "%s request_irq failed\n", __func__);
- goto err_return;
- }
-
- dev_dbg(&board_dat->pdev->dev, "%s request_irq returned=%d\n",
- __func__, retval);
-
- board_dat->irq_reg_sts = true;
dev_dbg(&board_dat->pdev->dev, "%s data->irq_reg_sts=true\n", __func__);
err_return:
if (retval != 0) {
dev_err(&board_dat->pdev->dev,
"%s FAIL:invoking pch_spi_free_resources\n", __func__);
- pch_spi_free_resources(board_dat);
+ pch_spi_free_resources(board_dat, data);
}
dev_dbg(&board_dat->pdev->dev, "%s Return=%d\n", __func__, retval);
@@ -986,255 +1276,387 @@ err_return:
return retval;
}
-static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+static void pch_free_dma_buf(struct pch_spi_board_data *board_dat,
+ struct pch_spi_data *data)
{
+ struct pch_spi_dma_ctrl *dma;
+
+ dma = &data->dma;
+ if (dma->tx_buf_dma)
+ dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE,
+ dma->tx_buf_virt, dma->tx_buf_dma);
+ if (dma->rx_buf_dma)
+ dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE,
+ dma->rx_buf_virt, dma->rx_buf_dma);
+ return;
+}
- struct spi_master *master;
-
- struct pch_spi_board_data *board_dat;
- int retval;
-
- dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
-
- /* allocate memory for private data */
- board_dat = kzalloc(sizeof(struct pch_spi_board_data), GFP_KERNEL);
- if (board_dat == NULL) {
- dev_err(&pdev->dev,
- " %s memory allocation for private data failed\n",
- __func__);
- retval = -ENOMEM;
- goto err_kmalloc;
- }
+static void pch_alloc_dma_buf(struct pch_spi_board_data *board_dat,
+ struct pch_spi_data *data)
+{
+ struct pch_spi_dma_ctrl *dma;
+
+ dma = &data->dma;
+ /* Get Consistent memory for Tx DMA */
+ dma->tx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev,
+ PCH_BUF_SIZE, &dma->tx_buf_dma, GFP_KERNEL);
+ /* Get Consistent memory for Rx DMA */
+ dma->rx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev,
+ PCH_BUF_SIZE, &dma->rx_buf_dma, GFP_KERNEL);
+}
- dev_dbg(&pdev->dev,
- "%s memory allocation for private data success\n", __func__);
+static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
+{
+ int ret;
+ struct spi_master *master;
+ struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
+ struct pch_spi_data *data;
- /* enable PCI device */
- retval = pci_enable_device(pdev);
- if (retval != 0) {
- dev_err(&pdev->dev, "%s pci_enable_device FAILED\n", __func__);
+ dev_dbg(&plat_dev->dev, "%s:debug\n", __func__);
- goto err_pci_en_device;
+ master = spi_alloc_master(&board_dat->pdev->dev,
+ sizeof(struct pch_spi_data));
+ if (!master) {
+ dev_err(&plat_dev->dev, "spi_alloc_master[%d] failed.\n",
+ plat_dev->id);
+ return -ENOMEM;
}
- dev_dbg(&pdev->dev, "%s pci_enable_device returned=%d\n",
- __func__, retval);
+ data = spi_master_get_devdata(master);
+ data->master = master;
- board_dat->pdev = pdev;
+ platform_set_drvdata(plat_dev, data);
- /* alllocate memory for SPI master */
- master = spi_alloc_master(&pdev->dev, sizeof(struct pch_spi_data));
- if (master == NULL) {
- retval = -ENOMEM;
- dev_err(&pdev->dev, "%s Fail.\n", __func__);
- goto err_spi_alloc_master;
+ /* baseaddress + address offset) */
+ data->io_base_addr = pci_resource_start(board_dat->pdev, 1) +
+ PCH_ADDRESS_SIZE * plat_dev->id;
+ data->io_remap_addr = pci_iomap(board_dat->pdev, 1, 0) +
+ PCH_ADDRESS_SIZE * plat_dev->id;
+ if (!data->io_remap_addr) {
+ dev_err(&plat_dev->dev, "%s pci_iomap failed\n", __func__);
+ ret = -ENOMEM;
+ goto err_pci_iomap;
}
- dev_dbg(&pdev->dev,
- "%s spi_alloc_master returned non NULL\n", __func__);
+ dev_dbg(&plat_dev->dev, "[ch%d] remap_addr=%p\n",
+ plat_dev->id, data->io_remap_addr);
/* initialize members of SPI master */
master->bus_num = -1;
master->num_chipselect = PCH_MAX_CS;
master->setup = pch_spi_setup;
master->transfer = pch_spi_transfer;
- dev_dbg(&pdev->dev,
- "%s transfer member of SPI master initialized\n", __func__);
-
- board_dat->data = spi_master_get_devdata(master);
-
- board_dat->data->master = master;
- board_dat->data->n_curnt_chip = 255;
- board_dat->data->board_dat = board_dat;
- board_dat->data->status = STATUS_RUNNING;
-
- INIT_LIST_HEAD(&board_dat->data->queue);
- spin_lock_init(&board_dat->data->lock);
- INIT_WORK(&board_dat->data->work, pch_spi_process_messages);
- init_waitqueue_head(&board_dat->data->wait);
- /* allocate resources for PCH SPI */
- retval = pch_spi_get_resources(board_dat);
- if (retval) {
- dev_err(&pdev->dev, "%s fail(retval=%d)\n", __func__, retval);
+ data->board_dat = board_dat;
+ data->plat_dev = plat_dev;
+ data->n_curnt_chip = 255;
+ data->status = STATUS_RUNNING;
+ data->ch = plat_dev->id;
+ data->use_dma = use_dma;
+
+ INIT_LIST_HEAD(&data->queue);
+ spin_lock_init(&data->lock);
+ INIT_WORK(&data->work, pch_spi_process_messages);
+ init_waitqueue_head(&data->wait);
+
+ ret = pch_spi_get_resources(board_dat, data);
+ if (ret) {
+ dev_err(&plat_dev->dev, "%s fail(retval=%d)\n", __func__, ret);
goto err_spi_get_resources;
}
- dev_dbg(&pdev->dev, "%s pch_spi_get_resources returned=%d\n",
- __func__, retval);
-
- /* save private data in dev */
- pci_set_drvdata(pdev, board_dat);
- dev_dbg(&pdev->dev, "%s invoked pci_set_drvdata\n", __func__);
+ ret = request_irq(board_dat->pdev->irq, pch_spi_handler,
+ IRQF_SHARED, KBUILD_MODNAME, data);
+ if (ret) {
+ dev_err(&plat_dev->dev,
+ "%s request_irq failed\n", __func__);
+ goto err_request_irq;
+ }
+ data->irq_reg_sts = true;
- /* set master mode */
pch_spi_set_master_mode(master);
- dev_dbg(&pdev->dev,
- "%s invoked pch_spi_set_master_mode\n", __func__);
- /* Register the controller with the SPI core. */
- retval = spi_register_master(master);
- if (retval != 0) {
- dev_err(&pdev->dev,
+ ret = spi_register_master(master);
+ if (ret != 0) {
+ dev_err(&plat_dev->dev,
"%s spi_register_master FAILED\n", __func__);
- goto err_spi_reg_master;
+ goto err_spi_register_master;
}
- dev_dbg(&pdev->dev, "%s spi_register_master returned=%d\n",
- __func__, retval);
-
+ if (use_dma) {
+ dev_info(&plat_dev->dev, "Use DMA for data transfers\n");
+ pch_alloc_dma_buf(board_dat, data);
+ }
return 0;
-err_spi_reg_master:
- spi_unregister_master(master);
+err_spi_register_master:
+ free_irq(board_dat->pdev->irq, board_dat);
+err_request_irq:
+ pch_spi_free_resources(board_dat, data);
err_spi_get_resources:
-err_spi_alloc_master:
+ pci_iounmap(board_dat->pdev, data->io_remap_addr);
+err_pci_iomap:
spi_master_put(master);
- pci_disable_device(pdev);
-err_pci_en_device:
- kfree(board_dat);
-err_kmalloc:
- return retval;
+
+ return ret;
}
-static void pch_spi_remove(struct pci_dev *pdev)
+static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev)
{
- struct pch_spi_board_data *board_dat = pci_get_drvdata(pdev);
+ struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
+ struct pch_spi_data *data = platform_get_drvdata(plat_dev);
int count;
+ unsigned long flags;
- dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+ dev_dbg(&plat_dev->dev, "%s:[ch%d] irq=%d\n",
+ __func__, plat_dev->id, board_dat->pdev->irq);
- if (!board_dat) {
- dev_err(&pdev->dev,
- "%s pci_get_drvdata returned NULL\n", __func__);
- return;
- }
+ if (use_dma)
+ pch_free_dma_buf(board_dat, data);
/* check for any pending messages; no action is taken if the queue
* is still full; but at least we tried. Unload anyway */
count = 500;
- spin_lock(&board_dat->data->lock);
- board_dat->data->status = STATUS_EXITING;
- while ((list_empty(&board_dat->data->queue) == 0) && --count) {
+ spin_lock_irqsave(&data->lock, flags);
+ data->status = STATUS_EXITING;
+ while ((list_empty(&data->queue) == 0) && --count) {
dev_dbg(&board_dat->pdev->dev, "%s :queue not empty\n",
__func__);
- spin_unlock(&board_dat->data->lock);
+ spin_unlock_irqrestore(&data->lock, flags);
msleep(PCH_SLEEP_TIME);
- spin_lock(&board_dat->data->lock);
+ spin_lock_irqsave(&data->lock, flags);
}
- spin_unlock(&board_dat->data->lock);
-
- /* Free resources allocated for PCH SPI */
- pch_spi_free_resources(board_dat);
-
- spi_unregister_master(board_dat->data->master);
-
- /* free memory for private data */
- kfree(board_dat);
+ spin_unlock_irqrestore(&data->lock, flags);
- pci_set_drvdata(pdev, NULL);
+ pch_spi_free_resources(board_dat, data);
+ /* disable interrupts & free IRQ */
+ if (data->irq_reg_sts) {
+ /* disable interrupts */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
+ data->irq_reg_sts = false;
+ free_irq(board_dat->pdev->irq, data);
+ }
- /* disable PCI device */
- pci_disable_device(pdev);
+ pci_iounmap(board_dat->pdev, data->io_remap_addr);
+ spi_unregister_master(data->master);
+ spi_master_put(data->master);
+ platform_set_drvdata(plat_dev, NULL);
- dev_dbg(&pdev->dev, "%s invoked pci_disable_device\n", __func__);
+ return 0;
}
-
#ifdef CONFIG_PM
-static int pch_spi_suspend(struct pci_dev *pdev, pm_message_t state)
+static int pch_spi_pd_suspend(struct platform_device *pd_dev,
+ pm_message_t state)
{
u8 count;
- int retval;
-
- struct pch_spi_board_data *board_dat = pci_get_drvdata(pdev);
+ struct pch_spi_board_data *board_dat = dev_get_platdata(&pd_dev->dev);
+ struct pch_spi_data *data = platform_get_drvdata(pd_dev);
- dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+ dev_dbg(&pd_dev->dev, "%s ENTRY\n", __func__);
if (!board_dat) {
- dev_err(&pdev->dev,
+ dev_err(&pd_dev->dev,
"%s pci_get_drvdata returned NULL\n", __func__);
return -EFAULT;
}
- retval = 0;
- board_dat->suspend_sts = true;
-
/* check if the current message is processed:
Only after thats done the transfer will be suspended */
count = 255;
while ((--count) > 0) {
- if (!(board_dat->data->bcurrent_msg_processing)) {
- dev_dbg(&pdev->dev, "%s board_dat->data->bCurrent_"
- "msg_processing = false\n", __func__);
+ if (!(data->bcurrent_msg_processing))
break;
- } else {
- dev_dbg(&pdev->dev, "%s board_dat->data->bCurrent_msg_"
- "processing = true\n", __func__);
- }
msleep(PCH_SLEEP_TIME);
}
/* Free IRQ */
- if (board_dat->irq_reg_sts) {
+ if (data->irq_reg_sts) {
/* disable all interrupts */
- pch_spi_setclr_reg(board_dat->data->master, PCH_SPCR, 0,
- PCH_ALL);
- pch_spi_reset(board_dat->data->master);
-
- free_irq(board_dat->pdev->irq, board_dat);
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
+ pch_spi_reset(data->master);
+ free_irq(board_dat->pdev->irq, data);
- board_dat->irq_reg_sts = false;
- dev_dbg(&pdev->dev,
+ data->irq_reg_sts = false;
+ dev_dbg(&pd_dev->dev,
"%s free_irq invoked successfully.\n", __func__);
}
+ return 0;
+}
+
+static int pch_spi_pd_resume(struct platform_device *pd_dev)
+{
+ struct pch_spi_board_data *board_dat = dev_get_platdata(&pd_dev->dev);
+ struct pch_spi_data *data = platform_get_drvdata(pd_dev);
+ int retval;
+
+ if (!board_dat) {
+ dev_err(&pd_dev->dev,
+ "%s pci_get_drvdata returned NULL\n", __func__);
+ return -EFAULT;
+ }
+
+ if (!data->irq_reg_sts) {
+ /* register IRQ */
+ retval = request_irq(board_dat->pdev->irq, pch_spi_handler,
+ IRQF_SHARED, KBUILD_MODNAME, data);
+ if (retval < 0) {
+ dev_err(&pd_dev->dev,
+ "%s request_irq failed\n", __func__);
+ return retval;
+ }
+
+ /* reset PCH SPI h/w */
+ pch_spi_reset(data->master);
+ pch_spi_set_master_mode(data->master);
+ data->irq_reg_sts = true;
+ }
+ return 0;
+}
+#else
+#define pch_spi_pd_suspend NULL
+#define pch_spi_pd_resume NULL
+#endif
+
+static struct platform_driver pch_spi_pd_driver = {
+ .driver = {
+ .name = "pch-spi",
+ .owner = THIS_MODULE,
+ },
+ .probe = pch_spi_pd_probe,
+ .remove = __devexit_p(pch_spi_pd_remove),
+ .suspend = pch_spi_pd_suspend,
+ .resume = pch_spi_pd_resume
+};
+
+static int __devinit pch_spi_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct pch_spi_board_data *board_dat;
+ struct platform_device *pd_dev = NULL;
+ int retval;
+ int i;
+ struct pch_pd_dev_save *pd_dev_save;
+
+ pd_dev_save = kzalloc(sizeof(struct pch_pd_dev_save), GFP_KERNEL);
+ if (!pd_dev_save) {
+ dev_err(&pdev->dev, "%s Can't allocate pd_dev_sav\n", __func__);
+ return -ENOMEM;
+ }
+
+ board_dat = kzalloc(sizeof(struct pch_spi_board_data), GFP_KERNEL);
+ if (!board_dat) {
+ dev_err(&pdev->dev, "%s Can't allocate board_dat\n", __func__);
+ retval = -ENOMEM;
+ goto err_no_mem;
+ }
+
+ retval = pci_request_regions(pdev, KBUILD_MODNAME);
+ if (retval) {
+ dev_err(&pdev->dev, "%s request_region failed\n", __func__);
+ goto pci_request_regions;
+ }
+
+ board_dat->pdev = pdev;
+ board_dat->num = id->driver_data;
+ pd_dev_save->num = id->driver_data;
+ pd_dev_save->board_dat = board_dat;
+
+ retval = pci_enable_device(pdev);
+ if (retval) {
+ dev_err(&pdev->dev, "%s pci_enable_device failed\n", __func__);
+ goto pci_enable_device;
+ }
+
+ for (i = 0; i < board_dat->num; i++) {
+ pd_dev = platform_device_alloc("pch-spi", i);
+ if (!pd_dev) {
+ dev_err(&pdev->dev, "platform_device_alloc failed\n");
+ goto err_platform_device;
+ }
+ pd_dev_save->pd_save[i] = pd_dev;
+ pd_dev->dev.parent = &pdev->dev;
+
+ retval = platform_device_add_data(pd_dev, board_dat,
+ sizeof(*board_dat));
+ if (retval) {
+ dev_err(&pdev->dev,
+ "platform_device_add_data failed\n");
+ platform_device_put(pd_dev);
+ goto err_platform_device;
+ }
+
+ retval = platform_device_add(pd_dev);
+ if (retval) {
+ dev_err(&pdev->dev, "platform_device_add failed\n");
+ platform_device_put(pd_dev);
+ goto err_platform_device;
+ }
+ }
+
+ pci_set_drvdata(pdev, pd_dev_save);
+
+ return 0;
+
+err_platform_device:
+ pci_disable_device(pdev);
+pci_enable_device:
+ pci_release_regions(pdev);
+pci_request_regions:
+ kfree(board_dat);
+err_no_mem:
+ kfree(pd_dev_save);
+
+ return retval;
+}
+
+static void __devexit pch_spi_remove(struct pci_dev *pdev)
+{
+ int i;
+ struct pch_pd_dev_save *pd_dev_save = pci_get_drvdata(pdev);
+
+ dev_dbg(&pdev->dev, "%s ENTRY:pdev=%p\n", __func__, pdev);
+
+ for (i = 0; i < pd_dev_save->num; i++)
+ platform_device_unregister(pd_dev_save->pd_save[i]);
+
+ pci_disable_device(pdev);
+ pci_release_regions(pdev);
+ kfree(pd_dev_save->board_dat);
+ kfree(pd_dev_save);
+}
+
+#ifdef CONFIG_PM
+static int pch_spi_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ int retval;
+ struct pch_pd_dev_save *pd_dev_save = pci_get_drvdata(pdev);
+
+ dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+
+ pd_dev_save->board_dat->suspend_sts = true;
+
/* save config space */
retval = pci_save_state(pdev);
-
if (retval == 0) {
- dev_dbg(&pdev->dev, "%s pci_save_state returned=%d\n",
- __func__, retval);
- /* disable PM notifications */
pci_enable_wake(pdev, PCI_D3hot, 0);
- dev_dbg(&pdev->dev,
- "%s pci_enable_wake invoked successfully\n", __func__);
- /* disable PCI device */
pci_disable_device(pdev);
- dev_dbg(&pdev->dev,
- "%s pci_disable_device invoked successfully\n",
- __func__);
- /* move device to D3hot state */
pci_set_power_state(pdev, PCI_D3hot);
- dev_dbg(&pdev->dev,
- "%s pci_set_power_state invoked successfully\n",
- __func__);
} else {
dev_err(&pdev->dev, "%s pci_save_state failed\n", __func__);
}
- dev_dbg(&pdev->dev, "%s return=%d\n", __func__, retval);
-
return retval;
}
static int pch_spi_resume(struct pci_dev *pdev)
{
int retval;
-
- struct pch_spi_board_data *board = pci_get_drvdata(pdev);
+ struct pch_pd_dev_save *pd_dev_save = pci_get_drvdata(pdev);
dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
- if (!board) {
- dev_err(&pdev->dev,
- "%s pci_get_drvdata returned NULL\n", __func__);
- return -EFAULT;
- }
-
- /* move device to DO power state */
pci_set_power_state(pdev, PCI_D0);
-
- /* restore state */
pci_restore_state(pdev);
retval = pci_enable_device(pdev);
@@ -1242,34 +1664,12 @@ static int pch_spi_resume(struct pci_dev *pdev)
dev_err(&pdev->dev,
"%s pci_enable_device failed\n", __func__);
} else {
- /* disable PM notifications */
pci_enable_wake(pdev, PCI_D3hot, 0);
- /* register IRQ handler */
- if (!board->irq_reg_sts) {
- /* register IRQ */
- retval = request_irq(board->pdev->irq, pch_spi_handler,
- IRQF_SHARED, KBUILD_MODNAME,
- board);
- if (retval < 0) {
- dev_err(&pdev->dev,
- "%s request_irq failed\n", __func__);
- return retval;
- }
- board->irq_reg_sts = true;
-
- /* reset PCH SPI h/w */
- pch_spi_reset(board->data->master);
- pch_spi_set_master_mode(board->data->master);
-
- /* set suspend status to false */
- board->suspend_sts = false;
-
- }
+ /* set suspend status to false */
+ pd_dev_save->board_dat->suspend_sts = false;
}
- dev_dbg(&pdev->dev, "%s returning=%d\n", __func__, retval);
-
return retval;
}
#else
@@ -1289,15 +1689,29 @@ static struct pci_driver pch_spi_pcidev = {
static int __init pch_spi_init(void)
{
- return pci_register_driver(&pch_spi_pcidev);
+ int ret;
+ ret = platform_driver_register(&pch_spi_pd_driver);
+ if (ret)
+ return ret;
+
+ ret = pci_register_driver(&pch_spi_pcidev);
+ if (ret)
+ return ret;
+
+ return 0;
}
module_init(pch_spi_init);
static void __exit pch_spi_exit(void)
{
pci_unregister_driver(&pch_spi_pcidev);
+ platform_driver_unregister(&pch_spi_pd_driver);
}
module_exit(pch_spi_exit);
+module_param(use_dma, int, 0644);
+MODULE_PARM_DESC(use_dma,
+ "to use DMA for data transfers pass 1 else 0; default 1");
+
MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Topcliff PCH SPI PCI Driver");
+MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7xxx IOH SPI Driver");
diff --git a/drivers/spi/spi_txx9.c b/drivers/spi/spi-txx9.c
index dfa024b633e1..f0a2ab0428a3 100644
--- a/drivers/spi/spi_txx9.c
+++ b/drivers/spi/spi-txx9.c
@@ -1,5 +1,5 @@
/*
- * spi_txx9.c - TXx9 SPI controller driver.
+ * TXx9 SPI controller driver.
*
* Based on linux/arch/mips/tx4938/toshiba_rbtx4938/spi_txx9.c
* Copyright (C) 2000-2001 Toshiba Corporation
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/spi-xilinx.c
index 4d2c75df886c..4d2c75df886c 100644
--- a/drivers/spi/xilinx_spi.c
+++ b/drivers/spi/spi-xilinx.c
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 2e13a14bba3f..4d1b9f517ce8 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1,5 +1,5 @@
/*
- * spi.c - SPI init/core code
+ * SPI init/core code
*
* Copyright (C) 2005 David Brownell
*
diff --git a/drivers/spi/spi_s3c24xx_gpio.c b/drivers/spi/spi_s3c24xx_gpio.c
deleted file mode 100644
index be991359bf92..000000000000
--- a/drivers/spi/spi_s3c24xx_gpio.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/* linux/drivers/spi/spi_s3c24xx_gpio.c
- *
- * Copyright (c) 2006 Ben Dooks
- * Copyright (c) 2006 Simtec Electronics
- *
- * S3C24XX GPIO based SPI driver
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
-*/
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/workqueue.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-
-#include <linux/spi/spi.h>
-#include <linux/spi/spi_bitbang.h>
-
-#include <mach/regs-gpio.h>
-#include <mach/spi-gpio.h>
-#include <mach/hardware.h>
-
-struct s3c2410_spigpio {
- struct spi_bitbang bitbang;
-
- struct s3c2410_spigpio_info *info;
- struct platform_device *dev;
-};
-
-static inline struct s3c2410_spigpio *spidev_to_sg(struct spi_device *spi)
-{
- return spi_master_get_devdata(spi->master);
-}
-
-static inline void setsck(struct spi_device *dev, int on)
-{
- struct s3c2410_spigpio *sg = spidev_to_sg(dev);
- s3c2410_gpio_setpin(sg->info->pin_clk, on ? 1 : 0);
-}
-
-static inline void setmosi(struct spi_device *dev, int on)
-{
- struct s3c2410_spigpio *sg = spidev_to_sg(dev);
- s3c2410_gpio_setpin(sg->info->pin_mosi, on ? 1 : 0);
-}
-
-static inline u32 getmiso(struct spi_device *dev)
-{
- struct s3c2410_spigpio *sg = spidev_to_sg(dev);
- return s3c2410_gpio_getpin(sg->info->pin_miso) ? 1 : 0;
-}
-
-#define spidelay(x) ndelay(x)
-
-#include "spi_bitbang_txrx.h"
-
-
-static u32 s3c2410_spigpio_txrx_mode0(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits)
-{
- return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits);
-}
-
-static u32 s3c2410_spigpio_txrx_mode1(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits)
-{
- return bitbang_txrx_be_cpha1(spi, nsecs, 0, 0, word, bits);
-}
-
-static u32 s3c2410_spigpio_txrx_mode2(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits)
-{
- return bitbang_txrx_be_cpha0(spi, nsecs, 1, 0, word, bits);
-}
-
-static u32 s3c2410_spigpio_txrx_mode3(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits)
-{
- return bitbang_txrx_be_cpha1(spi, nsecs, 1, 0, word, bits);
-}
-
-
-static void s3c2410_spigpio_chipselect(struct spi_device *dev, int value)
-{
- struct s3c2410_spigpio *sg = spidev_to_sg(dev);
-
- if (sg->info && sg->info->chip_select)
- (sg->info->chip_select)(sg->info, value);
-}
-
-static int s3c2410_spigpio_probe(struct platform_device *dev)
-{
- struct s3c2410_spigpio_info *info;
- struct spi_master *master;
- struct s3c2410_spigpio *sp;
- int ret;
-
- master = spi_alloc_master(&dev->dev, sizeof(struct s3c2410_spigpio));
- if (master == NULL) {
- dev_err(&dev->dev, "failed to allocate spi master\n");
- ret = -ENOMEM;
- goto err;
- }
-
- sp = spi_master_get_devdata(master);
-
- platform_set_drvdata(dev, sp);
-
- /* copy in the plkatform data */
- info = sp->info = dev->dev.platform_data;
-
- /* setup spi bitbang adaptor */
- sp->bitbang.master = spi_master_get(master);
- sp->bitbang.master->bus_num = info->bus_num;
- sp->bitbang.master->num_chipselect = info->num_chipselect;
- sp->bitbang.chipselect = s3c2410_spigpio_chipselect;
-
- sp->bitbang.txrx_word[SPI_MODE_0] = s3c2410_spigpio_txrx_mode0;
- sp->bitbang.txrx_word[SPI_MODE_1] = s3c2410_spigpio_txrx_mode1;
- sp->bitbang.txrx_word[SPI_MODE_2] = s3c2410_spigpio_txrx_mode2;
- sp->bitbang.txrx_word[SPI_MODE_3] = s3c2410_spigpio_txrx_mode3;
-
- /* set state of spi pins, always assume that the clock is
- * available, but do check the MOSI and MISO. */
- s3c2410_gpio_setpin(info->pin_clk, 0);
- s3c2410_gpio_cfgpin(info->pin_clk, S3C2410_GPIO_OUTPUT);
-
- if (info->pin_mosi < S3C2410_GPH10) {
- s3c2410_gpio_setpin(info->pin_mosi, 0);
- s3c2410_gpio_cfgpin(info->pin_mosi, S3C2410_GPIO_OUTPUT);
- }
-
- if (info->pin_miso != S3C2410_GPA0 && info->pin_miso < S3C2410_GPH10)
- s3c2410_gpio_cfgpin(info->pin_miso, S3C2410_GPIO_INPUT);
-
- ret = spi_bitbang_start(&sp->bitbang);
- if (ret)
- goto err_no_bitbang;
-
- return 0;
-
- err_no_bitbang:
- spi_master_put(sp->bitbang.master);
- err:
- return ret;
-
-}
-
-static int s3c2410_spigpio_remove(struct platform_device *dev)
-{
- struct s3c2410_spigpio *sp = platform_get_drvdata(dev);
-
- spi_bitbang_stop(&sp->bitbang);
- spi_master_put(sp->bitbang.master);
-
- return 0;
-}
-
-/* all gpio should be held over suspend/resume, so we should
- * not need to deal with this
-*/
-
-#define s3c2410_spigpio_suspend NULL
-#define s3c2410_spigpio_resume NULL
-
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:spi_s3c24xx_gpio");
-
-static struct platform_driver s3c2410_spigpio_drv = {
- .probe = s3c2410_spigpio_probe,
- .remove = s3c2410_spigpio_remove,
- .suspend = s3c2410_spigpio_suspend,
- .resume = s3c2410_spigpio_resume,
- .driver = {
- .name = "spi_s3c24xx_gpio",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init s3c2410_spigpio_init(void)
-{
- return platform_driver_register(&s3c2410_spigpio_drv);
-}
-
-static void __exit s3c2410_spigpio_exit(void)
-{
- platform_driver_unregister(&s3c2410_spigpio_drv);
-}
-
-module_init(s3c2410_spigpio_init);
-module_exit(s3c2410_spigpio_exit);
-
-MODULE_DESCRIPTION("S3C24XX SPI Driver");
-MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index d9fd86211365..830adbed1d7a 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -1,5 +1,5 @@
/*
- * spidev.c -- simple synchronous userspace interface to SPI devices
+ * Simple synchronous userspace interface to SPI devices
*
* Copyright (C) 2006 SWAPP
* Andrea Paterniani <a.paterniani@swapp-eng.it>