linux-spi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Fabio Estevam <festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
Cc: Fabio Estevam
	<fabio.estevam-KZfg59tc24xl57MIdRCFDg@public.gmane.org>,
	snijsure-4jo+YWezP1RWk0Htik3J/w@public.gmane.org,
	marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
	kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org,
	shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org
Subject: [PATCH 2/2] spi: Add initial support for spi-mxs
Date: Wed, 18 Apr 2012 21:30:34 -0300	[thread overview]
Message-ID: <1334795434-8780-2-git-send-email-festevam@gmail.com> (raw)
In-Reply-To: <1334795434-8780-1-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Add initial support for the spi driver on mxs processors.

Currently only PIO mode is supported.

Tested with a sst25vf016b spi flash on a mx28evk board using mtd-utils.

Signed-off-by: Fabio Estevam <fabio.estevam-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
---
It still does not contain DT support, but I wanted to post it as is, so
that people can test it and I would also like to get some initial feedback.

 arch/arm/mach-mxs/include/mach/ssp-regs.h |   32 ++
 drivers/spi/Kconfig                       |    6 +
 drivers/spi/Makefile                      |    1 +
 drivers/spi/spi-mxs.c                     |  457 +++++++++++++++++++++++++++++
 4 files changed, 496 insertions(+), 0 deletions(-)
 create mode 100644 drivers/spi/spi-mxs.c

diff --git a/arch/arm/mach-mxs/include/mach/ssp-regs.h b/arch/arm/mach-mxs/include/mach/ssp-regs.h
index 4bb0b27..fc467fa 100644
--- a/arch/arm/mach-mxs/include/mach/ssp-regs.h
+++ b/arch/arm/mach-mxs/include/mach/ssp-regs.h
@@ -27,6 +27,10 @@
 
 /* SSP registers */
 #define HW_SSP_CTRL0				0x000
+#define HW_SSP_CTRL0_SET			0x00000004
+#define HW_SSP_CTRL0_CLR			0x00000008
+#define HW_SSP_CTRL0_TOG			0x0000000c
+#define BM_SSP_CTRL0_LOCK_CS			0x08000000
 #define BM_SSP_CTRL0_RUN			(1 << 29)
 #define BM_SSP_CTRL0_SDIO_IRQ_CHECK		(1 << 28)
 #define BM_SSP_CTRL0_IGNORE_CRC			(1 << 26)
@@ -41,6 +45,10 @@
 #define BP_SSP_CTRL0_XFER_COUNT			0
 #define BM_SSP_CTRL0_XFER_COUNT			0xffff
 #define HW_SSP_CMD0				0x010
+#define HW_SSP_CMD0_SET				0x014
+#define HW_SSP_CMD0_CLR				0x018
+#define HW_SSP_CMD0_TOG				0x01c
+
 #define BM_SSP_CMD0_DBL_DATA_RATE_EN		(1 << 25)
 #define BM_SSP_CMD0_SLOW_CLKING_EN		(1 << 22)
 #define BM_SSP_CMD0_CONT_CLKING_EN		(1 << 21)
@@ -63,8 +71,12 @@
 #define BM_SSP_TIMING_TIMEOUT			(0xffff << 16)
 #define BP_SSP_TIMING_CLOCK_DIVIDE		8
 #define BM_SSP_TIMING_CLOCK_DIVIDE		(0xff << 8)
+#define BF_SSP_TIMING_CLOCK_DIVIDE(v)  \
+		(((v) << 8) & BM_SSP_TIMING_CLOCK_DIVIDE)
 #define BP_SSP_TIMING_CLOCK_RATE		0
 #define BM_SSP_TIMING_CLOCK_RATE		0xff
+#define BF_SSP_TIMING_CLOCK_RATE(v)  \
+		(((v) << 0) & BM_SSP_TIMING_CLOCK_RATE)
 #define HW_SSP_CTRL1				(ssp_is_old() ? 0x060 : 0x080)
 #define BM_SSP_CTRL1_SDIO_IRQ			(1 << 31)
 #define BM_SSP_CTRL1_SDIO_IRQ_EN		(1 << 30)
@@ -83,11 +95,30 @@
 #define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ		(1 << 15)
 #define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN	(1 << 14)
 #define BM_SSP_CTRL1_DMA_ENABLE			(1 << 13)
+#define BM_SSP_CTRL1_PHASE			0x00000400
 #define BM_SSP_CTRL1_POLARITY			(1 << 9)
 #define BP_SSP_CTRL1_WORD_LENGTH		4
 #define BM_SSP_CTRL1_WORD_LENGTH		(0xf << 4)
+#define BF_SSP_CTRL1_WORD_LENGTH(v)  \
+		(((v) << 4) & BM_SSP_CTRL1_WORD_LENGTH)
+#define BV_SSP_CTRL1_WORD_LENGTH__RESERVED0	0x0
+#define BV_SSP_CTRL1_WORD_LENGTH__RESERVED1	0x1
+#define BV_SSP_CTRL1_WORD_LENGTH__RESERVED2	0x2
+#define BV_SSP_CTRL1_WORD_LENGTH__FOUR_BITS	0x3
+#define BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS	0x7
+#define BV_SSP_CTRL1_WORD_LENGTH__SIXTEEN_BITS	0xF
+#define BM_SSP_CTRL0_WAIT_FOR_CMD		0x00100000
 #define BP_SSP_CTRL1_SSP_MODE			0
 #define BM_SSP_CTRL1_SSP_MODE			0xf
+#define BF_SSP_CTRL1_SSP_MODE(v)  \
+		(((v) << 0) & BM_SSP_CTRL1_SSP_MODE)
+#define BV_SSP_CTRL1_SSP_MODE__SPI		0x0
+#define BV_SSP_CTRL1_SSP_MODE__SSI		0x1
+#define BV_SSP_CTRL1_SSP_MODE__SD_MMC		0x3
+#define BV_SSP_CTRL1_SSP_MODE__MS		0x4
+
+#define HW_SSP_DATA				0x090
+
 #define HW_SSP_SDRESP0				(ssp_is_old() ? 0x080 : 0x0a0)
 #define HW_SSP_SDRESP1				(ssp_is_old() ? 0x090 : 0x0b0)
 #define HW_SSP_SDRESP2				(ssp_is_old() ? 0x0a0 : 0x0c0)
@@ -95,6 +126,7 @@
 #define HW_SSP_STATUS				(ssp_is_old() ? 0x0c0 : 0x100)
 #define BM_SSP_STATUS_CARD_DETECT		(1 << 28)
 #define BM_SSP_STATUS_SDIO_IRQ			(1 << 17)
+#define BM_SSP_STATUS_FIFO_EMPTY		0x00000020
 #define HW_SSP_VERSION				(cpu_is_mx23() ? 0x110 : 0x130)
 #define BP_SSP_VERSION_MAJOR			24
 
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 3ed7483..f951604 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -199,6 +199,12 @@ config SPI_MPC512x_PSC
 	  This enables using the Freescale MPC5121 Programmable Serial
 	  Controller in SPI master mode.
 
+config SPI_MXS
+	tristate "Freescale MXS SPI controller"
+	depends on ARCH_MXS
+	help
+	   SPI driver for Freescale MXS devices
+
 config SPI_FSL_LIB
 	tristate
 	depends on FSL_SOC
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index a1d48e0..0e6fe03 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -35,6 +35,7 @@ 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_MXS)			+= spi-mxs.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
diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c
new file mode 100644
index 0000000..3550ab6
--- /dev/null
+++ b/drivers/spi/spi-mxs.c
@@ -0,0 +1,457 @@
+/*
+ * Freescale MXS SPI master driver
+ *
+ * Heavily based on spi-stmp.c, which is:
+ * Author: dmitry pervushin <dimka-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
+ *
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <mach/mxs.h>
+#include <mach/ssp-regs.h>
+#include <mach/common.h>
+
+#define SSP_TIMEOUT		200	/* 200 ms */
+
+#define rev_struct		(ss->version)
+
+struct mxs_spi {
+	void __iomem *regs;	/* vaddr of the control registers */
+
+	u32 speed_khz;
+	u32 divider;
+
+	struct clk *clk;
+	struct device *master_dev;
+
+	struct work_struct work;
+	struct workqueue_struct *workqueue;
+	spinlock_t lock;
+	struct list_head queue;
+
+	u32 version;
+};
+
+static int mxs_spi_setup_transfer(struct spi_device *spi,
+				  struct spi_transfer *t)
+{
+	u8 bits_per_word;
+	u32 hz;
+	struct mxs_spi *ss;
+	u16 rate;
+
+	ss = spi_master_get_devdata(spi->master);
+
+	bits_per_word = spi->bits_per_word;
+	if (t && t->bits_per_word)
+		bits_per_word = t->bits_per_word;
+/*
+ * Calculate speed:
+ * - by default, use maximum speed from ssp clk
+ * - if device overrides it, use it
+ * - if transfer specifies other speed, use transfer's one
+ */
+	hz = 1000 * ss->speed_khz / ss->divider;
+	if (spi->max_speed_hz)
+		hz = min(hz, spi->max_speed_hz);
+	if (t && t->speed_hz)
+		hz = min(hz, t->speed_hz);
+
+	if (hz == 0) {
+		dev_err(&spi->dev, "Cannot continue with zero clock\n");
+		return -EINVAL;
+	}
+
+	if (bits_per_word != 8) {
+		dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
+			__func__, bits_per_word);
+		return -EINVAL;
+	}
+
+	dev_dbg(&spi->dev, "Requested clk rate = %uHz, max = %ukHz/%d = %uHz\n",
+		hz, ss->speed_khz, ss->divider,
+		ss->speed_khz * 1000 / ss->divider);
+
+	if (ss->speed_khz * 1000 / ss->divider < hz) {
+		dev_err(&spi->dev, "%s, unsupported clock rate %uHz\n",
+			__func__, hz);
+		return -EINVAL;
+	}
+
+	rate = 1000 * ss->speed_khz / ss->divider / hz;
+
+	__raw_writel(BF_SSP_TIMING_CLOCK_DIVIDE(ss->divider) |
+		     BF_SSP_TIMING_CLOCK_RATE(rate - 1),
+		     ss->regs + HW_SSP_TIMING);
+
+	__raw_writel(BF_SSP_CTRL1_SSP_MODE(BV_SSP_CTRL1_SSP_MODE__SPI) |
+		     BF_SSP_CTRL1_WORD_LENGTH
+		     (BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) |
+		     ((spi->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) |
+		     ((spi->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0),
+		     ss->regs + HW_SSP_CTRL1);
+
+	__raw_writel(0x0, ss->regs + HW_SSP_CMD0_SET);
+
+	return 0;
+}
+
+static void mxs_spi_cleanup(struct spi_device *spi)
+{
+	return;
+}
+
+/* the spi->mode bits understood by this driver: */
+#define MODEBITS (SPI_CPOL | SPI_CPHA)
+static int mxs_spi_setup(struct spi_device *spi)
+{
+	struct mxs_spi *ss;
+	int err = 0;
+
+	ss = spi_master_get_devdata(spi->master);
+
+	if (!spi->bits_per_word)
+		spi->bits_per_word = 8;
+
+	if (spi->mode & ~MODEBITS)
+		return -EINVAL;
+
+	err = mxs_spi_setup_transfer(spi, NULL);
+	if (err)
+		dev_err(&spi->dev, "Failed to setup transfer: %d\n", err);
+
+	return err;
+}
+
+static inline u32 mxs_spi_cs(unsigned cs)
+{
+	return ((cs & 1) ? BM_SSP_CTRL0_WAIT_FOR_CMD : 0) |
+	    ((cs & 2) ? BM_SSP_CTRL0_WAIT_FOR_IRQ : 0);
+}
+
+static inline void mxs_spi_enable(struct mxs_spi *ss)
+{
+	__raw_writel(BM_SSP_CTRL0_LOCK_CS, ss->regs + HW_SSP_CTRL0_SET);
+	__raw_writel(BM_SSP_CTRL0_IGNORE_CRC, ss->regs + HW_SSP_CTRL0_CLR);
+}
+
+static inline void mxs_spi_disable(struct mxs_spi *ss)
+{
+	__raw_writel(BM_SSP_CTRL0_LOCK_CS, ss->regs + HW_SSP_CTRL0_CLR);
+	__raw_writel(BM_SSP_CTRL0_IGNORE_CRC, ss->regs + HW_SSP_CTRL0_SET);
+}
+
+int mxs_ssp_wait_set(struct mxs_spi *ss, int offset, int mask)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT);
+
+	while (!(readl_relaxed(&ss->regs + offset) & mask)) {
+		udelay(1);
+		if (time_after(jiffies, timeout))
+			return -ETIMEDOUT;
+	}
+	return 0;
+}
+
+int mxs_ssp_wait_clr(struct mxs_spi *ss, int offset, int mask)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT);
+
+	while ((readl_relaxed(&ss->regs + offset) & mask)) {
+		udelay(1);
+		if (time_after(jiffies, timeout))
+			return -ETIMEDOUT;
+	}
+	return 0;
+}
+
+static int mxs_spi_txrx_pio(struct mxs_spi *ss, int cs,
+			    unsigned char *buf, int len,
+			    int *first, int *last, int write)
+{
+	if (*first) {
+		mxs_spi_enable(ss);
+		*first = 0;
+	}
+
+	__raw_writel(mxs_spi_cs(cs), ss->regs + HW_SSP_CTRL0_SET);
+
+	while (len--) {
+		if (*last && len == 0) {
+			mxs_spi_disable(ss);
+			*last = 0;
+		}
+
+		if (ss->version > 3) {
+			__raw_writel(1, ss->regs + HW_SSP_XFER_SIZE);
+		} else {
+			__raw_writel(BM_SSP_CTRL0_XFER_COUNT,
+				     ss->regs + HW_SSP_CTRL0_CLR);
+			__raw_writel(1, ss->regs + HW_SSP_CTRL0_SET);
+		}
+
+		if (write)
+			__raw_writel(BM_SSP_CTRL0_READ,
+				     ss->regs + HW_SSP_CTRL0_CLR);
+		else
+			__raw_writel(BM_SSP_CTRL0_READ,
+				     ss->regs + HW_SSP_CTRL0_SET);
+
+		 /* Activate Run bit */
+		__raw_writel(BM_SSP_CTRL0_RUN, ss->regs + HW_SSP_CTRL0_SET);
+
+
+		if (mxs_ssp_wait_set(ss->regs, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN))
+			return -ETIMEDOUT;
+
+		if (write)
+			__raw_writel(*buf, ss->regs + HW_SSP_DATA);
+
+		/* Set TRANSFER */
+		__raw_writel(BM_SSP_CTRL0_DATA_XFER,
+			     ss->regs + HW_SSP_CTRL0_SET);
+
+		if (!write) {
+			if (mxs_ssp_wait_clr(ss->regs, HW_SSP_STATUS,
+						BM_SSP_STATUS_FIFO_EMPTY))
+				return -ETIMEDOUT;
+
+			*buf = (__raw_readl(ss->regs + HW_SSP_DATA) & 0xFF);
+		}
+
+		if (mxs_ssp_wait_clr(ss->regs, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN))
+			return -ETIMEDOUT;
+		/* advance to the next byte */
+		buf++;
+	}
+	return len < 0 ? 0 : -ETIMEDOUT;
+}
+
+static int mxs_spi_handle_message(struct mxs_spi *ss, struct spi_message *m)
+{
+	int first, last;
+	struct spi_transfer *t, *tmp_t;
+	int status = 0;
+	int cs;
+
+	first = last = 0;
+
+	cs = m->spi->chip_select;
+
+	list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) {
+
+		mxs_spi_setup_transfer(m->spi, t);
+
+		if (&t->transfer_list == m->transfers.next)
+			first = !0;
+		if (&t->transfer_list == m->transfers.prev)
+			last = !0;
+		if (t->rx_buf && t->tx_buf) {
+			pr_debug("%s: cannot send and receive simultaneously\n",
+				 __func__);
+			return -EINVAL;
+		}
+		/*
+		 *  REVISIT:
+		 *  here driver completely ignores setting of t->cs_change
+		 */
+		if (t->tx_buf)
+			status = mxs_spi_txrx_pio(ss, cs, (void *)t->tx_buf,
+					     t->len, &first, &last, 1);
+		if (t->rx_buf)
+			status = mxs_spi_txrx_pio(ss, cs, t->rx_buf,
+					     t->len, &first, &last, 0);
+		m->actual_length += t->len;
+		if (status)
+			break;
+
+		first = last = 0;
+	}
+	return status;
+}
+
+/*
+ * mxs_spi_handle
+ *
+ * The workhorse of the driver - it handles messages from the list
+ */
+static void mxs_spi_handle(struct work_struct *w)
+{
+	struct mxs_spi *ss = container_of(w, struct mxs_spi, work);
+	unsigned long flags;
+	struct spi_message *m;
+
+	BUG_ON(w == NULL);
+
+	spin_lock_irqsave(&ss->lock, flags);
+	while (!list_empty(&ss->queue)) {
+		m = list_entry(ss->queue.next, struct spi_message, queue);
+		list_del_init(&m->queue);
+		spin_unlock_irqrestore(&ss->lock, flags);
+
+		m->status = mxs_spi_handle_message(ss, m);
+		if (m->complete)
+			m->complete(m->context);
+
+		spin_lock_irqsave(&ss->lock, flags);
+	}
+	spin_unlock_irqrestore(&ss->lock, flags);
+
+	return;
+}
+
+/*
+ * mxs_spi_transfer
+ *
+ * Called indirectly from spi_async, queues all the messages to
+ * spi_handle_message
+ *
+ */
+static int mxs_spi_transfer(struct spi_device *spi, struct spi_message *m)
+{
+	struct mxs_spi *ss = spi_master_get_devdata(spi->master);
+	unsigned long flags;
+
+	m->actual_length = 0;
+	m->status = -EINPROGRESS;
+	spin_lock_irqsave(&ss->lock, flags);
+	list_add_tail(&m->queue, &ss->queue);
+	queue_work(ss->workqueue, &ss->work);
+	spin_unlock_irqrestore(&ss->lock, flags);
+
+	return 0;
+}
+
+static int __devinit mxs_spi_probe(struct platform_device *dev)
+{
+	int ret;
+	struct spi_master *master;
+	struct mxs_spi *ss;
+	struct resource *res;
+
+	master = spi_alloc_master(&dev->dev, sizeof(struct mxs_spi));
+	ss = spi_master_get_devdata(master);
+	ss->master_dev = &dev->dev;
+
+	if (!master)
+		return -ENOMEM;
+
+	platform_set_drvdata(dev, master);
+
+	INIT_WORK(&ss->work, mxs_spi_handle);
+	INIT_LIST_HEAD(&ss->queue);
+	spin_lock_init(&ss->lock);
+	ss->workqueue = create_singlethread_workqueue(dev_name(&dev->dev));
+	master->transfer = mxs_spi_transfer;
+	master->setup = mxs_spi_setup;
+	master->cleanup = mxs_spi_cleanup;
+	master->mode_bits = MODEBITS;
+
+	master->bus_num = dev->id;
+	master->num_chipselect = 1;
+
+	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENOENT;
+
+	ss->regs = devm_request_and_ioremap(&dev->dev, res);
+	if (!ss->regs)
+		return -EBUSY;
+
+	mxs_reset_block(ss->regs);
+	ss->clk = clk_get(&dev->dev, NULL);
+	if (IS_ERR(ss->clk)) {
+		ret = PTR_ERR(ss->clk);
+		dev_err(&dev->dev, "cannot get spi clk\n");
+		goto out_put_master;
+	}
+
+	clk_prepare_enable(ss->clk);
+
+	ss->speed_khz = clk_get_rate(ss->clk) / 1000;
+	ss->divider = 2;
+	dev_dbg(&dev->dev, "Max possible speed %d = %ld/%d kHz\n",
+		 ss->speed_khz, clk_get_rate(ss->clk), ss->divider);
+
+	ss->version = __raw_readl(ss->regs + HW_SSP_VERSION) >> 24;
+
+	ret = spi_register_master(master);
+	if (ret) {
+		dev_err(&dev->dev, "cannot register spi master, %d\n", ret);
+		goto out_clk_put;
+	}
+
+	dev_info(&dev->dev, "driver probed\n");
+
+	return 0;
+
+out_clk_put:
+	clk_disable_unprepare(ss->clk);
+	clk_put(ss->clk);
+out_put_master:
+	spi_master_put(master);
+	if (ss->workqueue)
+		destroy_workqueue(ss->workqueue);
+	platform_set_drvdata(dev, NULL);
+
+	return ret;
+}
+
+static int __devexit mxs_spi_remove(struct platform_device *dev)
+{
+	struct mxs_spi *ss;
+	struct spi_master *master;
+
+	master = platform_get_drvdata(dev);
+	if (!master)
+		goto out0;
+	ss = spi_master_get_devdata(master);
+
+	clk_disable_unprepare(ss->clk);
+	clk_put(ss->clk);
+	spi_unregister_master(master);
+
+	destroy_workqueue(ss->workqueue);
+	spi_master_put(master);
+	platform_set_drvdata(dev, NULL);
+out0:
+	return 0;
+}
+
+static struct platform_driver mxs_spi_driver = {
+	.probe = mxs_spi_probe,
+	.remove = __devexit_p(mxs_spi_remove),
+	.driver = {
+		   .name = "mxs-spi",
+		   .owner = THIS_MODULE,
+		   },
+};
+module_platform_driver(mxs_spi_driver);
+
+MODULE_AUTHOR("dmitry pervushin <dimka-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>");
+MODULE_AUTHOR("Fabio Estevam <fabio.estevam-KZfg59tc24xl57MIdRCFDg@public.gmane.org");
+MODULE_DESCRIPTION("MXS SPI master driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mxs-spi");
-- 
1.7.1


------------------------------------------------------------------------------
For Developers, A Lot Can Happen In A Second.
Boundary is the first to Know...and Tell You.
Monitor Your Applications in Ultra-Fine Resolution. Try it FREE!
http://p.sf.net/sfu/Boundary-d2dvs2

  parent reply	other threads:[~2012-04-19  0:30 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-04-19  0:30 [PATCH 1/2] ARM: mxs: Provide a common header file for SSP controller Fabio Estevam
     [not found] ` <1334795434-8780-1-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2012-04-19  0:30   ` Fabio Estevam [this message]
     [not found]     ` <1334795434-8780-2-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2012-04-20  4:12       ` [PATCH 2/2] spi: Add initial support for spi-mxs Shawn Guo
2012-04-20  2:59   ` [PATCH 1/2] ARM: mxs: Provide a common header file for SSP controller Shawn Guo
     [not found]     ` <20120420025918.GI22219-rvtDTF3kK1ictlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2012-04-20  3:07       ` Fabio Estevam
     [not found]         ` <CAOMZO5AOmAA93LLQnzHrOf+prH5rmNw7C-ZSpYzpKGUrZ74G1g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-04-20  3:15           ` Shawn Guo
     [not found]             ` <CAAQ0ZWSj3-wWm0HGBPwAhCzZvSxDob6w2cgb1FWzp+K=Ev5BNA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-04-20 16:30               ` Marek Vasut

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1334795434-8780-2-git-send-email-festevam@gmail.com \
    --to=festevam-re5jqeeqqe8avxtiumwx3w@public.gmane.org \
    --cc=fabio.estevam-KZfg59tc24xl57MIdRCFDg@public.gmane.org \
    --cc=kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org \
    --cc=marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
    --cc=shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org \
    --cc=snijsure-4jo+YWezP1RWk0Htik3J/w@public.gmane.org \
    --cc=spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).