All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.