linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/3] OMAP SSI driver
@ 2013-08-11 16:13 Sebastian Reichel
  2013-08-11 16:17 ` [PATCH 1/3] ARM: OMAP2+: hwmod-data: Add SSI information Sebastian Reichel
  0 siblings, 1 reply; 15+ messages in thread
From: Sebastian Reichel @ 2013-08-11 16:13 UTC (permalink / raw)
  To: linux-kernel
  Cc: Sebastian Reichel, linux-omap, linus.walleij, Kevin Hilman,
	Shubhrajyoti Datta, Carlos Chinea

Hi !

This patchset adds an OMAP SSI driver to the HSI framework.

After leaving Nokia Carlos Chinea had no more time to work on this patchset;
Thus I tried updating the driver to fix the mentioned issues.

I applied the following changes to the last patch from Carlos:

 * convert driver to use hwmod and add SSI information to hwmod database
 * convert driver to use runtime_pm
 * change platform data, so that it forwards the wake_gpio to avoid irq_to_gpio
 * move the ssi.h and rename to hsi-omap-ssi.h (multiplatform support)

The code compiles and is initialized on the RX51. I also verified, that it
does not break the boot on my Pandaboard.

The driver has been written by Carlos Chinea and has already been sent for
public review as part of the HSI framework RFC before:

 https://lkml.org/lkml/2011/6/10/280

-- Sebastian

Sebastian Reichel (3):
  ARM: OMAP2+: hwmod-data: Add SSI information
  ARM: OMAP2+: HSI: Introduce OMAP SSI driver
  ARM: OMAP2+: Add SSI driver configuration

 arch/arm/mach-omap2/Makefile                 |    4 +
 arch/arm/mach-omap2/board-rx51-peripherals.c |   10 +-
 arch/arm/mach-omap2/omap_hwmod_3xxx_data.c   |  104 ++
 arch/arm/mach-omap2/ssi.c                    |   82 ++
 drivers/hsi/Kconfig                          |    1 +
 drivers/hsi/Makefile                         |    1 +
 drivers/hsi/controllers/Kconfig              |   23 +
 drivers/hsi/controllers/Makefile             |    5 +
 drivers/hsi/controllers/omap_ssi.c           | 1879 ++++++++++++++++++++++++++
 include/linux/platform_data/hsi-omap-ssi.h   |  202 +++
 10 files changed, 2310 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/mach-omap2/ssi.c
 create mode 100644 drivers/hsi/controllers/Kconfig
 create mode 100644 drivers/hsi/controllers/Makefile
 create mode 100644 drivers/hsi/controllers/omap_ssi.c
 create mode 100644 include/linux/platform_data/hsi-omap-ssi.h

-- 
1.7.10.4


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 1/3] ARM: OMAP2+: hwmod-data: Add SSI information
  2013-08-11 16:13 [RFC PATCH 0/3] OMAP SSI driver Sebastian Reichel
@ 2013-08-11 16:17 ` Sebastian Reichel
  2013-08-11 16:17   ` [PATCH 2/3] ARM: OMAP2+: HSI: Introduce OMAP SSI driver Sebastian Reichel
                     ` (3 more replies)
  0 siblings, 4 replies; 15+ messages in thread
From: Sebastian Reichel @ 2013-08-11 16:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-omap, linus.walleij, Kevin Hilman, Shubhrajyoti Datta,
	Carlos Chinea, Sebastian Reichel

From: Sebastian Reichel <sre@ring0.de>

This patch adds Synchronous Serial Interface (SSI) hwmod support for
OMAP34xx SoCs.
---
 arch/arm/mach-omap2/omap_hwmod_3xxx_data.c |  104 ++++++++++++++++++++++++++++
 1 file changed, 104 insertions(+)

diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index 0c3a427..74006c4 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -3693,6 +3693,109 @@ static struct omap_hwmod_ocp_if omap3xxx_l4_core__aes = {
 	.user		= OCP_USER_MPU | OCP_USER_SDMA,
 };
 
+/*
+ * 'ssi' class
+ * synchronous serial interface (multichannel and full-duplex serial if)
+ */
+
+static struct omap_hwmod_class_sysconfig omap34xx_ssi_sysc = {
+	.rev_offs	= 0x0000,
+	.sysc_offs	= 0x0010,
+	.syss_offs	= 0x0014,
+	.sysc_flags	= (SYSC_HAS_AUTOIDLE | SYSC_HAS_EMUFREE |
+			   SYSC_HAS_MIDLEMODE | SYSC_HAS_SIDLEMODE |
+			   SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
+	.idlemodes	= (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+			   SIDLE_SMART_WKUP | MSTANDBY_FORCE | MSTANDBY_NO |
+			   MSTANDBY_SMART | MSTANDBY_SMART_WKUP),
+	.sysc_fields	= &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap34xx_ssi_hwmod_class = {
+	.name	= "ssi",
+	.sysc	= &omap34xx_ssi_sysc,
+};
+
+static struct omap_hwmod_irq_info omap34xx_ssi_irqs[] = {
+	{ .name = "ssi_p1_mpu_irq0", .irq = 67 },
+	{ .name = "ssi_p1_mpu_irq1", .irq = 68 },
+	{ .name = "ssi_p2_mpu_irq0", .irq = 69 },
+	{ .name = "ssi_p2_mpu_irq1", .irq = 70 },
+	{ .name = "ssi_gdd_mpu",     .irq = 71 },
+	{ .irq = -1 },
+};
+
+static struct omap_hwmod_addr_space omap34xx_ssi_addrs[] = {
+	{
+		.name		= "sys",
+		.pa_start	= 0x48058000,
+		.pa_end		= 0x48058fff,
+		.flags		= ADDR_TYPE_RT,
+	},
+	{
+		/* generic distributed DMA */
+		.name		= "gdd",
+		.pa_start	= 0x48059000,
+		.pa_end		= 0x48059fff,
+		.flags		= ADDR_TYPE_RT,
+	},
+	{
+		/* port 1: synchronous serial transmitter */
+		.name		= "p1_sst",
+		.pa_start	= 0x4805a000,
+		.pa_end		= 0x4805a7ff,
+		.flags		= ADDR_TYPE_RT,
+	},
+	{
+		/* port 1: synchronous serial receiver */
+		.name		= "p1_ssr",
+		.pa_start	= 0x4805a800,
+		.pa_end		= 0x4805afff,
+		.flags		= ADDR_TYPE_RT,
+	},
+	{
+		/* port 2: synchronous serial transmitter */
+		.name		= "p2_sst",
+		.pa_start	= 0x4805b000,
+		.pa_end		= 0x4805b7ff,
+		.flags		= ADDR_TYPE_RT,
+	},
+	{
+		/* port 2: synchronous serial receiver */
+		.name		= "p2_ssr",
+		.pa_start	= 0x4805b800,
+		.pa_end		= 0x4805bfff,
+		.flags		= ADDR_TYPE_RT,
+	},
+	{}
+};
+
+static struct omap_hwmod omap34xx_ssi_hwmod = {
+	.name		= "ssi",
+	.class		= &omap34xx_ssi_hwmod_class,
+	.clkdm_name	= "l3_init_clkdm",
+	.mpu_irqs	= omap34xx_ssi_irqs,
+	.main_clk	= "ssi_ssr_fck",
+	.prcm		= {
+		.omap2 = {
+			.prcm_reg_id		= 1,
+			.module_bit		= OMAP3430_EN_SSI_SHIFT,
+			.module_offs		= WKUP_MOD,
+			.idlest_reg_id		= 1,
+			.idlest_idle_bit	= OMAP3430ES2_ST_SSI_IDLE_SHIFT,
+		},
+	},
+};
+
+/* SSI -> l3 */
+static struct omap_hwmod_ocp_if omap34xx_l3__ssi = {
+	.master		= &omap3xxx_l3_main_hwmod,
+	.slave		= &omap34xx_ssi_hwmod,
+	.clk		= "ssi_ick",
+	.addr		= omap34xx_ssi_addrs,
+	.user		= OCP_USER_MPU | OCP_USER_SDMA,
+};
+
 static struct omap_hwmod_ocp_if *omap3xxx_hwmod_ocp_ifs[] __initdata = {
 	&omap3xxx_l3_main__l4_core,
 	&omap3xxx_l3_main__l4_per,
@@ -3818,6 +3921,7 @@ static struct omap_hwmod_ocp_if *omap34xx_hwmod_ocp_ifs[] __initdata = {
 #ifdef CONFIG_OMAP_IOMMU_IVA2
 	&omap3xxx_l3_main__mmu_iva,
 #endif
+	&omap34xx_l3__ssi,
 	NULL
 };
 
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 2/3] ARM: OMAP2+: HSI: Introduce OMAP SSI driver
  2013-08-11 16:17 ` [PATCH 1/3] ARM: OMAP2+: hwmod-data: Add SSI information Sebastian Reichel
@ 2013-08-11 16:17   ` Sebastian Reichel
  2013-08-12  8:28     ` Tony Lindgren
  2013-08-23 13:57     ` Linus Walleij
  2013-08-11 16:17   ` [PATCH 3/3] ARM: OMAP2+: Add SSI driver configuration Sebastian Reichel
                     ` (2 subsequent siblings)
  3 siblings, 2 replies; 15+ messages in thread
From: Sebastian Reichel @ 2013-08-11 16:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-omap, linus.walleij, Kevin Hilman, Shubhrajyoti Datta,
	Carlos Chinea, Sebastian Reichel

From: Sebastian Reichel <sre@ring0.de>

This patch adds an OMAP SSI driver to the HSI framework.

The Synchronous Serial Interface (SSI) is a legacy version
of HSI. As in the case of HSI, it is mainly used to connect
Application engines (APE) with cellular modem engines (CMT)
in cellular handsets.

It provides a multichannel, full-duplex, multi-core communication
with no reference clock. The OMAP SSI block is capable of reaching
speeds of 110 Mbit/s.
---
 drivers/hsi/Kconfig                        |    1 +
 drivers/hsi/Makefile                       |    1 +
 drivers/hsi/controllers/Kconfig            |   23 +
 drivers/hsi/controllers/Makefile           |    5 +
 drivers/hsi/controllers/omap_ssi.c         | 1879 ++++++++++++++++++++++++++++
 include/linux/platform_data/hsi-omap-ssi.h |  202 +++
 6 files changed, 2111 insertions(+)
 create mode 100644 drivers/hsi/controllers/Kconfig
 create mode 100644 drivers/hsi/controllers/Makefile
 create mode 100644 drivers/hsi/controllers/omap_ssi.c
 create mode 100644 include/linux/platform_data/hsi-omap-ssi.h

diff --git a/drivers/hsi/Kconfig b/drivers/hsi/Kconfig
index d94e38d..2c76de4 100644
--- a/drivers/hsi/Kconfig
+++ b/drivers/hsi/Kconfig
@@ -14,6 +14,7 @@ config HSI_BOARDINFO
 	bool
 	default y
 
+source "drivers/hsi/controllers/Kconfig"
 source "drivers/hsi/clients/Kconfig"
 
 endif # HSI
diff --git a/drivers/hsi/Makefile b/drivers/hsi/Makefile
index 9d5d33f..360371e 100644
--- a/drivers/hsi/Makefile
+++ b/drivers/hsi/Makefile
@@ -3,4 +3,5 @@
 #
 obj-$(CONFIG_HSI_BOARDINFO)	+= hsi_boardinfo.o
 obj-$(CONFIG_HSI)		+= hsi.o
+obj-y				+= controllers/
 obj-y				+= clients/
diff --git a/drivers/hsi/controllers/Kconfig b/drivers/hsi/controllers/Kconfig
new file mode 100644
index 0000000..3efe0f0
--- /dev/null
+++ b/drivers/hsi/controllers/Kconfig
@@ -0,0 +1,23 @@
+#
+# HSI controllers configuration
+#
+comment "HSI controllers"
+
+config OMAP_SSI
+	tristate "OMAP SSI hardware driver"
+	depends on ARCH_OMAP && HSI
+	default n
+	---help---
+	  SSI is a legacy version of HSI. It is usually used to connect
+	  an application engine with a cellular modem.
+	  If you say Y here, you will enable the OMAP SSI hardware driver.
+
+	  If unsure, say N.
+
+if OMAP_SSI
+
+config OMAP_SSI_CONFIG
+	boolean
+	default y
+
+endif # OMAP_SSI
diff --git a/drivers/hsi/controllers/Makefile b/drivers/hsi/controllers/Makefile
new file mode 100644
index 0000000..c4ba2c2
--- /dev/null
+++ b/drivers/hsi/controllers/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for HSI controllers drivers
+#
+
+obj-$(CONFIG_OMAP_SSI)		+= omap_ssi.o
diff --git a/drivers/hsi/controllers/omap_ssi.c b/drivers/hsi/controllers/omap_ssi.c
new file mode 100644
index 0000000..edac3d3
--- /dev/null
+++ b/drivers/hsi/controllers/omap_ssi.c
@@ -0,0 +1,1879 @@
+/*
+ * omap_ssi.c
+ *
+ * Implements the OMAP SSI driver.
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Carlos Chinea <carlos.chinea@nokia.com>
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <linux/compiler.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/omap-dma.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/scatterlist.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/hsi/hsi.h>
+
+#include <linux/platform_data/hsi-omap-ssi.h>
+
+#define SSI_MAX_CHANNELS	8
+#define SSI_MAX_GDD_LCH		8
+#define SSI_BYTES_TO_FRAMES(x) ((((x) - 1) >> 2) + 1)
+
+/**
+ * struct gdd_trn - GDD transaction data
+ * @msg: Pointer to the HSI message being served
+ * @sg: Pointer to the current sg entry being served
+ */
+struct gdd_trn {
+	struct hsi_msg		*msg;
+	struct scatterlist	*sg;
+};
+
+/**
+ * struct omap_ssm_ctx - OMAP synchronous serial module (TX/RX) context
+ * @mode: Bit transmission mode
+ * @channels: Number of channels
+ * @framesize: Frame size in bits
+ * @timeout: RX frame timeout
+ * @divisor: TX divider
+ * @arb_mode: Arbitration mode for TX frame (Round robin, priority)
+ */
+struct omap_ssm_ctx {
+	u32	mode;
+	u32	channels;
+	u32	frame_size;
+	union	{
+			u32	timeout; /* Rx Only */
+			struct	{
+					u32	arb_mode;
+					u32	divisor;
+			}; /* Tx only */
+	};
+};
+
+/**
+ * struct omap_ssi_port - OMAP SSI port data
+ * @dev: device associated to the port (HSI port)
+ * @sst_dma: SSI transmitter physical base address
+ * @ssr_dma: SSI receiver physical base address
+ * @sst_base: SSI transmitter base address
+ * @ssr_base: SSI receiver base address
+ * @wk_lock: spin lock to serialize access to the wake lines
+ * @lock: Spin lock to serialize access to the SSI port
+ * @channels: Current number of channels configured (1,2,4 or 8)
+ * @txqueue: TX message queues
+ * @rxqueue: RX message queues
+ * @brkqueue: Queue of incoming HWBREAK requests (FRAME mode)
+ * @irq: IRQ number
+ * @wake_irq: IRQ number for incoming wake line (-1 if none)
+ * @wake_gpio: GPIO number for incoming wake line (-1 if none)
+ * @pio_tasklet: Bottom half for PIO transfers and events
+ * @wake_tasklet: Bottom half for incoming wake events
+ * @wkin_cken: Keep track of clock references due to the incoming wake line
+ * @wk_refcount: Reference count for output wake line
+ * @sys_mpu_enable: Context for the interrupt enable register for irq 0
+ * @sst: Context for the synchronous serial transmitter
+ * @ssr: Context for the synchronous serial receiver
+ */
+struct omap_ssi_port {
+	struct device		*dev;
+	dma_addr_t		sst_dma;
+	dma_addr_t		ssr_dma;
+	void __iomem		*sst_base;
+	void __iomem		*ssr_base;
+	spinlock_t		wk_lock;
+	spinlock_t		lock;
+	unsigned int		channels;
+	struct list_head	txqueue[SSI_MAX_CHANNELS];
+	struct list_head	rxqueue[SSI_MAX_CHANNELS];
+	struct list_head	brkqueue;
+	unsigned int		irq;
+	int			wake_irq;
+	int			wake_gpio;
+	struct tasklet_struct	pio_tasklet;
+	struct tasklet_struct	wake_tasklet;
+	unsigned int		wktest:1; /* FIXME: HACK to be removed */
+	unsigned int		wkin_cken:1; /* Workaround */
+	int			wk_refcount;
+	/* OMAP SSI port context */
+	u32			sys_mpu_enable; /* We use only one irq */
+	struct omap_ssm_ctx	sst;
+	struct omap_ssm_ctx	ssr;
+};
+
+/**
+ * struct omap_ssi_controller - OMAP SSI controller data
+ * @dev: device associated to the controller (HSI controller)
+ * @sys: SSI I/O base address
+ * @gdd: GDD I/O base address
+ * @ick: SSI interconnect clock
+ * @fck: SSI functional clock
+ * @ck_refcount: References count for clocks
+ * @gdd_irq: IRQ line for GDD
+ * @gdd_tasklet: bottom half for DMA transfers
+ * @gdd_trn: Array of GDD transaction data for ongoing GDD transfers
+ * @lock: lock to serialize access to GDD
+ * @ck_lock: lock to serialize access to the clocks
+ * @loss_count: To follow if we need to restore context or not
+ * @max_speed: Maximum TX speed (Kb/s) set by the clients.
+ * @sysconfig: SSI controller saved context
+ * @gdd_gcr: SSI GDD saved context
+ * @get_loss: Pointer to omap_pm_get_dev_context_loss_count, if any
+ * @port: Array of pointers of the ports of the controller
+ * @dir: Debugfs SSI root directory
+ */
+struct omap_ssi_controller {
+	struct device		*dev;
+	void __iomem		*sys;
+	void __iomem		*gdd;
+	struct clk		*ick;
+	struct clk		*fck;
+	int			ck_refcount;
+	unsigned int		gdd_irq;
+	struct tasklet_struct	gdd_tasklet;
+	struct gdd_trn		gdd_trn[SSI_MAX_GDD_LCH];
+	spinlock_t		lock;
+	spinlock_t		ck_lock;
+	unsigned long		fck_rate;
+	u32			loss_count;
+	u32			max_speed;
+	/* OMAP SSI Controller context */
+	u32			sysconfig;
+	u32			gdd_gcr;
+	int			(*get_loss)(struct device *dev);
+	struct omap_ssi_port	**port;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dir;
+#endif
+};
+
+static inline unsigned int ssi_wakein(struct hsi_port *port)
+{
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	return gpio_get_value(omap_port->wake_gpio);
+}
+
+static int ssi_for_each_port(struct hsi_controller *ssi, void *data,
+			int (*fn)(struct omap_ssi_port *p, void *data))
+{
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	unsigned int i = 0;
+	int err = 0;
+
+	for (i = 0; ((i < ssi->num_ports) && !err); i++)
+		err = (*fn)(omap_ssi->port[i], data);
+
+	return err;
+}
+
+static int ssi_set_port_mode(struct omap_ssi_port *omap_port, void *data)
+{
+	u32 *mode = data;
+
+	__raw_writel(*mode, omap_port->sst_base + SSI_SST_MODE_REG);
+	__raw_writel(*mode, omap_port->ssr_base + SSI_SSR_MODE_REG);
+	/* OCP barrier */
+	*mode = __raw_readl(omap_port->ssr_base + SSI_SSR_MODE_REG);
+
+	return 0;
+}
+
+static inline void ssi_set_mode(struct hsi_controller *ssi, u32 mode)
+{
+	ssi_for_each_port(ssi, &mode, ssi_set_port_mode);
+}
+
+static int ssi_restore_port_mode(struct omap_ssi_port *omap_port,
+						void *data __maybe_unused)
+{
+	u32 mode;
+
+	__raw_writel(omap_port->sst.mode,
+				omap_port->sst_base + SSI_SST_MODE_REG);
+	__raw_writel(omap_port->ssr.mode,
+				omap_port->ssr_base + SSI_SSR_MODE_REG);
+	/* OCP barrier */
+	mode =  __raw_readl(omap_port->ssr_base + SSI_SSR_MODE_REG);
+
+	return 0;
+}
+
+static int ssi_restore_divisor(struct omap_ssi_port *omap_port,
+						void *data __maybe_unused)
+{
+	__raw_writel(omap_port->sst.divisor,
+				omap_port->sst_base + SSI_SST_DIVISOR_REG);
+
+	return 0;
+}
+
+static int ssi_restore_port_ctx(struct omap_ssi_port *omap_port,
+						void *data __maybe_unused)
+{
+	struct hsi_port *port = to_hsi_port(omap_port->dev);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	void __iomem	*base = omap_port->sst_base;
+
+	__raw_writel(omap_port->sys_mpu_enable,
+			omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+	/* SST context */
+	__raw_writel(omap_port->sst.frame_size, base + SSI_SST_FRAMESIZE_REG);
+	__raw_writel(omap_port->sst.channels, base + SSI_SST_CHANNELS_REG);
+	__raw_writel(omap_port->sst.arb_mode, base + SSI_SST_ARBMODE_REG);
+	/* SSR context */
+	base = omap_port->ssr_base;
+	__raw_writel(omap_port->ssr.frame_size, base + SSI_SSR_FRAMESIZE_REG);
+	__raw_writel(omap_port->ssr.channels, base + SSI_SSR_CHANNELS_REG);
+	__raw_writel(omap_port->ssr.timeout, base + SSI_SSR_TIMEOUT_REG);
+
+	return 0;
+}
+
+static int ssi_save_port_ctx(struct omap_ssi_port *omap_port,
+						void *data __maybe_unused)
+{
+	struct hsi_port *port = to_hsi_port(omap_port->dev);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+	omap_port->sys_mpu_enable = __raw_readl(omap_ssi->sys +
+					SSI_MPU_ENABLE_REG(port->num, 0));
+
+	return 0;
+}
+
+static int ssi_clk_enable(struct hsi_controller *ssi)
+{
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	struct device *pdev = ssi->device.parent;
+	int err = 0;
+
+	spin_lock_bh(&omap_ssi->ck_lock);
+
+	if (omap_ssi->ck_refcount++)
+		goto out;
+
+	err = pm_runtime_get_sync(pdev);
+	if (unlikely(err < 0))
+		goto out;
+
+	if ((omap_ssi->get_loss) && (omap_ssi->loss_count ==
+				(*omap_ssi->get_loss)(ssi->device.parent)))
+		goto mode; /* We always need to restore the mode & TX divisor */
+
+	__raw_writel(omap_ssi->sysconfig, omap_ssi->sys + SSI_SYSCONFIG_REG);
+	__raw_writel(omap_ssi->gdd_gcr, omap_ssi->gdd + SSI_GDD_GCR_REG);
+
+	ssi_for_each_port(ssi, NULL, ssi_restore_port_ctx);
+mode:
+	ssi_for_each_port(ssi, NULL, ssi_restore_divisor);
+	ssi_for_each_port(ssi, NULL, ssi_restore_port_mode);
+out:
+	spin_unlock_bh(&omap_ssi->ck_lock);
+
+	return err;
+}
+
+static void ssi_clk_disable(struct hsi_controller *ssi)
+{
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	struct device *pdev = ssi->device.parent;
+
+	spin_lock_bh(&omap_ssi->ck_lock);
+	WARN_ON(omap_ssi->ck_refcount <= 0);
+	if (--omap_ssi->ck_refcount)
+		goto out;
+
+	ssi_set_mode(ssi, SSI_MODE_SLEEP);
+
+	if (omap_ssi->get_loss)
+		omap_ssi->loss_count =
+				(*omap_ssi->get_loss)(ssi->device.parent);
+
+	ssi_for_each_port(ssi, NULL, ssi_save_port_ctx);
+
+	spin_unlock_bh(&omap_ssi->ck_lock);
+
+	pm_runtime_put_sync(pdev);
+	return;
+
+out:
+	spin_unlock_bh(&omap_ssi->ck_lock);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int ssi_debug_show(struct seq_file *m, void *p __maybe_unused)
+{
+	struct hsi_controller *ssi = m->private;
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	void __iomem	*sys = omap_ssi->sys;
+
+	ssi_clk_enable(ssi);
+	seq_printf(m, "REVISION\t: 0x%08x\n",
+					__raw_readl(sys + SSI_REVISION_REG));
+	seq_printf(m, "SYSCONFIG\t: 0x%08x\n",
+					__raw_readl(sys + SSI_SYSCONFIG_REG));
+	seq_printf(m, "SYSSTATUS\t: 0x%08x\n",
+					__raw_readl(sys + SSI_SYSSTATUS_REG));
+	ssi_clk_disable(ssi);
+
+	return 0;
+}
+
+static int ssi_debug_port_show(struct seq_file *m, void *p __maybe_unused)
+{
+	struct hsi_port *port = m->private;
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	void __iomem	*base = omap_ssi->sys;
+	unsigned int ch;
+
+	ssi_clk_enable(ssi);
+	if (omap_port->wake_irq > 0)
+		seq_printf(m, "CAWAKE\t\t: %d\n", ssi_wakein(port));
+	seq_printf(m, "WAKE\t\t: 0x%08x\n",
+				__raw_readl(base + SSI_WAKE_REG(port->num)));
+	seq_printf(m, "MPU_ENABLE_IRQ%d\t: 0x%08x\n", 0,
+			__raw_readl(base + SSI_MPU_ENABLE_REG(port->num, 0)));
+	seq_printf(m, "MPU_STATUS_IRQ%d\t: 0x%08x\n", 0,
+			__raw_readl(base + SSI_MPU_STATUS_REG(port->num, 0)));
+	/* SST */
+	base = omap_port->sst_base;
+	seq_printf(m, "\nSST\n===\n");
+	seq_printf(m, "ID SST\t\t: 0x%08x\n",
+				__raw_readl(base + SSI_SST_ID_REG));
+	seq_printf(m, "MODE\t\t: 0x%08x\n",
+				__raw_readl(base + SSI_SST_MODE_REG));
+	seq_printf(m, "FRAMESIZE\t: 0x%08x\n",
+				__raw_readl(base + SSI_SST_FRAMESIZE_REG));
+	seq_printf(m, "DIVISOR\t\t: 0x%08x\n",
+				__raw_readl(base + SSI_SST_DIVISOR_REG));
+	seq_printf(m, "CHANNELS\t: 0x%08x\n",
+				__raw_readl(base + SSI_SST_CHANNELS_REG));
+	seq_printf(m, "ARBMODE\t\t: 0x%08x\n",
+				__raw_readl(base + SSI_SST_ARBMODE_REG));
+	seq_printf(m, "TXSTATE\t\t: 0x%08x\n",
+				__raw_readl(base + SSI_SST_TXSTATE_REG));
+	seq_printf(m, "BUFSTATE\t: 0x%08x\n",
+				__raw_readl(base + SSI_SST_BUFSTATE_REG));
+	seq_printf(m, "BREAK\t\t: 0x%08x\n",
+				__raw_readl(base + SSI_SST_BREAK_REG));
+	for (ch = 0; ch < omap_port->channels; ch++) {
+		seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch,
+				__raw_readl(base + SSI_SST_BUFFER_CH_REG(ch)));
+	}
+	/* SSR */
+	base = omap_port->ssr_base;
+	seq_printf(m, "\nSSR\n===\n");
+	seq_printf(m, "ID SSR\t\t: 0x%08x\n",
+				__raw_readl(base + SSI_SSR_ID_REG));
+	seq_printf(m, "MODE\t\t: 0x%08x\n",
+				__raw_readl(base + SSI_SSR_MODE_REG));
+	seq_printf(m, "FRAMESIZE\t: 0x%08x\n",
+				__raw_readl(base + SSI_SSR_FRAMESIZE_REG));
+	seq_printf(m, "CHANNELS\t: 0x%08x\n",
+				__raw_readl(base + SSI_SSR_CHANNELS_REG));
+	seq_printf(m, "TIMEOUT\t\t: 0x%08x\n",
+				__raw_readl(base + SSI_SSR_TIMEOUT_REG));
+	seq_printf(m, "RXSTATE\t\t: 0x%08x\n",
+				__raw_readl(base + SSI_SSR_RXSTATE_REG));
+	seq_printf(m, "BUFSTATE\t: 0x%08x\n",
+				__raw_readl(base + SSI_SSR_BUFSTATE_REG));
+	seq_printf(m, "BREAK\t\t: 0x%08x\n",
+				__raw_readl(base + SSI_SSR_BREAK_REG));
+	seq_printf(m, "ERROR\t\t: 0x%08x\n",
+				__raw_readl(base + SSI_SSR_ERROR_REG));
+	seq_printf(m, "ERRORACK\t: 0x%08x\n",
+				__raw_readl(base + SSI_SSR_ERRORACK_REG));
+	for (ch = 0; ch < omap_port->channels; ch++) {
+		seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch,
+				__raw_readl(base + SSI_SSR_BUFFER_CH_REG(ch)));
+	}
+	ssi_clk_disable(ssi);
+
+	return 0;
+}
+
+static int ssi_debug_gdd_show(struct seq_file *m, void *p __maybe_unused)
+{
+	struct hsi_controller *ssi = m->private;
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	void __iomem	*gdd = omap_ssi->gdd;
+	int lch;
+
+	ssi_clk_enable(ssi);
+	seq_printf(m, "GDD_MPU_STATUS\t: 0x%08x\n",
+		__raw_readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_STATUS_REG));
+	seq_printf(m, "GDD_MPU_ENABLE\t: 0x%08x\n\n",
+		__raw_readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG));
+	seq_printf(m, "HW_ID\t\t: 0x%08x\n",
+				__raw_readl(gdd + SSI_GDD_HW_ID_REG));
+	seq_printf(m, "PPORT_ID\t: 0x%08x\n",
+				__raw_readl(gdd + SSI_GDD_PPORT_ID_REG));
+	seq_printf(m, "MPORT_ID\t: 0x%08x\n",
+				__raw_readl(gdd + SSI_GDD_MPORT_ID_REG));
+	seq_printf(m, "TEST\t\t: 0x%08x\n",
+				__raw_readl(gdd + SSI_GDD_TEST_REG));
+	seq_printf(m, "GCR\t\t: 0x%08x\n",
+				__raw_readl(gdd + SSI_GDD_GCR_REG));
+
+	for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) {
+		seq_printf(m, "\nGDD LCH %d\n=========\n", lch);
+		seq_printf(m, "CSDP\t\t: 0x%04x\n",
+				__raw_readw(gdd + SSI_GDD_CSDP_REG(lch)));
+		seq_printf(m, "CCR\t\t: 0x%04x\n",
+				__raw_readw(gdd + SSI_GDD_CCR_REG(lch)));
+		seq_printf(m, "CICR\t\t: 0x%04x\n",
+				__raw_readw(gdd + SSI_GDD_CICR_REG(lch)));
+		seq_printf(m, "CSR\t\t: 0x%04x\n",
+				__raw_readw(gdd + SSI_GDD_CSR_REG(lch)));
+		seq_printf(m, "CSSA\t\t: 0x%08x\n",
+				__raw_readl(gdd + SSI_GDD_CSSA_REG(lch)));
+		seq_printf(m, "CDSA\t\t: 0x%08x\n",
+				__raw_readl(gdd + SSI_GDD_CDSA_REG(lch)));
+		seq_printf(m, "CEN\t\t: 0x%04x\n",
+				__raw_readw(gdd + SSI_GDD_CEN_REG(lch)));
+		seq_printf(m, "CSAC\t\t: 0x%04x\n",
+				__raw_readw(gdd + SSI_GDD_CSAC_REG(lch)));
+		seq_printf(m, "CDAC\t\t: 0x%04x\n",
+				__raw_readw(gdd + SSI_GDD_CDAC_REG(lch)));
+		seq_printf(m, "CLNK_CTRL\t: 0x%04x\n",
+				__raw_readw(gdd + SSI_GDD_CLNK_CTRL_REG(lch)));
+	}
+	ssi_clk_disable(ssi);
+
+	return 0;
+}
+
+static int ssi_div_get(void *data, u64 *val)
+{
+	struct hsi_port *port = data;
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+
+	ssi_clk_enable(ssi);
+	*val = __raw_readl(omap_port->sst_base + SSI_SST_DIVISOR_REG);
+	ssi_clk_disable(ssi);
+
+	return 0;
+}
+
+static int ssi_div_set(void *data, u64 val)
+{
+	struct hsi_port *port = data;
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+
+	if (val > 127)
+		return -EINVAL;
+
+	ssi_clk_enable(ssi);
+	__raw_writel(val, omap_port->sst_base + SSI_SST_DIVISOR_REG);
+	omap_port->sst.divisor = val;
+	ssi_clk_disable(ssi);
+
+	return 0;
+}
+
+static int ssi_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ssi_debug_show, inode->i_private);
+}
+
+static int ssi_port_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ssi_debug_port_show, inode->i_private);
+}
+
+static int ssi_gdd_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ssi_debug_gdd_show, inode->i_private);
+}
+
+static const struct file_operations ssi_regs_fops = {
+	.open		= ssi_regs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static const struct file_operations ssi_port_regs_fops = {
+	.open		= ssi_port_regs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static const struct file_operations ssi_gdd_regs_fops = {
+	.open		= ssi_gdd_regs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+DEFINE_SIMPLE_ATTRIBUTE(ssi_sst_div_fops, ssi_div_get, ssi_div_set, "%llu\n");
+
+static int __init ssi_debug_add_port(struct omap_ssi_port *omap_port,
+								void *data)
+{
+	struct hsi_port *port = to_hsi_port(omap_port->dev);
+	struct dentry *dir = data;
+
+	dir = debugfs_create_dir(dev_name(omap_port->dev), dir);
+	if (IS_ERR(dir))
+		return PTR_ERR(dir);
+	debugfs_create_file("regs", S_IRUGO, dir, port, &ssi_port_regs_fops);
+	dir = debugfs_create_dir("sst", dir);
+	if (IS_ERR(dir))
+		return PTR_ERR(dir);
+	debugfs_create_file("divisor", S_IRUGO | S_IWUSR, dir, port,
+							&ssi_sst_div_fops);
+
+	return 0;
+}
+
+static int __init ssi_debug_add_ctrl(struct hsi_controller *ssi)
+{
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	struct dentry *dir;
+	int err;
+
+	/* SSI controller */
+	omap_ssi->dir = debugfs_create_dir(dev_name(&ssi->device), NULL);
+	if (IS_ERR(omap_ssi->dir))
+		return PTR_ERR(omap_ssi->dir);
+
+	debugfs_create_file("regs", S_IRUGO, omap_ssi->dir, ssi,
+								&ssi_regs_fops);
+	/* SSI GDD (DMA) */
+	dir = debugfs_create_dir("gdd", omap_ssi->dir);
+	if (IS_ERR(dir))
+		goto rback;
+	debugfs_create_file("regs", S_IRUGO, dir, ssi, &ssi_gdd_regs_fops);
+	/* SSI ports */
+	err = ssi_for_each_port(ssi, omap_ssi->dir, ssi_debug_add_port);
+	if (err < 0)
+		goto rback;
+
+	return 0;
+rback:
+	debugfs_remove_recursive(omap_ssi->dir);
+
+	return PTR_ERR(dir);
+}
+
+static void ssi_debug_remove_ctrl(struct hsi_controller *ssi)
+{
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+	debugfs_remove_recursive(omap_ssi->dir);
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static int ssi_claim_lch(struct hsi_msg *msg)
+{
+
+	struct hsi_port *port = hsi_get_port(msg->cl);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	int lch;
+
+	for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++)
+		if (!omap_ssi->gdd_trn[lch].msg) {
+			omap_ssi->gdd_trn[lch].msg = msg;
+			omap_ssi->gdd_trn[lch].sg = msg->sgt.sgl;
+			return lch;
+		}
+
+	return -EBUSY;
+}
+
+static int ssi_start_pio(struct hsi_msg *msg)
+{
+	struct hsi_port *port = hsi_get_port(msg->cl);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	u32 val;
+
+	ssi_clk_enable(ssi);
+	if (msg->ttype == HSI_MSG_WRITE) {
+		val = SSI_DATAACCEPT(msg->channel);
+		ssi_clk_enable(ssi); /* Hold clocks for pio writes */
+	} else {
+		val = SSI_DATAAVAILABLE(msg->channel) | SSI_ERROROCCURED;
+	}
+	dev_dbg(&port->device, "Single %s transfer\n",
+						msg->ttype ? "write" : "read");
+	val |= __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+	__raw_writel(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+	ssi_clk_disable(ssi);
+	msg->actual_len = 0;
+	msg->status = HSI_STATUS_PROCEEDING;
+
+	return 0;
+}
+
+static int ssi_start_dma(struct hsi_msg *msg, int lch)
+{
+	struct hsi_port *port = hsi_get_port(msg->cl);
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	void __iomem *gdd = omap_ssi->gdd;
+	int err;
+	u16 csdp;
+	u16 ccr;
+	u32 s_addr;
+	u32 d_addr;
+	u32 tmp;
+
+	if (msg->ttype == HSI_MSG_READ) {
+		err = dma_map_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents,
+							DMA_FROM_DEVICE);
+		if (err < 0) {
+			dev_dbg(&ssi->device, "DMA map SG failed !\n");
+			return err;
+		}
+		csdp = SSI_DST_BURST_4x32_BIT | SSI_DST_MEMORY_PORT |
+			SSI_SRC_SINGLE_ACCESS0 | SSI_SRC_PERIPHERAL_PORT |
+			SSI_DATA_TYPE_S32;
+		ccr = msg->channel + 0x10 + (port->num * 8); /* Sync */
+		ccr |= SSI_DST_AMODE_POSTINC | SSI_SRC_AMODE_CONST |
+			SSI_CCR_ENABLE;
+		s_addr = omap_port->ssr_dma +
+					SSI_SSR_BUFFER_CH_REG(msg->channel);
+		d_addr = sg_dma_address(msg->sgt.sgl);
+	} else {
+		err = dma_map_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents,
+							DMA_TO_DEVICE);
+		if (err < 0) {
+			dev_dbg(&ssi->device, "DMA map SG failed !\n");
+			return err;
+		}
+		csdp = SSI_SRC_BURST_4x32_BIT | SSI_SRC_MEMORY_PORT |
+			SSI_DST_SINGLE_ACCESS0 | SSI_DST_PERIPHERAL_PORT |
+			SSI_DATA_TYPE_S32;
+		ccr = (msg->channel + 1 + (port->num * 8)) & 0xf; /* Sync */
+		ccr |= SSI_SRC_AMODE_POSTINC | SSI_DST_AMODE_CONST |
+			SSI_CCR_ENABLE;
+		s_addr = sg_dma_address(msg->sgt.sgl);
+		d_addr = omap_port->sst_dma +
+					SSI_SST_BUFFER_CH_REG(msg->channel);
+	}
+	dev_dbg(&ssi->device, "lch %d cdsp %08x ccr %04x s_addr %08x"
+			" d_addr %08x\n", lch, csdp, ccr, s_addr, d_addr);
+	ssi_clk_enable(ssi); /* Hold clocks during the transfer */
+	__raw_writew(csdp, gdd + SSI_GDD_CSDP_REG(lch));
+	__raw_writew(SSI_BLOCK_IE | SSI_TOUT_IE, gdd + SSI_GDD_CICR_REG(lch));
+	__raw_writel(d_addr, gdd + SSI_GDD_CDSA_REG(lch));
+	__raw_writel(s_addr, gdd + SSI_GDD_CSSA_REG(lch));
+	__raw_writew(SSI_BYTES_TO_FRAMES(msg->sgt.sgl->length),
+						gdd + SSI_GDD_CEN_REG(lch));
+
+	spin_lock_bh(&omap_ssi->lock);
+	tmp = __raw_readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+	tmp |= SSI_GDD_LCH(lch);
+	__raw_writel(tmp, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+	spin_unlock_bh(&omap_ssi->lock);
+	__raw_writew(ccr, gdd + SSI_GDD_CCR_REG(lch));
+	msg->status = HSI_STATUS_PROCEEDING;
+
+	return 0;
+}
+
+static int ssi_start_transfer(struct list_head *queue)
+{
+	struct hsi_msg *msg;
+	int lch = -1;
+
+	if (list_empty(queue))
+		return 0;
+	msg = list_first_entry(queue, struct hsi_msg, link);
+	if (msg->status != HSI_STATUS_QUEUED)
+		return 0;
+	if ((msg->sgt.nents) && (msg->sgt.sgl->length > sizeof(u32)))
+		lch = ssi_claim_lch(msg);
+	if (lch >= 0)
+		return ssi_start_dma(msg, lch);
+	else
+		return ssi_start_pio(msg);
+}
+
+static void ssi_transfer(struct omap_ssi_port *omap_port,
+							struct list_head *queue)
+{
+	struct hsi_msg *msg;
+	int err = -1;
+
+	spin_lock_bh(&omap_port->lock);
+	while (err < 0) {
+		err = ssi_start_transfer(queue);
+		if (err < 0) {
+			msg = list_first_entry(queue, struct hsi_msg, link);
+			msg->status = HSI_STATUS_ERROR;
+			msg->actual_len = 0;
+			list_del(&msg->link);
+			spin_unlock_bh(&omap_port->lock);
+			msg->complete(msg);
+			spin_lock_bh(&omap_port->lock);
+		}
+	}
+	spin_unlock_bh(&omap_port->lock);
+}
+
+static u32 ssi_calculate_div(struct hsi_controller *ssi)
+{
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	u32 tx_fckrate = (u32) omap_ssi->fck_rate;
+
+	/* / 2 : SSI TX clock is always half of the SSI functional clock */
+	tx_fckrate >>= 1;
+	/* Round down when tx_fckrate % omap_ssi->max_speed == 0 */
+	tx_fckrate--;
+	dev_dbg(&ssi->device, "TX div %d for fck_rate %lu Khz speed %d Kb/s\n",
+			tx_fckrate / omap_ssi->max_speed, omap_ssi->fck_rate,
+							omap_ssi->max_speed);
+
+	return tx_fckrate / omap_ssi->max_speed;
+}
+
+/*
+ * FIXME: Horrible HACK needed until we remove the useless wakeline test
+ * in the CMT. To be removed !!!!
+ */
+void ssi_waketest(struct hsi_client *cl, unsigned int enable)
+{
+	struct hsi_port *port = hsi_get_port(cl);
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+	omap_port->wktest = !!enable;
+	if (omap_port->wktest) {
+		ssi_clk_enable(ssi);
+		__raw_writel(SSI_WAKE(0),
+				omap_ssi->sys + SSI_SET_WAKE_REG(port->num));
+	} else {
+		__raw_writel(SSI_WAKE(0),
+				omap_ssi->sys +	SSI_CLEAR_WAKE_REG(port->num));
+		ssi_clk_disable(ssi);
+	}
+}
+EXPORT_SYMBOL_GPL(ssi_waketest);
+
+static void ssi_error(struct hsi_port *port)
+{
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	struct hsi_msg *msg;
+	unsigned int i;
+	u32 err;
+	u32 val;
+	u32 tmp;
+
+	/* ACK error */
+	err = __raw_readl(omap_port->ssr_base + SSI_SSR_ERROR_REG);
+	dev_err(&port->device, "SSI error: 0x%02x\n", err);
+	if (!err) {
+		dev_dbg(&port->device, "spurious SSI error ignored!\n");
+		return;
+	}
+	spin_lock(&omap_ssi->lock);
+	/* Cancel all GDD read transfers */
+	for (i = 0, val = 0; i < SSI_MAX_GDD_LCH; i++) {
+		msg = omap_ssi->gdd_trn[i].msg;
+		if ((msg) && (msg->ttype == HSI_MSG_READ)) {
+			__raw_writew(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i));
+			val |= (1 << i);
+			omap_ssi->gdd_trn[i].msg = NULL;
+		}
+	}
+	tmp = __raw_readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+	tmp &= ~val;
+	__raw_writel(tmp, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+	spin_unlock(&omap_ssi->lock);
+	/* Cancel all PIO read transfers */
+	spin_lock(&omap_port->lock);
+	tmp = __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+	tmp &= 0xfeff00ff; /* Disable error & all dataavailable interrupts */
+	__raw_writel(tmp, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+	/* ACK error */
+	__raw_writel(err, omap_port->ssr_base + SSI_SSR_ERRORACK_REG);
+	__raw_writel(SSI_ERROROCCURED,
+			omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
+	/* Signal the error all current pending read requests */
+	for (i = 0; i < omap_port->channels; i++) {
+		if (list_empty(&omap_port->rxqueue[i]))
+			continue;
+		msg = list_first_entry(&omap_port->rxqueue[i], struct hsi_msg,
+									link);
+		list_del(&msg->link);
+		msg->status = HSI_STATUS_ERROR;
+		spin_unlock(&omap_port->lock);
+		msg->complete(msg);
+		/* Now restart queued reads if any */
+		ssi_transfer(omap_port, &omap_port->rxqueue[i]);
+		spin_lock(&omap_port->lock);
+	}
+	spin_unlock(&omap_port->lock);
+}
+
+static void ssi_break_complete(struct hsi_port *port)
+{
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	struct hsi_msg *msg;
+	struct hsi_msg *tmp;
+	u32 val;
+
+	dev_dbg(&port->device, "HWBREAK received\n");
+
+	spin_lock(&omap_port->lock);
+	val = __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+	val &= ~SSI_BREAKDETECTED;
+	__raw_writel(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+	__raw_writel(0, omap_port->ssr_base + SSI_SSR_BREAK_REG);
+	__raw_writel(SSI_BREAKDETECTED,
+			omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
+	spin_unlock(&omap_port->lock);
+
+	list_for_each_entry_safe(msg, tmp, &omap_port->brkqueue, link) {
+		msg->status = HSI_STATUS_COMPLETED;
+		spin_lock(&omap_port->lock);
+		list_del(&msg->link);
+		spin_unlock(&omap_port->lock);
+		msg->complete(msg);
+	}
+
+}
+
+static int ssi_async_break(struct hsi_msg *msg)
+{
+	struct hsi_port *port = hsi_get_port(msg->cl);
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	int err = 0;
+	u32 tmp;
+
+	ssi_clk_enable(ssi);
+	if (msg->ttype == HSI_MSG_WRITE) {
+		if (omap_port->sst.mode != SSI_MODE_FRAME) {
+			err = -EINVAL;
+			goto out;
+		}
+		__raw_writel(1, omap_port->sst_base + SSI_SST_BREAK_REG);
+		msg->status = HSI_STATUS_COMPLETED;
+		msg->complete(msg);
+	} else {
+		if (omap_port->ssr.mode != SSI_MODE_FRAME) {
+			err = -EINVAL;
+			goto out;
+		}
+		spin_lock_bh(&omap_port->lock);
+		tmp = __raw_readl(omap_ssi->sys +
+					SSI_MPU_ENABLE_REG(port->num, 0));
+		__raw_writel(tmp | SSI_BREAKDETECTED,
+			omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+		msg->status = HSI_STATUS_PROCEEDING;
+		list_add_tail(&msg->link, &omap_port->brkqueue);
+		spin_unlock_bh(&omap_port->lock);
+	}
+out:
+	ssi_clk_disable(ssi);
+
+	return err;
+}
+
+static int ssi_async(struct hsi_msg *msg)
+{
+	struct hsi_port *port = hsi_get_port(msg->cl);
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct list_head *queue;
+	int err = 0;
+
+	BUG_ON(!msg);
+
+	if (msg->sgt.nents > 1)
+		return -ENOSYS; /* TODO: Add sg support */
+
+	if (msg->break_frame)
+		return ssi_async_break(msg);
+
+	if (msg->ttype) {
+		BUG_ON(msg->channel >= omap_port->sst.channels);
+		queue = &omap_port->txqueue[msg->channel];
+	} else {
+		BUG_ON(msg->channel >= omap_port->ssr.channels);
+		queue = &omap_port->rxqueue[msg->channel];
+	}
+	msg->status = HSI_STATUS_QUEUED;
+	spin_lock_bh(&omap_port->lock);
+	list_add_tail(&msg->link, queue);
+	err = ssi_start_transfer(queue);
+	if (err < 0) {
+		list_del(&msg->link);
+		msg->status = HSI_STATUS_ERROR;
+	}
+	spin_unlock_bh(&omap_port->lock);
+	dev_dbg(&port->device, "msg status %d ttype %d ch %d\n",
+				msg->status, msg->ttype, msg->channel);
+
+	return err;
+}
+
+static void ssi_flush_queue(struct list_head *queue, struct hsi_client *cl)
+{
+	struct list_head *node, *tmp;
+	struct hsi_msg *msg;
+
+	list_for_each_safe(node, tmp, queue) {
+		msg = list_entry(node, struct hsi_msg, link);
+		if ((cl) && (cl != msg->cl))
+			continue;
+		list_del(node);
+		pr_debug("flush queue: ch %d, msg %p len %d type %d ctxt %p\n",
+			msg->channel, msg, msg->sgt.sgl->length,
+					msg->ttype, msg->context);
+		if (msg->destructor)
+			msg->destructor(msg);
+		else
+			hsi_free_msg(msg);
+	}
+}
+
+static int ssi_setup(struct hsi_client *cl)
+{
+	struct hsi_port *port = to_hsi_port(cl->device.parent);
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	void __iomem *sst = omap_port->sst_base;
+	void __iomem *ssr = omap_port->ssr_base;
+	u32 div;
+	u32 val;
+	int err = 0;
+
+	ssi_clk_enable(ssi);
+	spin_lock_bh(&omap_port->lock);
+	if (cl->tx_cfg.speed)
+		omap_ssi->max_speed = cl->tx_cfg.speed;
+	div = ssi_calculate_div(ssi);
+	if (div > SSI_MAX_DIVISOR) {
+		dev_err(&cl->device, "Invalid TX speed %d Mb/s (div %d)\n",
+						cl->tx_cfg.speed, div);
+		err = -EINVAL;
+		goto out;
+	}
+	/* Set TX/RX module to sleep to stop TX/RX during cfg update */
+	__raw_writel(SSI_MODE_SLEEP, sst + SSI_SST_MODE_REG);
+	__raw_writel(SSI_MODE_SLEEP, ssr + SSI_SSR_MODE_REG);
+	/* Flush posted write */
+	val = __raw_readl(ssr + SSI_SSR_MODE_REG);
+	/* TX */
+	__raw_writel(31, sst + SSI_SST_FRAMESIZE_REG);
+	__raw_writel(div, sst + SSI_SST_DIVISOR_REG);
+	__raw_writel(cl->tx_cfg.channels, sst + SSI_SST_CHANNELS_REG);
+	__raw_writel(cl->tx_cfg.arb_mode, sst + SSI_SST_ARBMODE_REG);
+	__raw_writel(cl->tx_cfg.mode, sst + SSI_SST_MODE_REG);
+	/* RX */
+	__raw_writel(31, ssr + SSI_SSR_FRAMESIZE_REG);
+	__raw_writel(cl->rx_cfg.channels, ssr + SSI_SSR_CHANNELS_REG);
+	__raw_writel(0, ssr + SSI_SSR_TIMEOUT_REG);
+	/* Cleanup the break queue if we leave FRAME mode */
+	if ((omap_port->ssr.mode == SSI_MODE_FRAME) &&
+		(cl->rx_cfg.mode != SSI_MODE_FRAME))
+		ssi_flush_queue(&omap_port->brkqueue, cl);
+	__raw_writel(cl->rx_cfg.mode, ssr + SSI_SSR_MODE_REG);
+	omap_port->channels = max(cl->rx_cfg.channels, cl->tx_cfg.channels);
+	/* Shadow registering for OFF mode */
+	/* SST */
+	omap_port->sst.divisor = div;
+	omap_port->sst.frame_size = 31;
+	omap_port->sst.channels = cl->tx_cfg.channels;
+	omap_port->sst.arb_mode = cl->tx_cfg.arb_mode;
+	omap_port->sst.mode = cl->tx_cfg.mode;
+	/* SSR */
+	omap_port->ssr.frame_size = 31;
+	omap_port->ssr.timeout = 0;
+	omap_port->ssr.channels = cl->rx_cfg.channels;
+	omap_port->ssr.mode = cl->rx_cfg.mode;
+out:
+	spin_unlock_bh(&omap_port->lock);
+	ssi_clk_disable(ssi);
+
+	return err;
+}
+
+static void ssi_cleanup_queues(struct hsi_client *cl)
+{
+	struct hsi_port *port = hsi_get_port(cl);
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	struct hsi_msg *msg;
+	unsigned int i;
+	u32 rxbufstate = 0;
+	u32 txbufstate = 0;
+	u32 status = SSI_ERROROCCURED;
+	u32 tmp;
+
+	ssi_flush_queue(&omap_port->brkqueue, cl);
+	if (list_empty(&omap_port->brkqueue))
+		status |= SSI_BREAKDETECTED;
+
+	for (i = 0; i < omap_port->channels; i++) {
+		if (list_empty(&omap_port->txqueue[i]))
+			continue;
+		msg = list_first_entry(&omap_port->txqueue[i], struct hsi_msg,
+									link);
+		if ((msg->cl == cl) && (msg->status == HSI_STATUS_PROCEEDING)) {
+			txbufstate |= (1 << i);
+			status |= SSI_DATAACCEPT(i);
+			/* Release the clocks writes, also GDD ones */
+			ssi_clk_disable(ssi);
+		}
+		ssi_flush_queue(&omap_port->txqueue[i], cl);
+	}
+	for (i = 0; i < omap_port->channels; i++) {
+		if (list_empty(&omap_port->rxqueue[i]))
+			continue;
+		msg = list_first_entry(&omap_port->rxqueue[i], struct hsi_msg,
+									link);
+		if ((msg->cl == cl) && (msg->status == HSI_STATUS_PROCEEDING)) {
+			rxbufstate |= (1 << i);
+			status |= SSI_DATAAVAILABLE(i);
+		}
+		ssi_flush_queue(&omap_port->rxqueue[i], cl);
+		/* Check if we keep the error detection interrupt armed */
+		if (!list_empty(&omap_port->rxqueue[i]))
+			status &= ~SSI_ERROROCCURED;
+	}
+	/* Cleanup write buffers */
+	tmp = __raw_readl(omap_port->sst_base + SSI_SST_BUFSTATE_REG);
+	tmp &= ~txbufstate;
+	__raw_writel(tmp, omap_port->sst_base + SSI_SST_BUFSTATE_REG);
+	/* Cleanup read buffers */
+	tmp = __raw_readl(omap_port->ssr_base + SSI_SSR_BUFSTATE_REG);
+	tmp &= ~rxbufstate;
+	__raw_writel(tmp, omap_port->ssr_base + SSI_SSR_BUFSTATE_REG);
+	/* Disarm and ack pending interrupts */
+	tmp = __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+	tmp &= ~status;
+	__raw_writel(tmp, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+	__raw_writel(status, omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
+}
+
+static void ssi_cleanup_gdd(struct hsi_controller *ssi, struct hsi_client *cl)
+{
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	struct hsi_msg *msg;
+	unsigned int i;
+	u32 val = 0;
+	u32 tmp;
+
+	for (i = 0; i < SSI_MAX_GDD_LCH; i++) {
+		msg = omap_ssi->gdd_trn[i].msg;
+		if ((!msg) || (msg->cl != cl))
+			continue;
+		__raw_writew(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i));
+		val |= (1 << i);
+		/*
+		 * Clock references for write will be handled in
+		 * ssi_cleanup_queues
+		 */
+		if (msg->ttype == HSI_MSG_READ)
+			ssi_clk_disable(ssi);
+		omap_ssi->gdd_trn[i].msg = NULL;
+	}
+	tmp = __raw_readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+	tmp &= ~val;
+	__raw_writel(tmp, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+	__raw_writel(val, omap_ssi->sys + SSI_GDD_MPU_IRQ_STATUS_REG);
+}
+
+static int ssi_release(struct hsi_client *cl)
+{
+	struct hsi_port *port = hsi_get_port(cl);
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+	spin_lock_bh(&omap_port->lock);
+	ssi_clk_enable(ssi);
+	/* Stop all the pending DMA requests for that client */
+	ssi_cleanup_gdd(ssi, cl);
+	/* Now cleanup all the queues */
+	ssi_cleanup_queues(cl);
+	ssi_clk_disable(ssi);
+	/* If it is the last client of the port, do extra checks and cleanup */
+	if (port->claimed <= 1) {
+		/*
+		 * Drop the clock reference for the incoming wake line
+		 * if it is still kept high by the other side.
+		 */
+		if (omap_port->wkin_cken) {
+			ssi_clk_disable(ssi);
+			omap_port->wkin_cken = 0;
+		}
+		ssi_clk_enable(ssi);
+		/* Stop any SSI TX/RX without a client */
+		ssi_set_mode(ssi, SSI_MODE_SLEEP);
+		omap_port->sst.mode = SSI_MODE_SLEEP;
+		omap_port->ssr.mode = SSI_MODE_SLEEP;
+		ssi_clk_disable(ssi);
+		WARN_ON(omap_port->wk_refcount != 0);
+		WARN_ON(omap_ssi->ck_refcount != 0);
+	}
+	spin_unlock_bh(&omap_port->lock);
+
+	return 0;
+}
+
+static int ssi_flush(struct hsi_client *cl)
+{
+	struct hsi_port *port = hsi_get_port(cl);
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	struct hsi_msg *msg;
+	void __iomem *sst = omap_port->sst_base;
+	void __iomem *ssr = omap_port->ssr_base;
+	unsigned int i;
+	u32 err;
+
+	ssi_clk_enable(ssi);
+	spin_lock_bh(&omap_port->lock);
+	/* Stop all DMA transfers */
+	for (i = 0; i < SSI_MAX_GDD_LCH; i++) {
+		msg = omap_ssi->gdd_trn[i].msg;
+		if (!msg || (port != hsi_get_port(msg->cl)))
+			continue;
+		__raw_writew(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i));
+		if (msg->ttype == HSI_MSG_READ)
+			ssi_clk_disable(ssi);
+		omap_ssi->gdd_trn[i].msg = NULL;
+	}
+	/* Flush all SST buffers */
+	__raw_writel(0, sst + SSI_SST_BUFSTATE_REG);
+	__raw_writel(0, sst + SSI_SST_TXSTATE_REG);
+	/* Flush all SSR buffers */
+	__raw_writel(0, ssr + SSI_SSR_RXSTATE_REG);
+	__raw_writel(0, ssr + SSI_SSR_BUFSTATE_REG);
+	/* Flush all errors */
+	err = __raw_readl(ssr + SSI_SSR_ERROR_REG);
+	__raw_writel(err, ssr + SSI_SSR_ERRORACK_REG);
+	/* Flush break */
+	__raw_writel(0, ssr + SSI_SSR_BREAK_REG);
+	/* Clear interrupts */
+	__raw_writel(0, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+	__raw_writel(0xffffff00,
+			omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
+	__raw_writel(0, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+	__raw_writel(0xff, omap_ssi->sys + SSI_GDD_MPU_IRQ_STATUS_REG);
+	/* Dequeue all pending requests */
+	for (i = 0; i < omap_port->channels; i++) {
+		/* Release write clocks */
+		if (!list_empty(&omap_port->txqueue[i]))
+			ssi_clk_disable(ssi);
+		ssi_flush_queue(&omap_port->txqueue[i], NULL);
+		ssi_flush_queue(&omap_port->rxqueue[i], NULL);
+	}
+	ssi_flush_queue(&omap_port->brkqueue, NULL);
+	spin_unlock_bh(&omap_port->lock);
+	ssi_clk_disable(ssi);
+
+	return 0;
+}
+
+static int ssi_start_tx(struct hsi_client *cl)
+{
+	struct hsi_port *port = hsi_get_port(cl);
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+	dev_dbg(&port->device, "Wake out high %d\n", omap_port->wk_refcount);
+
+	spin_lock_bh(&omap_port->wk_lock);
+	if (omap_port->wk_refcount++) {
+		spin_unlock_bh(&omap_port->wk_lock);
+		return 0;
+	}
+	ssi_clk_enable(ssi); /* Grab clocks */
+	__raw_writel(SSI_WAKE(0), omap_ssi->sys + SSI_SET_WAKE_REG(port->num));
+	spin_unlock_bh(&omap_port->wk_lock);
+
+	return 0;
+}
+
+static int ssi_stop_tx(struct hsi_client *cl)
+{
+	struct hsi_port *port = hsi_get_port(cl);
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+	dev_dbg(&port->device, "Wake out low %d\n", omap_port->wk_refcount);
+
+	spin_lock_bh(&omap_port->wk_lock);
+	BUG_ON(!omap_port->wk_refcount);
+	if (--omap_port->wk_refcount) {
+		spin_unlock_bh(&omap_port->wk_lock);
+		return 0;
+	}
+	__raw_writel(SSI_WAKE(0),
+				omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num));
+	ssi_clk_disable(ssi); /* Release clocks */
+	spin_unlock_bh(&omap_port->wk_lock);
+
+	return 0;
+}
+
+static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue)
+{
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct hsi_msg *msg;
+	u32 *buf;
+	u32 reg;
+	u32 val;
+
+	spin_lock(&omap_port->lock);
+	msg = list_first_entry(queue, struct hsi_msg, link);
+	if ((!msg->sgt.nents) || (!msg->sgt.sgl->length)) {
+		msg->actual_len = 0;
+		msg->status = HSI_STATUS_PENDING;
+	}
+	if (msg->ttype == HSI_MSG_WRITE)
+		val = SSI_DATAACCEPT(msg->channel);
+	else
+		val = SSI_DATAAVAILABLE(msg->channel);
+	if (msg->status == HSI_STATUS_PROCEEDING) {
+		buf = sg_virt(msg->sgt.sgl) + msg->actual_len;
+		if (msg->ttype == HSI_MSG_WRITE)
+			__raw_writel(*buf, omap_port->sst_base +
+					SSI_SST_BUFFER_CH_REG(msg->channel));
+		 else
+			*buf = __raw_readl(omap_port->ssr_base +
+					SSI_SSR_BUFFER_CH_REG(msg->channel));
+		dev_dbg(&port->device, "ch %d ttype %d 0x%08x\n", msg->channel,
+							msg->ttype, *buf);
+		msg->actual_len += sizeof(*buf);
+		if (msg->actual_len >= msg->sgt.sgl->length)
+			msg->status = HSI_STATUS_COMPLETED;
+		/*
+		 * Wait for the last written frame to be really sent before
+		 * we call the complete callback
+		 */
+		if ((msg->status == HSI_STATUS_PROCEEDING) ||
+				((msg->status == HSI_STATUS_COMPLETED) &&
+					(msg->ttype == HSI_MSG_WRITE))) {
+			__raw_writel(val, omap_ssi->sys +
+					SSI_MPU_STATUS_REG(port->num, 0));
+			spin_unlock(&omap_port->lock);
+
+			return;
+		}
+
+	}
+	/* Transfer completed at this point */
+	reg = __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+	if (msg->ttype == HSI_MSG_WRITE)
+		ssi_clk_disable(ssi); /* Release clocks for write transfer */
+	reg &= ~val;
+	__raw_writel(reg, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+	__raw_writel(val, omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
+	list_del(&msg->link);
+	spin_unlock(&omap_port->lock);
+	msg->complete(msg);
+	ssi_transfer(omap_port, queue);
+}
+
+static void ssi_gdd_complete(struct hsi_controller *ssi, unsigned int lch)
+{
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	struct hsi_msg *msg = omap_ssi->gdd_trn[lch].msg;
+	struct hsi_port *port = to_hsi_port(msg->cl->device.parent);
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	unsigned int dir;
+	u32 csr;
+	u32 val;
+
+	spin_lock(&omap_ssi->lock);
+
+	val = __raw_readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+	val &= ~SSI_GDD_LCH(lch);
+	__raw_writel(val, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+
+	if (msg->ttype == HSI_MSG_READ) {
+		dir = DMA_FROM_DEVICE;
+		val = SSI_DATAAVAILABLE(msg->channel);
+		ssi_clk_disable(ssi);
+	} else {
+		dir = DMA_TO_DEVICE;
+		val = SSI_DATAACCEPT(msg->channel);
+		/* Keep clocks reference for write pio event */
+	}
+	dma_unmap_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents, dir);
+	csr = __raw_readw(omap_ssi->gdd + SSI_GDD_CSR_REG(lch));
+	omap_ssi->gdd_trn[lch].msg = NULL; /* release GDD lch */
+	dev_dbg(&port->device, "DMA completed ch %d ttype %d\n",
+				msg->channel, msg->ttype);
+	spin_unlock(&omap_ssi->lock);
+	if (csr & SSI_CSR_TOUR) { /* Timeout error */
+		msg->status = HSI_STATUS_ERROR;
+		msg->actual_len = 0;
+		spin_lock(&omap_port->lock);
+		list_del(&msg->link); /* Dequeue msg */
+		spin_unlock(&omap_port->lock);
+		msg->complete(msg);
+		return;
+	}
+	spin_lock(&omap_port->lock);
+	val |= __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+	__raw_writel(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+	spin_unlock(&omap_port->lock);
+
+	msg->status = HSI_STATUS_COMPLETED;
+	msg->actual_len = sg_dma_len(msg->sgt.sgl);
+}
+
+static void ssi_gdd_tasklet(unsigned long dev)
+{
+	struct hsi_controller *ssi = (struct hsi_controller *)dev;
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	void __iomem *sys = omap_ssi->sys;
+	unsigned int lch;
+	u32 status_reg;
+
+	ssi_clk_enable(ssi);
+
+	status_reg = __raw_readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG);
+	for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) {
+		if (status_reg & SSI_GDD_LCH(lch))
+			ssi_gdd_complete(ssi, lch);
+	}
+	__raw_writel(status_reg, sys + SSI_GDD_MPU_IRQ_STATUS_REG);
+	status_reg = __raw_readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG);
+	ssi_clk_disable(ssi);
+	if (status_reg)
+		tasklet_hi_schedule(&omap_ssi->gdd_tasklet);
+	else
+		enable_irq(omap_ssi->gdd_irq);
+
+}
+
+static irqreturn_t ssi_gdd_isr(int irq, void *ssi)
+{
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+	tasklet_hi_schedule(&omap_ssi->gdd_tasklet);
+	disable_irq_nosync(irq);
+
+	return IRQ_HANDLED;
+}
+
+static void ssi_pio_tasklet(unsigned long ssi_port)
+{
+	struct hsi_port *port = (struct hsi_port *)ssi_port;
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	void __iomem *sys = omap_ssi->sys;
+	unsigned int ch;
+	u32 status_reg;
+
+	ssi_clk_enable(ssi);
+	status_reg = __raw_readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
+	status_reg &= __raw_readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
+
+	for (ch = 0; ch < omap_port->channels; ch++) {
+		if (status_reg & SSI_DATAACCEPT(ch))
+			ssi_pio_complete(port, &omap_port->txqueue[ch]);
+		if (status_reg & SSI_DATAAVAILABLE(ch))
+			ssi_pio_complete(port, &omap_port->rxqueue[ch]);
+	}
+	if (status_reg & SSI_BREAKDETECTED)
+		ssi_break_complete(port);
+	if (status_reg & SSI_ERROROCCURED)
+		ssi_error(port);
+
+	status_reg = __raw_readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
+	status_reg &= __raw_readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
+	ssi_clk_disable(ssi);
+
+	if (status_reg)
+		tasklet_hi_schedule(&omap_port->pio_tasklet);
+	else
+		enable_irq(omap_port->irq);
+}
+
+static irqreturn_t ssi_pio_isr(int irq, void *port)
+{
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+
+	tasklet_hi_schedule(&omap_port->pio_tasklet);
+	disable_irq_nosync(irq);
+
+	return IRQ_HANDLED;
+}
+
+static void ssi_wake_tasklet(unsigned long ssi_port)
+{
+	struct hsi_port *port = (struct hsi_port *)ssi_port;
+	struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+	if (ssi_wakein(port)) {
+		/**
+		 * We can have a quick High-Low-High transition in the line.
+		 * In such a case if we have long interrupt latencies,
+		 * we can miss the low event or get twice a high event.
+		 * This workaround will avoid breaking the clock reference
+		 * count when such a situation ocurrs.
+		 */
+		spin_lock(&omap_port->lock);
+		if (!omap_port->wkin_cken) {
+			omap_port->wkin_cken = 1;
+			ssi_clk_enable(ssi);
+		}
+		spin_unlock(&omap_port->lock);
+		dev_dbg(&ssi->device, "Wake in high\n");
+		if (omap_port->wktest) { /* FIXME: HACK ! To be removed */
+			__raw_writel(SSI_WAKE(0),
+				omap_ssi->sys + SSI_SET_WAKE_REG(port->num));
+		}
+		hsi_event(port, HSI_EVENT_START_RX);
+	} else {
+		dev_dbg(&ssi->device, "Wake in low\n");
+		if (omap_port->wktest) { /* FIXME: HACK ! To be removed */
+			__raw_writel(SSI_WAKE(0),
+				omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num));
+		}
+		hsi_event(port, HSI_EVENT_STOP_RX);
+		spin_lock(&omap_port->lock);
+		if (omap_port->wkin_cken) {
+			ssi_clk_disable(ssi);
+			omap_port->wkin_cken = 0;
+		}
+		spin_unlock(&omap_port->lock);
+	}
+}
+
+static irqreturn_t ssi_wake_isr(int irq __maybe_unused, void *ssi_port)
+{
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(ssi_port);
+
+	tasklet_hi_schedule(&omap_port->wake_tasklet);
+
+	return IRQ_HANDLED;
+}
+
+static int __init ssi_port_irq(struct hsi_port *port,
+						struct platform_device *pd)
+{
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	struct resource *irq;
+	int err;
+	char irq_name[25];
+
+	sprintf(irq_name, "ssi_p%d_mpu_irq0", port->num+1);
+	irq = platform_get_resource_byname(pd, IORESOURCE_IRQ, irq_name);
+	if (!irq) {
+		dev_err(&port->device, "Port IRQ resource missing\n");
+		return -ENXIO;
+	}
+	omap_port->irq = irq->start;
+	tasklet_init(&omap_port->pio_tasklet, ssi_pio_tasklet,
+							(unsigned long)port);
+	err = devm_request_irq(&pd->dev, omap_port->irq, ssi_pio_isr,
+						IRQF_DISABLED, irq->name, port);
+	if (err < 0)
+		dev_err(&port->device, "Request IRQ %d failed (%d)\n",
+							omap_port->irq, err);
+	return err;
+}
+
+static int __init ssi_wake_irq(struct hsi_port *port,
+						struct platform_device *pd)
+{
+	struct omap_ssi_platform_data *omap_ssi_pdata = pd->dev.platform_data;
+	struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+	char irq_name[23];
+	int cawake_gpio;
+	int cawake_irq;
+	int err;
+
+	if (port->num >= omap_ssi_pdata->num_ports) {
+		dev_err(&port->device, "Wake in IRQ resource missing");
+		return -ENXIO;
+	}
+
+	sprintf(irq_name, "ssi_p%d_cawake", port->num+1);
+
+	cawake_gpio = omap_ssi_pdata->cawake_gpio[port->num];
+
+	if (cawake_gpio == -1) {
+		omap_port->wake_gpio = -1;
+		omap_port->wake_irq = -1;
+		return 0;
+	}
+
+	cawake_irq = gpio_to_irq(cawake_gpio);
+
+	omap_port->wake_gpio = cawake_gpio;
+	omap_port->wake_irq = cawake_irq;
+	tasklet_init(&omap_port->wake_tasklet, ssi_wake_tasklet,
+							(unsigned long)port);
+	err = devm_request_irq(&pd->dev, cawake_irq, ssi_wake_isr,
+		IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+							irq_name, port);
+	if (err < 0)
+		dev_err(&port->device, "Request Wake in IRQ %d failed %d\n",
+						cawake_irq, err);
+	err = enable_irq_wake(cawake_irq);
+	if (err < 0)
+		dev_err(&port->device, "Enable wake on the wakeline in irq %d"
+				" failed %d\n", cawake_irq, err);
+
+	return err;
+}
+
+static void __init ssi_queues_init(struct omap_ssi_port *omap_port)
+{
+	unsigned int ch;
+
+	for (ch = 0; ch < SSI_MAX_CHANNELS; ch++) {
+		INIT_LIST_HEAD(&omap_port->txqueue[ch]);
+		INIT_LIST_HEAD(&omap_port->rxqueue[ch]);
+	}
+	INIT_LIST_HEAD(&omap_port->brkqueue);
+}
+
+static int __init ssi_get_iomem(struct platform_device *pd,
+		const char *name, void __iomem **pbase, dma_addr_t *phy)
+{
+	struct resource *mem;
+	struct resource *ioarea;
+	void __iomem *base;
+
+	mem = platform_get_resource_byname(pd, IORESOURCE_MEM, name);
+	if (!mem) {
+		dev_err(&pd->dev, "IO memory region missing (%s)\n", name);
+		return -ENXIO;
+	}
+	ioarea = devm_request_mem_region(&pd->dev, mem->start,
+					resource_size(mem), dev_name(&pd->dev));
+	if (!ioarea) {
+		dev_err(&pd->dev, "%s IO memory region request failed\n",
+								mem->name);
+		return -ENXIO;
+	}
+	base = devm_ioremap(&pd->dev, mem->start, resource_size(mem));
+	if (!base) {
+		dev_err(&pd->dev, "%s IO remap failed\n", mem->name);
+		return -ENXIO;
+	}
+	*pbase = base;
+
+	if (phy)
+		*phy = mem->start;
+
+	return 0;
+}
+
+static int __init ssi_ports_init(struct hsi_controller *ssi,
+					struct platform_device *pd)
+{
+	struct hsi_port *port;
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	struct omap_ssi_port *omap_port;
+	unsigned int i;
+	int err;
+	char mem_sst_name[16];
+	char mem_ssr_name[16];
+
+	omap_ssi->port = devm_kzalloc(&pd->dev,
+				sizeof(omap_port) * ssi->num_ports, GFP_KERNEL);
+	if (!omap_ssi->port)
+		return -ENOMEM;
+
+	for (i = 0; i < ssi->num_ports; i++) {
+		port = ssi->port[i];
+		omap_port = devm_kzalloc(&pd->dev, sizeof(*omap_port),
+								GFP_KERNEL);
+		if (!omap_port)
+			return -ENOMEM;
+		port->async = ssi_async;
+		port->setup = ssi_setup;
+		port->flush = ssi_flush;
+		port->start_tx = ssi_start_tx;
+		port->stop_tx = ssi_stop_tx;
+		port->release = ssi_release;
+		hsi_port_set_drvdata(port, omap_port);
+
+		sprintf(mem_sst_name, "p%d_sst", i+1);
+		sprintf(mem_ssr_name, "p%d_ssr", i+1);
+
+		/* Get SST base addresses*/
+		err = ssi_get_iomem(pd, mem_sst_name, &omap_port->sst_base,
+							&omap_port->sst_dma);
+		if (err < 0)
+			return err;
+		/* Get SSR base addresses */
+		err = ssi_get_iomem(pd, mem_ssr_name, &omap_port->ssr_base,
+							&omap_port->ssr_dma);
+		if (err < 0)
+			return err;
+		err = ssi_port_irq(port, pd);
+		if (err < 0)
+			return err;
+		err = ssi_wake_irq(port, pd);
+		if (err < 0)
+			return err;
+		ssi_queues_init(omap_port);
+		spin_lock_init(&omap_port->lock);
+		spin_lock_init(&omap_port->wk_lock);
+		omap_port->dev = &port->device;
+		omap_ssi->port[i] = omap_port;
+	}
+
+	return 0;
+}
+
+static void ssi_ports_exit(struct hsi_controller *ssi)
+{
+	struct omap_ssi_port *omap_port;
+	unsigned int i;
+
+	for (i = 0; i < ssi->num_ports; i++) {
+		omap_port = hsi_port_drvdata(ssi->port[i]);
+		tasklet_kill(&omap_port->wake_tasklet);
+		tasklet_kill(&omap_port->pio_tasklet);
+	}
+}
+
+static unsigned long ssi_get_clk_rate(struct hsi_controller *ssi)
+{
+	struct device *pdev = ssi->device.parent;
+	struct clk *clk;
+	unsigned long rate;
+
+	clk = clk_get(pdev, "ssi_ssr_fck");
+	if (IS_ERR(clk)) {
+		dev_err(pdev, "clock get ssi_ssr_fck failed %li\n", PTR_ERR(clk));
+		return 0;
+	}
+
+	rate = clk_get_rate(clk);
+
+	clk_put(clk);
+
+	return rate;
+}
+
+static int __init ssi_add_controller(struct hsi_controller *ssi,
+						struct platform_device *pd)
+{
+	struct omap_ssi_platform_data *omap_ssi_pdata = pd->dev.platform_data;
+	struct omap_ssi_controller *omap_ssi;
+	struct resource *irq;
+	int err;
+
+	omap_ssi = devm_kzalloc(&pd->dev, sizeof(*omap_ssi), GFP_KERNEL);
+	if (!omap_ssi) {
+		dev_err(&pd->dev, "not enough memory for omap ssi\n");
+		return -ENOMEM;
+	}
+	ssi->id = pd->id;
+	ssi->owner = THIS_MODULE;
+	ssi->device.parent = &pd->dev;
+	dev_set_name(&ssi->device, "ssi%d", ssi->id);
+	hsi_controller_set_drvdata(ssi, omap_ssi);
+	omap_ssi->dev = &ssi->device;
+	err = ssi_get_iomem(pd, "sys", &omap_ssi->sys, NULL);
+	if (err < 0)
+		return err;
+	err = ssi_get_iomem(pd, "gdd", &omap_ssi->gdd, NULL);
+	if (err < 0)
+		return err;
+	irq = platform_get_resource_byname(pd, IORESOURCE_IRQ, "ssi_gdd_mpu");
+	if (!irq) {
+		dev_err(&pd->dev, "GDD IRQ resource missing\n");
+		return -ENXIO;
+	}
+	omap_ssi->gdd_irq = irq->start;
+	tasklet_init(&omap_ssi->gdd_tasklet, ssi_gdd_tasklet,
+							(unsigned long)ssi);
+	err = devm_request_irq(&pd->dev, omap_ssi->gdd_irq, ssi_gdd_isr,
+						IRQF_DISABLED, irq->name, ssi);
+	if (err < 0) {
+		dev_err(&ssi->device, "Request GDD IRQ %d failed (%d)",
+							omap_ssi->gdd_irq, err);
+		return err;
+	}
+	err = ssi_ports_init(ssi, pd);
+	if (err < 0)
+		return err;
+	omap_ssi->get_loss = omap_ssi_pdata->get_dev_context_loss_count;
+	omap_ssi->max_speed = UINT_MAX;
+	spin_lock_init(&omap_ssi->lock);
+	spin_lock_init(&omap_ssi->ck_lock);
+	err = hsi_register_controller(ssi);
+
+	return err;
+}
+
+static int __init ssi_hw_init(struct hsi_controller *ssi)
+{
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+	unsigned int i;
+	u32 val;
+	int err;
+
+	err = ssi_clk_enable(ssi);
+	if (err < 0) {
+		dev_err(&ssi->device, "Failed to enable the clocks %d\n", err);
+		return err;
+	}
+	/* Reseting SSI controller */
+	__raw_writel(SSI_SOFTRESET, omap_ssi->sys + SSI_SYSCONFIG_REG);
+	val = __raw_readl(omap_ssi->sys + SSI_SYSSTATUS_REG);
+	for (i = 0; ((i < 20) && !(val & SSI_RESETDONE)); i++) {
+		msleep(20);
+		val = __raw_readl(omap_ssi->sys + SSI_SYSSTATUS_REG);
+	}
+	if (!(val & SSI_RESETDONE)) {
+		dev_err(&ssi->device, "SSI HW reset failed\n");
+		ssi_clk_disable(ssi);
+		return -EIO;
+	}
+	/* Reseting GDD */
+	__raw_writel(SSI_SWRESET, omap_ssi->gdd + SSI_GDD_GRST_REG);
+	/* Get FCK rate */
+	omap_ssi->fck_rate = ssi_get_clk_rate(ssi) / 1000; /* KHz */
+	dev_dbg(&ssi->device, "SSI fck rate %lu KHz\n", omap_ssi->fck_rate);
+	/* Set default PM settings */
+	val = SSI_AUTOIDLE | SSI_SIDLEMODE_SMART | SSI_MIDLEMODE_SMART;
+	__raw_writel(val, omap_ssi->sys + SSI_SYSCONFIG_REG);
+	omap_ssi->sysconfig = val;
+	__raw_writel(SSI_CLK_AUTOGATING_ON, omap_ssi->sys + SSI_GDD_GCR_REG);
+	omap_ssi->gdd_gcr = SSI_CLK_AUTOGATING_ON;
+	ssi_clk_disable(ssi);
+
+	return 0;
+}
+
+static void ssi_remove_controller(struct hsi_controller *ssi)
+{
+	struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+	ssi_ports_exit(ssi);
+	tasklet_kill(&omap_ssi->gdd_tasklet);
+	hsi_unregister_controller(ssi);
+}
+
+static int __init ssi_probe(struct platform_device *pd)
+{
+	struct omap_ssi_platform_data *omap_ssi_pdata = pd->dev.platform_data;
+	struct hsi_controller *ssi;
+	int err;
+
+	if (!omap_ssi_pdata) {
+		dev_err(&pd->dev, "No OMAP SSI platform data\n");
+		return -EINVAL;
+	}
+	ssi = hsi_alloc_controller(omap_ssi_pdata->num_ports, GFP_KERNEL);
+	if (!ssi) {
+		dev_err(&pd->dev, "No memory for controller\n");
+		return -ENOMEM;
+	}
+	platform_set_drvdata(pd, ssi);
+	pm_runtime_enable(&pd->dev);
+	err = ssi_add_controller(ssi, pd);
+	if (err < 0)
+		goto out1;
+	err = ssi_hw_init(ssi);
+	if (err < 0)
+		goto out2;
+#ifdef CONFIG_DEBUG_FS
+	err = ssi_debug_add_ctrl(ssi);
+	if (err < 0)
+		goto out2;
+#endif
+	return err;
+out2:
+	ssi_remove_controller(ssi);
+out1:
+	platform_set_drvdata(pd, NULL);
+
+	pm_runtime_disable(&pd->dev);
+
+	return err;
+}
+
+static int __exit ssi_remove(struct platform_device *pd)
+{
+	struct hsi_controller *ssi = platform_get_drvdata(pd);
+
+#ifdef CONFIG_DEBUG_FS
+	ssi_debug_remove_ctrl(ssi);
+#endif
+	ssi_remove_controller(ssi);
+	platform_set_drvdata(pd, NULL);
+
+	pm_runtime_disable(&pd->dev);
+
+	return 0;
+}
+
+static struct platform_driver ssi_pdriver = {
+	.remove	= __exit_p(ssi_remove),
+	.driver	= {
+		.name	= "omap_ssi",
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver_probe(ssi_pdriver, ssi_probe);
+
+MODULE_ALIAS("platform:omap_ssi");
+MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>");
+MODULE_DESCRIPTION("Synchronous Serial Interface Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/platform_data/hsi-omap-ssi.h b/include/linux/platform_data/hsi-omap-ssi.h
new file mode 100644
index 0000000..7c3801a
--- /dev/null
+++ b/include/linux/platform_data/hsi-omap-ssi.h
@@ -0,0 +1,202 @@
+/* Hardware definitions for SSI.
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Carlos Chinea <carlos.chinea@nokia.com>
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __OMAP_SSI_REGS_H__
+#define __OMAP_SSI_REGS_H__
+
+#define SSI_NUM_PORTS  1
+/*
+ * SSI SYS registers
+ */
+#define SSI_REVISION_REG    0
+#  define SSI_REV_MAJOR    0xf0
+#  define SSI_REV_MINOR    0xf
+#define SSI_SYSCONFIG_REG    0x10
+#  define SSI_AUTOIDLE    (1 << 0)
+#  define SSI_SOFTRESET    (1 << 1)
+#  define SSI_SIDLEMODE_FORCE  0
+#  define SSI_SIDLEMODE_NO    (1 << 3)
+#  define SSI_SIDLEMODE_SMART  (1 << 4)
+#  define SSI_SIDLEMODE_MASK  0x18
+#  define SSI_MIDLEMODE_FORCE  0
+#  define SSI_MIDLEMODE_NO    (1 << 12)
+#  define SSI_MIDLEMODE_SMART  (1 << 13)
+#  define SSI_MIDLEMODE_MASK  0x3000
+#define SSI_SYSSTATUS_REG    0x14
+#  define SSI_RESETDONE    1
+#define SSI_MPU_STATUS_REG(port, irq)  (0x808 + ((port) * 0x10) + ((irq) * 2))
+#define SSI_MPU_ENABLE_REG(port, irq)  (0x80c + ((port) * 0x10) + ((irq) * 8))
+#  define SSI_DATAACCEPT(channel)    (1 << (channel))
+#  define SSI_DATAAVAILABLE(channel)  (1 << ((channel) + 8))
+#  define SSI_DATAOVERRUN(channel)    (1 << ((channel) + 16))
+#  define SSI_ERROROCCURED      (1 << 24)
+#  define SSI_BREAKDETECTED    (1 << 25)
+#define SSI_GDD_MPU_IRQ_STATUS_REG  0x0800
+#define SSI_GDD_MPU_IRQ_ENABLE_REG  0x0804
+#  define SSI_GDD_LCH(channel)  (1 << (channel))
+#define SSI_WAKE_REG(port)    (0xc00 + ((port) * 0x10))
+#define SSI_CLEAR_WAKE_REG(port)  (0xc04 + ((port) * 0x10))
+#define SSI_SET_WAKE_REG(port)    (0xc08 + ((port) * 0x10))
+#  define SSI_WAKE(channel)  (1 << (channel))
+#  define SSI_WAKE_MASK    0xff
+
+/*
+ * SSI SST registers
+ */
+#define SSI_SST_ID_REG      0
+#define SSI_SST_MODE_REG    4
+#  define SSI_MODE_VAL_MASK  3
+#  define SSI_MODE_SLEEP    0
+#  define SSI_MODE_STREAM    1
+#  define SSI_MODE_FRAME    2
+#  define SSI_MODE_MULTIPOINTS  3
+#define SSI_SST_FRAMESIZE_REG    8
+#  define SSI_FRAMESIZE_DEFAULT  31
+#define SSI_SST_TXSTATE_REG    0xc
+#  define  SSI_TXSTATE_IDLE  0
+#define SSI_SST_BUFSTATE_REG    0x10
+#  define  SSI_FULL(channel)  (1 << (channel))
+#define SSI_SST_DIVISOR_REG    0x18
+#  define SSI_MAX_DIVISOR    127
+#define SSI_SST_BREAK_REG    0x20
+#define SSI_SST_CHANNELS_REG    0x24
+#  define SSI_CHANNELS_DEFAULT  4
+#define SSI_SST_ARBMODE_REG    0x28
+#  define SSI_ARBMODE_ROUNDROBIN  0
+#  define SSI_ARBMODE_PRIORITY  1
+#define SSI_SST_BUFFER_CH_REG(channel)  (0x80 + ((channel) * 4))
+#define SSI_SST_SWAPBUF_CH_REG(channel)  (0xc0 + ((channel) * 4))
+
+/*
+ * SSI SSR registers
+ */
+#define SSI_SSR_ID_REG      0
+#define SSI_SSR_MODE_REG    4
+#define SSI_SSR_FRAMESIZE_REG    8
+#define SSI_SSR_RXSTATE_REG    0xc
+#define SSI_SSR_BUFSTATE_REG    0x10
+#  define SSI_NOTEMPTY(channel)  (1 << (channel))
+#define SSI_SSR_BREAK_REG    0x1c
+#define SSI_SSR_ERROR_REG    0x20
+#define SSI_SSR_ERRORACK_REG    0x24
+#define SSI_SSR_OVERRUN_REG    0x2c
+#define SSI_SSR_OVERRUNACK_REG    0x30
+#define SSI_SSR_TIMEOUT_REG    0x34
+#  define SSI_TIMEOUT_DEFAULT  0
+#define SSI_SSR_CHANNELS_REG    0x28
+#define SSI_SSR_BUFFER_CH_REG(channel)  (0x80 + ((channel) * 4))
+#define SSI_SSR_SWAPBUF_CH_REG(channel)  (0xc0 + ((channel) * 4))
+
+/*
+ * SSI GDD registers
+ */
+#define SSI_GDD_HW_ID_REG    0
+#define SSI_GDD_PPORT_ID_REG    0x10
+#define SSI_GDD_MPORT_ID_REG    0x14
+#define SSI_GDD_PPORT_SR_REG    0x20
+#define SSI_GDD_MPORT_SR_REG    0x24
+#  define SSI_ACTIVE_LCH_NUM_MASK  0xff
+#define SSI_GDD_TEST_REG    0x40
+#  define SSI_TEST      1
+#define SSI_GDD_GCR_REG      0x100
+#  define  SSI_CLK_AUTOGATING_ON  (1 << 3)
+#  define  SSI_FREE    (1 << 2)
+#  define  SSI_SWITCH_OFF    (1 << 0)
+#define SSI_GDD_GRST_REG    0x200
+#  define SSI_SWRESET    1
+#define SSI_GDD_CSDP_REG(channel)  (0x800 + ((channel) * 0x40))
+#  define SSI_DST_BURST_EN_MASK  0xc000
+#  define SSI_DST_SINGLE_ACCESS0  0
+#  define SSI_DST_SINGLE_ACCESS  (1 << 14)
+#  define SSI_DST_BURST_4x32_BIT  (2 << 14)
+#  define SSI_DST_BURST_8x32_BIT  (3 << 14)
+#  define SSI_DST_MASK    0x1e00
+#  define SSI_DST_MEMORY_PORT  (8 << 9)
+#  define SSI_DST_PERIPHERAL_PORT  (9 << 9)
+#  define SSI_SRC_BURST_EN_MASK  0x180
+#  define SSI_SRC_SINGLE_ACCESS0  0
+#  define SSI_SRC_SINGLE_ACCESS  (1 << 7)
+#  define SSI_SRC_BURST_4x32_BIT  (2 << 7)
+#  define SSI_SRC_BURST_8x32_BIT  (3 << 7)
+#  define SSI_SRC_MASK    0x3c
+#  define SSI_SRC_MEMORY_PORT  (8 << 2)
+#  define SSI_SRC_PERIPHERAL_PORT  (9 << 2)
+#  define SSI_DATA_TYPE_MASK  3
+#  define SSI_DATA_TYPE_S32  2
+#define SSI_GDD_CCR_REG(channel)  (0x802 + ((channel) * 0x40))
+#  define SSI_DST_AMODE_MASK  (3 << 14)
+#  define SSI_DST_AMODE_CONST  0
+#  define SSI_DST_AMODE_POSTINC  (1 << 12)
+#  define SSI_SRC_AMODE_MASK  (3 << 12)
+#  define SSI_SRC_AMODE_CONST  0
+#  define SSI_SRC_AMODE_POSTINC  (1 << 12)
+#  define SSI_CCR_ENABLE    (1 << 7)
+#  define SSI_CCR_SYNC_MASK  0x1f
+#define SSI_GDD_CICR_REG(channel)  (0x804 + ((channel) * 0x40))
+#  define SSI_BLOCK_IE    (1 << 5)
+#  define SSI_HALF_IE    (1 << 2)
+#  define SSI_TOUT_IE    (1 << 0)
+#define SSI_GDD_CSR_REG(channel)  (0x806 + ((channel) * 0x40))
+#  define SSI_CSR_SYNC    (1 << 6)
+#  define SSI_CSR_BLOCK    (1 << 5)
+#  define SSI_CSR_HALF    (1 << 2)
+#  define SSI_CSR_TOUR    (1 << 0)
+#define SSI_GDD_CSSA_REG(channel)  (0x808 + ((channel) * 0x40))
+#define SSI_GDD_CDSA_REG(channel)  (0x80c + ((channel) * 0x40))
+#define SSI_GDD_CEN_REG(channel)  (0x810 + ((channel) * 0x40))
+#define SSI_GDD_CSAC_REG(channel)  (0x818 + ((channel) * 0x40))
+#define SSI_GDD_CDAC_REG(channel)  (0x81a + ((channel) * 0x40))
+#define SSI_GDD_CLNK_CTRL_REG(channel)  (0x828 + ((channel) * 0x40))
+#  define SSI_ENABLE_LNK    (1 << 15)
+#  define SSI_STOP_LNK    (1 << 14)
+#  define SSI_NEXT_CH_ID_MASK  0xf
+
+/**
+ * struct omap_ssi_platform_data - OMAP SSI platform data
+ * @num_ports: Number of ports on the controller
+ * @ctxt_loss_count: Pointer to omap_pm_get_dev_context_loss_count
+ */
+struct omap_ssi_platform_data {
+	unsigned int  num_ports;
+	int cawake_gpio[SSI_NUM_PORTS];
+	int (*get_dev_context_loss_count)(struct device *dev);
+};
+
+/**
+ * struct omap_ssi_config - SSI board configuration
+ * @num_ports: Number of ports in use
+ * @cawake_line: Array of cawake gpio lines
+ */
+struct omap_ssi_board_config {
+	unsigned int num_ports;
+	int cawake_gpio[SSI_NUM_PORTS];
+};
+
+#ifdef CONFIG_OMAP_SSI_CONFIG
+extern int omap_ssi_config(struct omap_ssi_board_config *ssi_config);
+#else
+static inline int omap_ssi_config(struct omap_ssi_board_config *ssi_config)
+{
+	return 0;
+}
+#endif /* CONFIG_OMAP_SSI_CONFIG */
+
+#endif /* __OMAP_SSI_REGS_H__ */
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 3/3] ARM: OMAP2+: Add SSI driver configuration
  2013-08-11 16:17 ` [PATCH 1/3] ARM: OMAP2+: hwmod-data: Add SSI information Sebastian Reichel
  2013-08-11 16:17   ` [PATCH 2/3] ARM: OMAP2+: HSI: Introduce OMAP SSI driver Sebastian Reichel
@ 2013-08-11 16:17   ` Sebastian Reichel
  2013-08-12  8:30     ` Tony Lindgren
  2013-08-12  8:26   ` [PATCH 1/3] ARM: OMAP2+: hwmod-data: Add SSI information Tony Lindgren
  2013-08-21  1:22   ` Paul Walmsley
  3 siblings, 1 reply; 15+ messages in thread
From: Sebastian Reichel @ 2013-08-11 16:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-omap, linus.walleij, Kevin Hilman, Shubhrajyoti Datta,
	Carlos Chinea, Sebastian Reichel

From: Sebastian Reichel <sre@ring0.de>

This patch configures and activates the OMAP SSI driver on the RX-51.
---
 arch/arm/mach-omap2/Makefile                 |    4 ++
 arch/arm/mach-omap2/board-rx51-peripherals.c |   10 +++-
 arch/arm/mach-omap2/ssi.c                    |   82 ++++++++++++++++++++++++++
 3 files changed, 95 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/mach-omap2/ssi.c

diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index d4f6715..ace860d 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -222,6 +222,10 @@ ifneq ($(CONFIG_DRM_OMAP),)
 obj-y					+= drm.o
 endif
 
+# Synchronous Serial Interface (SSI)
+omap-ssi-$(CONFIG_OMAP_SSI)		:= ssi.o
+obj-y					+= $(omap-ssi-m) $(omap-ssi-y)
+
 # Specific board support
 obj-$(CONFIG_MACH_OMAP_GENERIC)		+= board-generic.o
 obj-$(CONFIG_MACH_OMAP_H4)		+= board-h4.o
diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c
index 9c2dd10..e2ca155 100644
--- a/arch/arm/mach-omap2/board-rx51-peripherals.c
+++ b/arch/arm/mach-omap2/board-rx51-peripherals.c
@@ -27,6 +27,7 @@
 #include <linux/power/isp1704_charger.h>
 #include <linux/platform_data/spi-omap2-mcspi.h>
 #include <linux/platform_data/mtd-onenand-omap2.h>
+#include <linux/platform_data/hsi-omap-ssi.h>
 
 #include <asm/system_info.h>
 
@@ -73,6 +74,8 @@
 #define LIS302_IRQ1_GPIO 181
 #define LIS302_IRQ2_GPIO 180  /* Not yet in use */
 
+#define RX51_CAWAKE_GPIO 151
+
 /* List all SPI devices here. Note that the list/probe order seems to matter! */
 enum {
 	RX51_SPI_WL1251,
@@ -265,6 +268,11 @@ static struct spi_board_info rx51_peripherals_spi_board_info[] __initdata = {
 	},
 };
 
+static struct omap_ssi_board_config ssi_board_config = {
+	.num_ports = 1,
+	.cawake_gpio = { RX51_CAWAKE_GPIO },
+};
+
 static struct platform_device rx51_battery_device = {
 	.name	= "rx51-battery",
 	.id	= -1,
@@ -1295,7 +1303,7 @@ void __init rx51_peripherals_init(void)
 	if (partition)
 		omap_hsmmc_init(mmc);
 
+	omap_ssi_config(&ssi_board_config);
 	rx51_charger_init();
 	rx51_init_twl4030_hwmon();
 }
-
diff --git a/arch/arm/mach-omap2/ssi.c b/arch/arm/mach-omap2/ssi.c
new file mode 100644
index 0000000..a9d6eee
--- /dev/null
+++ b/arch/arm/mach-omap2/ssi.c
@@ -0,0 +1,82 @@
+/*
+ * linux/arch/arm/mach-omap2/ssi.c
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Carlos Chinea <carlos.chinea@nokia.com>
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/hsi-omap-ssi.h>
+#include "omap_hwmod.h"
+#include "omap_device.h"
+#include "omap-pm.h"
+
+static struct omap_ssi_platform_data ssi_pdata = {
+	.num_ports      = SSI_NUM_PORTS,
+	.cawake_gpio    = {0},
+	.get_dev_context_loss_count  = omap_pm_get_dev_context_loss_count,
+};
+
+int __init omap_ssi_config(struct omap_ssi_board_config *ssi_config)
+{
+	unsigned int port, offset, cawake_gpio;
+	int err;
+
+	ssi_pdata.num_ports = ssi_config->num_ports;
+
+	for (port = 0, offset = 7; port < ssi_config->num_ports; port++, offset += 5) {
+		cawake_gpio = ssi_config->cawake_gpio[port];
+		if (!cawake_gpio)
+			continue; /* Nothing to do */
+		err = gpio_request(cawake_gpio, "cawake");
+		if (err < 0)
+			goto rback;
+		gpio_direction_input(cawake_gpio);
+
+		ssi_pdata.cawake_gpio[port] = ssi_config->cawake_gpio[port];
+	}
+
+	return 0;
+
+rback:
+	pr_err("omap-ssi: Request cawake (gpio%d) failed\n", cawake_gpio);
+	while (port > 0)
+		gpio_free(ssi_config->cawake_gpio[--port]);
+
+	return err;
+}
+
+static int __init omap_ssi_init(void)
+{
+	struct omap_hwmod *oh;
+	struct platform_device *pdev;
+
+	oh = omap_hwmod_lookup("ssi");
+	if (!oh)
+		return -EINVAL;
+
+	pdev = omap_device_build("omap_ssi", 0, oh, &ssi_pdata, sizeof(struct omap_ssi_platform_data));
+	WARN(IS_ERR(pdev), "Can't build omap_device for omap_ssi\n");
+
+	return 0;
+}
+subsys_initcall(omap_ssi_init);
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/3] ARM: OMAP2+: hwmod-data: Add SSI information
  2013-08-11 16:17 ` [PATCH 1/3] ARM: OMAP2+: hwmod-data: Add SSI information Sebastian Reichel
  2013-08-11 16:17   ` [PATCH 2/3] ARM: OMAP2+: HSI: Introduce OMAP SSI driver Sebastian Reichel
  2013-08-11 16:17   ` [PATCH 3/3] ARM: OMAP2+: Add SSI driver configuration Sebastian Reichel
@ 2013-08-12  8:26   ` Tony Lindgren
  2013-08-21  1:22   ` Paul Walmsley
  3 siblings, 0 replies; 15+ messages in thread
From: Tony Lindgren @ 2013-08-12  8:26 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: linux-kernel, linux-omap, linus.walleij, Kevin Hilman,
	Shubhrajyoti Datta, Carlos Chinea, Sebastian Reichel, paul

* Sebastian Reichel <sre@debian.org> [130811 09:25]:
> From: Sebastian Reichel <sre@ring0.de>
> 
> This patch adds Synchronous Serial Interface (SSI) hwmod support for
> OMAP34xx SoCs.

This should be queued or acked by Paul.

Regards,

Tony

>  arch/arm/mach-omap2/omap_hwmod_3xxx_data.c |  104 ++++++++++++++++++++++++++++
>  1 file changed, 104 insertions(+)
> 
> diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
> index 0c3a427..74006c4 100644
> --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
> +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
> @@ -3693,6 +3693,109 @@ static struct omap_hwmod_ocp_if omap3xxx_l4_core__aes = {
>  	.user		= OCP_USER_MPU | OCP_USER_SDMA,
>  };
>  
> +/*
> + * 'ssi' class
> + * synchronous serial interface (multichannel and full-duplex serial if)
> + */
> +
> +static struct omap_hwmod_class_sysconfig omap34xx_ssi_sysc = {
> +	.rev_offs	= 0x0000,
> +	.sysc_offs	= 0x0010,
> +	.syss_offs	= 0x0014,
> +	.sysc_flags	= (SYSC_HAS_AUTOIDLE | SYSC_HAS_EMUFREE |
> +			   SYSC_HAS_MIDLEMODE | SYSC_HAS_SIDLEMODE |
> +			   SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
> +	.idlemodes	= (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
> +			   SIDLE_SMART_WKUP | MSTANDBY_FORCE | MSTANDBY_NO |
> +			   MSTANDBY_SMART | MSTANDBY_SMART_WKUP),
> +	.sysc_fields	= &omap_hwmod_sysc_type1,
> +};
> +
> +static struct omap_hwmod_class omap34xx_ssi_hwmod_class = {
> +	.name	= "ssi",
> +	.sysc	= &omap34xx_ssi_sysc,
> +};
> +
> +static struct omap_hwmod_irq_info omap34xx_ssi_irqs[] = {
> +	{ .name = "ssi_p1_mpu_irq0", .irq = 67 },
> +	{ .name = "ssi_p1_mpu_irq1", .irq = 68 },
> +	{ .name = "ssi_p2_mpu_irq0", .irq = 69 },
> +	{ .name = "ssi_p2_mpu_irq1", .irq = 70 },
> +	{ .name = "ssi_gdd_mpu",     .irq = 71 },
> +	{ .irq = -1 },
> +};
> +
> +static struct omap_hwmod_addr_space omap34xx_ssi_addrs[] = {
> +	{
> +		.name		= "sys",
> +		.pa_start	= 0x48058000,
> +		.pa_end		= 0x48058fff,
> +		.flags		= ADDR_TYPE_RT,
> +	},
> +	{
> +		/* generic distributed DMA */
> +		.name		= "gdd",
> +		.pa_start	= 0x48059000,
> +		.pa_end		= 0x48059fff,
> +		.flags		= ADDR_TYPE_RT,
> +	},
> +	{
> +		/* port 1: synchronous serial transmitter */
> +		.name		= "p1_sst",
> +		.pa_start	= 0x4805a000,
> +		.pa_end		= 0x4805a7ff,
> +		.flags		= ADDR_TYPE_RT,
> +	},
> +	{
> +		/* port 1: synchronous serial receiver */
> +		.name		= "p1_ssr",
> +		.pa_start	= 0x4805a800,
> +		.pa_end		= 0x4805afff,
> +		.flags		= ADDR_TYPE_RT,
> +	},
> +	{
> +		/* port 2: synchronous serial transmitter */
> +		.name		= "p2_sst",
> +		.pa_start	= 0x4805b000,
> +		.pa_end		= 0x4805b7ff,
> +		.flags		= ADDR_TYPE_RT,
> +	},
> +	{
> +		/* port 2: synchronous serial receiver */
> +		.name		= "p2_ssr",
> +		.pa_start	= 0x4805b800,
> +		.pa_end		= 0x4805bfff,
> +		.flags		= ADDR_TYPE_RT,
> +	},
> +	{}
> +};
> +
> +static struct omap_hwmod omap34xx_ssi_hwmod = {
> +	.name		= "ssi",
> +	.class		= &omap34xx_ssi_hwmod_class,
> +	.clkdm_name	= "l3_init_clkdm",
> +	.mpu_irqs	= omap34xx_ssi_irqs,
> +	.main_clk	= "ssi_ssr_fck",
> +	.prcm		= {
> +		.omap2 = {
> +			.prcm_reg_id		= 1,
> +			.module_bit		= OMAP3430_EN_SSI_SHIFT,
> +			.module_offs		= WKUP_MOD,
> +			.idlest_reg_id		= 1,
> +			.idlest_idle_bit	= OMAP3430ES2_ST_SSI_IDLE_SHIFT,
> +		},
> +	},
> +};
> +
> +/* SSI -> l3 */
> +static struct omap_hwmod_ocp_if omap34xx_l3__ssi = {
> +	.master		= &omap3xxx_l3_main_hwmod,
> +	.slave		= &omap34xx_ssi_hwmod,
> +	.clk		= "ssi_ick",
> +	.addr		= omap34xx_ssi_addrs,
> +	.user		= OCP_USER_MPU | OCP_USER_SDMA,
> +};
> +
>  static struct omap_hwmod_ocp_if *omap3xxx_hwmod_ocp_ifs[] __initdata = {
>  	&omap3xxx_l3_main__l4_core,
>  	&omap3xxx_l3_main__l4_per,
> @@ -3818,6 +3921,7 @@ static struct omap_hwmod_ocp_if *omap34xx_hwmod_ocp_ifs[] __initdata = {
>  #ifdef CONFIG_OMAP_IOMMU_IVA2
>  	&omap3xxx_l3_main__mmu_iva,
>  #endif
> +	&omap34xx_l3__ssi,
>  	NULL
>  };
>  
> -- 
> 1.7.10.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/3] ARM: OMAP2+: HSI: Introduce OMAP SSI driver
  2013-08-11 16:17   ` [PATCH 2/3] ARM: OMAP2+: HSI: Introduce OMAP SSI driver Sebastian Reichel
@ 2013-08-12  8:28     ` Tony Lindgren
  2013-08-23 13:57     ` Linus Walleij
  1 sibling, 0 replies; 15+ messages in thread
From: Tony Lindgren @ 2013-08-12  8:28 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: linux-kernel, linux-omap, linus.walleij, Kevin Hilman,
	Shubhrajyoti Datta, Carlos Chinea, Sebastian Reichel

* Sebastian Reichel <sre@debian.org> [130811 09:25]:
> From: Sebastian Reichel <sre@ring0.de>
> 
> This patch adds an OMAP SSI driver to the HSI framework.
> 
> The Synchronous Serial Interface (SSI) is a legacy version
> of HSI. As in the case of HSI, it is mainly used to connect
> Application engines (APE) with cellular modem engines (CMT)
> in cellular handsets.
> 
> It provides a multichannel, full-duplex, multi-core communication
> with no reference clock. The OMAP SSI block is capable of reaching
> speeds of 110 Mbit/s.

Good to see this being worked on, maybe we can finally start
making calls with mainline kernel :)

This one should be queued by:

$ scripts/get_maintainer.pl -f drivers/hsi

This should be a stand alone patch, right? You should resend
with those people in cc.

Regards,

Tony

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/3] ARM: OMAP2+: Add SSI driver configuration
  2013-08-11 16:17   ` [PATCH 3/3] ARM: OMAP2+: Add SSI driver configuration Sebastian Reichel
@ 2013-08-12  8:30     ` Tony Lindgren
  2013-08-23 13:58       ` Linus Walleij
  0 siblings, 1 reply; 15+ messages in thread
From: Tony Lindgren @ 2013-08-12  8:30 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: linux-kernel, linux-omap, linus.walleij, Kevin Hilman,
	Shubhrajyoti Datta, Carlos Chinea, Sebastian Reichel

* Sebastian Reichel <sre@debian.org> [130811 09:25]:
> From: Sebastian Reichel <sre@ring0.de>
> 
> This patch configures and activates the OMAP SSI driver on the RX-51.

Hmm, I'd rather see this be DT only driver at this point. We're pretty
close to flipping omap3 to be DT only, and I'd rather not add new
platform code at this point. With DT, none of this patch is needed,
you can do all this in the driver probe based on the .dts configuration.

Regards,

Tony

>  arch/arm/mach-omap2/Makefile                 |    4 ++
>  arch/arm/mach-omap2/board-rx51-peripherals.c |   10 +++-
>  arch/arm/mach-omap2/ssi.c                    |   82 ++++++++++++++++++++++++++
>  3 files changed, 95 insertions(+), 1 deletion(-)
>  create mode 100644 arch/arm/mach-omap2/ssi.c
> 
> diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
> index d4f6715..ace860d 100644
> --- a/arch/arm/mach-omap2/Makefile
> +++ b/arch/arm/mach-omap2/Makefile
> @@ -222,6 +222,10 @@ ifneq ($(CONFIG_DRM_OMAP),)
>  obj-y					+= drm.o
>  endif
>  
> +# Synchronous Serial Interface (SSI)
> +omap-ssi-$(CONFIG_OMAP_SSI)		:= ssi.o
> +obj-y					+= $(omap-ssi-m) $(omap-ssi-y)
> +
>  # Specific board support
>  obj-$(CONFIG_MACH_OMAP_GENERIC)		+= board-generic.o
>  obj-$(CONFIG_MACH_OMAP_H4)		+= board-h4.o
> diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c
> index 9c2dd10..e2ca155 100644
> --- a/arch/arm/mach-omap2/board-rx51-peripherals.c
> +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c
> @@ -27,6 +27,7 @@
>  #include <linux/power/isp1704_charger.h>
>  #include <linux/platform_data/spi-omap2-mcspi.h>
>  #include <linux/platform_data/mtd-onenand-omap2.h>
> +#include <linux/platform_data/hsi-omap-ssi.h>
>  
>  #include <asm/system_info.h>
>  
> @@ -73,6 +74,8 @@
>  #define LIS302_IRQ1_GPIO 181
>  #define LIS302_IRQ2_GPIO 180  /* Not yet in use */
>  
> +#define RX51_CAWAKE_GPIO 151
> +
>  /* List all SPI devices here. Note that the list/probe order seems to matter! */
>  enum {
>  	RX51_SPI_WL1251,
> @@ -265,6 +268,11 @@ static struct spi_board_info rx51_peripherals_spi_board_info[] __initdata = {
>  	},
>  };
>  
> +static struct omap_ssi_board_config ssi_board_config = {
> +	.num_ports = 1,
> +	.cawake_gpio = { RX51_CAWAKE_GPIO },
> +};
> +
>  static struct platform_device rx51_battery_device = {
>  	.name	= "rx51-battery",
>  	.id	= -1,
> @@ -1295,7 +1303,7 @@ void __init rx51_peripherals_init(void)
>  	if (partition)
>  		omap_hsmmc_init(mmc);
>  
> +	omap_ssi_config(&ssi_board_config);
>  	rx51_charger_init();
>  	rx51_init_twl4030_hwmon();
>  }
> -
> diff --git a/arch/arm/mach-omap2/ssi.c b/arch/arm/mach-omap2/ssi.c
> new file mode 100644
> index 0000000..a9d6eee
> --- /dev/null
> +++ b/arch/arm/mach-omap2/ssi.c
> @@ -0,0 +1,82 @@
> +/*
> + * linux/arch/arm/mach-omap2/ssi.c
> + *
> + * Copyright (C) 2010 Nokia Corporation. All rights reserved.
> + *
> + * Contact: Carlos Chinea <carlos.chinea@nokia.com>
> + *
> + * 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.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/err.h>
> +#include <linux/gpio.h>
> +#include <linux/platform_device.h>
> +#include <linux/platform_data/hsi-omap-ssi.h>
> +#include "omap_hwmod.h"
> +#include "omap_device.h"
> +#include "omap-pm.h"
> +
> +static struct omap_ssi_platform_data ssi_pdata = {
> +	.num_ports      = SSI_NUM_PORTS,
> +	.cawake_gpio    = {0},
> +	.get_dev_context_loss_count  = omap_pm_get_dev_context_loss_count,
> +};
> +
> +int __init omap_ssi_config(struct omap_ssi_board_config *ssi_config)
> +{
> +	unsigned int port, offset, cawake_gpio;
> +	int err;
> +
> +	ssi_pdata.num_ports = ssi_config->num_ports;
> +
> +	for (port = 0, offset = 7; port < ssi_config->num_ports; port++, offset += 5) {
> +		cawake_gpio = ssi_config->cawake_gpio[port];
> +		if (!cawake_gpio)
> +			continue; /* Nothing to do */
> +		err = gpio_request(cawake_gpio, "cawake");
> +		if (err < 0)
> +			goto rback;
> +		gpio_direction_input(cawake_gpio);
> +
> +		ssi_pdata.cawake_gpio[port] = ssi_config->cawake_gpio[port];
> +	}
> +
> +	return 0;
> +
> +rback:
> +	pr_err("omap-ssi: Request cawake (gpio%d) failed\n", cawake_gpio);
> +	while (port > 0)
> +		gpio_free(ssi_config->cawake_gpio[--port]);
> +
> +	return err;
> +}
> +
> +static int __init omap_ssi_init(void)
> +{
> +	struct omap_hwmod *oh;
> +	struct platform_device *pdev;
> +
> +	oh = omap_hwmod_lookup("ssi");
> +	if (!oh)
> +		return -EINVAL;
> +
> +	pdev = omap_device_build("omap_ssi", 0, oh, &ssi_pdata, sizeof(struct omap_ssi_platform_data));
> +	WARN(IS_ERR(pdev), "Can't build omap_device for omap_ssi\n");
> +
> +	return 0;
> +}
> +subsys_initcall(omap_ssi_init);
> -- 
> 1.7.10.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/3] ARM: OMAP2+: hwmod-data: Add SSI information
  2013-08-11 16:17 ` [PATCH 1/3] ARM: OMAP2+: hwmod-data: Add SSI information Sebastian Reichel
                     ` (2 preceding siblings ...)
  2013-08-12  8:26   ` [PATCH 1/3] ARM: OMAP2+: hwmod-data: Add SSI information Tony Lindgren
@ 2013-08-21  1:22   ` Paul Walmsley
  2013-08-23 18:29     ` Sebastian Reichel
  3 siblings, 1 reply; 15+ messages in thread
From: Paul Walmsley @ 2013-08-21  1:22 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: linux-kernel, linux-omap, linus.walleij, Kevin Hilman,
	Shubhrajyoti Datta, Carlos Chinea, Sebastian Reichel

Hi Sebastian

On Sun, 11 Aug 2013, Sebastian Reichel wrote:

> From: Sebastian Reichel <sre@ring0.de>
> 
> This patch adds Synchronous Serial Interface (SSI) hwmod support for
> OMAP34xx SoCs.

a few comments:

- please add your Signed-off-by: to the patch description, per 
Documentation/SubmittingPatches

- please drop "omap34xx_ssi_irqs" and "omap34xx_ssi_addrs" - that data 
should go into DT now

The rest looks OK to me, based on a brief look.

... 

FYI, this is stretching my recollection, and is not directly related to 
this patch, but I seem to recall that there was a problem with auto-idle 
when SSI was active on the 3430.  Basically, if the SSI was active, but 
other CORE_L3/CORE_L4 clockdomain devices weren't, I think that DPLL3 
would be incorrectly shut down.  You'll probably need to check the Nokia 
kernel source for details.  I thought we had a hwmod flag for this, but I 
guess not.  Anyway, in case you notice odd behavior...


- Paul

> ---
>  arch/arm/mach-omap2/omap_hwmod_3xxx_data.c |  104 ++++++++++++++++++++++++++++
>  1 file changed, 104 insertions(+)
> 
> diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
> index 0c3a427..74006c4 100644
> --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
> +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
> @@ -3693,6 +3693,109 @@ static struct omap_hwmod_ocp_if omap3xxx_l4_core__aes = {
>  	.user		= OCP_USER_MPU | OCP_USER_SDMA,
>  };
>  
> +/*
> + * 'ssi' class
> + * synchronous serial interface (multichannel and full-duplex serial if)
> + */
> +
> +static struct omap_hwmod_class_sysconfig omap34xx_ssi_sysc = {
> +	.rev_offs	= 0x0000,
> +	.sysc_offs	= 0x0010,
> +	.syss_offs	= 0x0014,
> +	.sysc_flags	= (SYSC_HAS_AUTOIDLE | SYSC_HAS_EMUFREE |
> +			   SYSC_HAS_MIDLEMODE | SYSC_HAS_SIDLEMODE |
> +			   SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
> +	.idlemodes	= (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
> +			   SIDLE_SMART_WKUP | MSTANDBY_FORCE | MSTANDBY_NO |
> +			   MSTANDBY_SMART | MSTANDBY_SMART_WKUP),
> +	.sysc_fields	= &omap_hwmod_sysc_type1,
> +};
> +
> +static struct omap_hwmod_class omap34xx_ssi_hwmod_class = {
> +	.name	= "ssi",
> +	.sysc	= &omap34xx_ssi_sysc,
> +};
> +
> +static struct omap_hwmod_irq_info omap34xx_ssi_irqs[] = {
> +	{ .name = "ssi_p1_mpu_irq0", .irq = 67 },
> +	{ .name = "ssi_p1_mpu_irq1", .irq = 68 },
> +	{ .name = "ssi_p2_mpu_irq0", .irq = 69 },
> +	{ .name = "ssi_p2_mpu_irq1", .irq = 70 },
> +	{ .name = "ssi_gdd_mpu",     .irq = 71 },
> +	{ .irq = -1 },
> +};
> +
> +static struct omap_hwmod_addr_space omap34xx_ssi_addrs[] = {
> +	{
> +		.name		= "sys",
> +		.pa_start	= 0x48058000,
> +		.pa_end		= 0x48058fff,
> +		.flags		= ADDR_TYPE_RT,
> +	},
> +	{
> +		/* generic distributed DMA */
> +		.name		= "gdd",
> +		.pa_start	= 0x48059000,
> +		.pa_end		= 0x48059fff,
> +		.flags		= ADDR_TYPE_RT,
> +	},
> +	{
> +		/* port 1: synchronous serial transmitter */
> +		.name		= "p1_sst",
> +		.pa_start	= 0x4805a000,
> +		.pa_end		= 0x4805a7ff,
> +		.flags		= ADDR_TYPE_RT,
> +	},
> +	{
> +		/* port 1: synchronous serial receiver */
> +		.name		= "p1_ssr",
> +		.pa_start	= 0x4805a800,
> +		.pa_end		= 0x4805afff,
> +		.flags		= ADDR_TYPE_RT,
> +	},
> +	{
> +		/* port 2: synchronous serial transmitter */
> +		.name		= "p2_sst",
> +		.pa_start	= 0x4805b000,
> +		.pa_end		= 0x4805b7ff,
> +		.flags		= ADDR_TYPE_RT,
> +	},
> +	{
> +		/* port 2: synchronous serial receiver */
> +		.name		= "p2_ssr",
> +		.pa_start	= 0x4805b800,
> +		.pa_end		= 0x4805bfff,
> +		.flags		= ADDR_TYPE_RT,
> +	},
> +	{}
> +};
> +
> +static struct omap_hwmod omap34xx_ssi_hwmod = {
> +	.name		= "ssi",
> +	.class		= &omap34xx_ssi_hwmod_class,
> +	.clkdm_name	= "l3_init_clkdm",
> +	.mpu_irqs	= omap34xx_ssi_irqs,
> +	.main_clk	= "ssi_ssr_fck",
> +	.prcm		= {
> +		.omap2 = {
> +			.prcm_reg_id		= 1,
> +			.module_bit		= OMAP3430_EN_SSI_SHIFT,
> +			.module_offs		= WKUP_MOD,
> +			.idlest_reg_id		= 1,
> +			.idlest_idle_bit	= OMAP3430ES2_ST_SSI_IDLE_SHIFT,
> +		},
> +	},
> +};
> +
> +/* SSI -> l3 */
> +static struct omap_hwmod_ocp_if omap34xx_l3__ssi = {
> +	.master		= &omap3xxx_l3_main_hwmod,
> +	.slave		= &omap34xx_ssi_hwmod,
> +	.clk		= "ssi_ick",
> +	.addr		= omap34xx_ssi_addrs,
> +	.user		= OCP_USER_MPU | OCP_USER_SDMA,
> +};
> +
>  static struct omap_hwmod_ocp_if *omap3xxx_hwmod_ocp_ifs[] __initdata = {
>  	&omap3xxx_l3_main__l4_core,
>  	&omap3xxx_l3_main__l4_per,
> @@ -3818,6 +3921,7 @@ static struct omap_hwmod_ocp_if *omap34xx_hwmod_ocp_ifs[] __initdata = {
>  #ifdef CONFIG_OMAP_IOMMU_IVA2
>  	&omap3xxx_l3_main__mmu_iva,
>  #endif
> +	&omap34xx_l3__ssi,
>  	NULL
>  };
>  
> -- 
> 1.7.10.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


- Paul

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/3] ARM: OMAP2+: HSI: Introduce OMAP SSI driver
  2013-08-11 16:17   ` [PATCH 2/3] ARM: OMAP2+: HSI: Introduce OMAP SSI driver Sebastian Reichel
  2013-08-12  8:28     ` Tony Lindgren
@ 2013-08-23 13:57     ` Linus Walleij
  2013-08-23 18:17       ` Sebastian Reichel
  1 sibling, 1 reply; 15+ messages in thread
From: Linus Walleij @ 2013-08-23 13:57 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: linux-kernel, Linux-OMAP, Kevin Hilman, Shubhrajyoti Datta,
	Carlos Chinea, Sebastian Reichel

On Sun, Aug 11, 2013 at 6:17 PM, Sebastian Reichel <sre@debian.org> wrote:

> From: Sebastian Reichel <sre@ring0.de>
>
> This patch adds an OMAP SSI driver to the HSI framework.
>
> The Synchronous Serial Interface (SSI) is a legacy version
> of HSI. As in the case of HSI, it is mainly used to connect
> Application engines (APE) with cellular modem engines (CMT)
> in cellular handsets.
>
> It provides a multichannel, full-duplex, multi-core communication
> with no reference clock. The OMAP SSI block is capable of reaching
> speeds of 110 Mbit/s.

First: good to see this.

The HSI subsystem is lacking an active maintainer, interested?
Given that you can apparently test the OMAP HSI driver you're
one of the few applicable candidates.

Overall there is one big problem with this patch in that it implements
a lot of stuff that should not be implemented in the driver at all,
but in the HSI core.

For example compare commit
ffbbdd21329f3e15eeca6df2d4bc11c04d9d91c0
"spi: create a message queueing infrastructure"

This patch basically seems to redo the mistake we did in
SPI and not create a central message queue from day one,
instead re-implementing the same code in each and every
driver.

Please attempt to draw the message queueuing into the
driver core atlease.

Further the allocation of hosts seem pretty generic as well
but I'm unsure about this. I'd prefer if you take a second
look at the generalizeable parts.

(...)
>> +       spinlock_t              wk_lock;
> +       spinlock_t              lock;
> +       unsigned int            channels;
> +       struct list_head        txqueue[SSI_MAX_CHANNELS];
> +       struct list_head        rxqueue[SSI_MAX_CHANNELS];
> +       struct list_head        brkqueue;
> +       unsigned int            irq;
> +       int                     wake_irq;
> +       int                     wake_gpio;
> +       struct tasklet_struct   pio_tasklet;
> +       struct tasklet_struct   wake_tasklet;
> +       unsigned int            wktest:1; /* FIXME: HACK to be removed */
> +       unsigned int            wkin_cken:1; /* Workaround */

Atleast make them into the bool type.

> +       int                     wk_refcount;

A refcount does not seem like it coul be negative. Should
this be unsigned?

Since these seem to be associated with a piece of code
that only runs when this goes to 0, why not just use kref?

> +       /* OMAP SSI port context */
> +       u32                     sys_mpu_enable; /* We use only one irq */
> +       struct omap_ssm_ctx     sst;
> +       struct omap_ssm_ctx     ssr;
> +};
> +
> +/**
> + * struct omap_ssi_controller - OMAP SSI controller data
> + * @dev: device associated to the controller (HSI controller)
> + * @sys: SSI I/O base address
> + * @gdd: GDD I/O base address
> + * @ick: SSI interconnect clock
> + * @fck: SSI functional clock
> + * @ck_refcount: References count for clocks
> + * @gdd_irq: IRQ line for GDD
> + * @gdd_tasklet: bottom half for DMA transfers
> + * @gdd_trn: Array of GDD transaction data for ongoing GDD transfers
> + * @lock: lock to serialize access to GDD
> + * @ck_lock: lock to serialize access to the clocks
> + * @loss_count: To follow if we need to restore context or not
> + * @max_speed: Maximum TX speed (Kb/s) set by the clients.
> + * @sysconfig: SSI controller saved context
> + * @gdd_gcr: SSI GDD saved context
> + * @get_loss: Pointer to omap_pm_get_dev_context_loss_count, if any
> + * @port: Array of pointers of the ports of the controller
> + * @dir: Debugfs SSI root directory
> + */
> +struct omap_ssi_controller {
> +       struct device           *dev;
> +       void __iomem            *sys;
> +       void __iomem            *gdd;
> +       struct clk              *ick;
> +       struct clk              *fck;
> +       int                     ck_refcount;

Unsigned?

Since these seem to be associated with a piece of code
that only runs when this goes to 0, why not just use kref?

(...)
> +static int ssi_set_port_mode(struct omap_ssi_port *omap_port, void *data)
> +{
> +       u32 *mode = data;
> +
> +       __raw_writel(*mode, omap_port->sst_base + SSI_SST_MODE_REG);
> +       __raw_writel(*mode, omap_port->ssr_base + SSI_SSR_MODE_REG);
> +       /* OCP barrier */
> +       *mode = __raw_readl(omap_port->ssr_base + SSI_SSR_MODE_REG);
> +
> +       return 0;
> +}

Why do you insist on using __raw_readl/writel()? I can understand
that you want to use readl/writel_relaxed() but __raw* is just
ugly and confusing.

The last in a series of writel should probably be a plain
writel() so as to flush buffers etc.

Comment applies to every instance of these.

> +static u32 ssi_calculate_div(struct hsi_controller *ssi)
> +{
> +       struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
> +       u32 tx_fckrate = (u32) omap_ssi->fck_rate;
> +
> +       /* / 2 : SSI TX clock is always half of the SSI functional clock */
> +       tx_fckrate >>= 1;
> +       /* Round down when tx_fckrate % omap_ssi->max_speed == 0 */
> +       tx_fckrate--;
> +       dev_dbg(&ssi->device, "TX div %d for fck_rate %lu Khz speed %d Kb/s\n",
> +                       tx_fckrate / omap_ssi->max_speed, omap_ssi->fck_rate,
> +                                                       omap_ssi->max_speed);
> +
> +       return tx_fckrate / omap_ssi->max_speed;

Don't you want to use:
DIV_ROUND_CLOSEST(tx_fckrate, omap_ssi->max_speed)
?

(...)
> +static int ssi_setup(struct hsi_client *cl)
> +{
(...)
> +       /* Set TX/RX module to sleep to stop TX/RX during cfg update */
> +       __raw_writel(SSI_MODE_SLEEP, sst + SSI_SST_MODE_REG);
> +       __raw_writel(SSI_MODE_SLEEP, ssr + SSI_SSR_MODE_REG);
> +       /* Flush posted write */
> +       val = __raw_readl(ssr + SSI_SSR_MODE_REG);
> +       /* TX */
> +       __raw_writel(31, sst + SSI_SST_FRAMESIZE_REG);

31??

Use a #define for this, I have no clue why this is 31.

(...)
> +       __raw_writel(31, ssr + SSI_SSR_FRAMESIZE_REG);
(...)
> +       omap_port->sst.frame_size = 31;
(...)
> +       omap_port->ssr.frame_size = 31;

Seems like some universal constant.

> +static int ssi_flush(struct hsi_client *cl)
(...)
> +       /* Clear interrupts */
> +       __raw_writel(0, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
> +       __raw_writel(0xffffff00,
> +                       omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
> +       __raw_writel(0, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
> +       __raw_writel(0xff, omap_ssi->sys + SSI_GDD_MPU_IRQ_STATUS_REG);

Here are other magic numbers 0xfffff00, 0xff...

(...)
> +static int __init ssi_hw_init(struct hsi_controller *ssi)
> +{
> +       struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
> +       unsigned int i;
> +       u32 val;
> +       int err;
> +
> +       err = ssi_clk_enable(ssi);
> +       if (err < 0) {
> +               dev_err(&ssi->device, "Failed to enable the clocks %d\n", err);
> +               return err;
> +       }
> +       /* Reseting SSI controller */
> +       __raw_writel(SSI_SOFTRESET, omap_ssi->sys + SSI_SYSCONFIG_REG);
> +       val = __raw_readl(omap_ssi->sys + SSI_SYSSTATUS_REG);
> +       for (i = 0; ((i < 20) && !(val & SSI_RESETDONE)); i++) {
> +               msleep(20);
> +               val = __raw_readl(omap_ssi->sys + SSI_SYSSTATUS_REG);
> +       }

Explain constants chosen in a comment. From datasheet?

> +       /* Reseting GDD */
> +       __raw_writel(SSI_SWRESET, omap_ssi->gdd + SSI_GDD_GRST_REG);
> +       /* Get FCK rate */
> +       omap_ssi->fck_rate = ssi_get_clk_rate(ssi) / 1000; /* KHz */

DIV_ROUND_CLOSEST()? Or is this an integer divider?

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/3] ARM: OMAP2+: Add SSI driver configuration
  2013-08-12  8:30     ` Tony Lindgren
@ 2013-08-23 13:58       ` Linus Walleij
  2013-08-23 18:20         ` Sebastian Reichel
  0 siblings, 1 reply; 15+ messages in thread
From: Linus Walleij @ 2013-08-23 13:58 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Sebastian Reichel, linux-kernel, Linux-OMAP, Kevin Hilman,
	Shubhrajyoti Datta, Carlos Chinea, Sebastian Reichel

On Mon, Aug 12, 2013 at 10:30 AM, Tony Lindgren <tony@atomide.com> wrote:
> * Sebastian Reichel <sre@debian.org> [130811 09:25]:
>> From: Sebastian Reichel <sre@ring0.de>
>>
>> This patch configures and activates the OMAP SSI driver on the RX-51.
>
> Hmm, I'd rather see this be DT only driver at this point.

+1 on this.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/3] ARM: OMAP2+: HSI: Introduce OMAP SSI driver
  2013-08-23 13:57     ` Linus Walleij
@ 2013-08-23 18:17       ` Sebastian Reichel
  0 siblings, 0 replies; 15+ messages in thread
From: Sebastian Reichel @ 2013-08-23 18:17 UTC (permalink / raw)
  To: Linus Walleij
  Cc: linux-kernel, Linux-OMAP, Kevin Hilman, Shubhrajyoti Datta,
	Carlos Chinea

[-- Attachment #1: Type: text/plain, Size: 1911 bytes --]

Hi,

On Fri, Aug 23, 2013 at 03:57:05PM +0200, Linus Walleij wrote:
> The HSI subsystem is lacking an active maintainer, interested?
> Given that you can apparently test the OMAP HSI driver you're
> one of the few applicable candidates.

I don't think I'm a good candidate for that. At least not yet. This
is my first patch sent to the kernel and I merly took the code from
Carlos Chinea and ported it to some new kernel frameworks. Apart
from that I'm lacking any documentation for the interface (SSI is
not part of TI's public OMAP3 TRM).

> Overall there is one big problem with this patch in that it implements
> a lot of stuff that should not be implemented in the driver at all,
> but in the HSI core.

The actual implementation has not (yet?) been changed by me. It's
still the code as written by Carlos.

> For example compare commit
> ffbbdd21329f3e15eeca6df2d4bc11c04d9d91c0
> "spi: create a message queueing infrastructure"
> 
> This patch basically seems to redo the mistake we did in
> SPI and not create a central message queue from day one,
> instead re-implementing the same code in each and every
> driver.
> 
> Please attempt to draw the message queueuing into the
> driver core atlease.

I will have a look at it after conversion to DT. I will sent another
RFC before this change to speed up the DT review, though.

> Further the allocation of hosts seem pretty generic as well
> but I'm unsure about this. I'd prefer if you take a second
> look at the generalizeable parts.

OK.

> [...] (Code Comments)

Thanks for the review. The code has been written by Carlos, who
apparently had documentation for the SSI IP used in the OMAP3. I
cannot fix the magic numbers without either reverse engineering the
IP (which I do not have the time for) or access to the documentation.

I will fix the other comments in the next RFC patch.

-- Sebastian

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/3] ARM: OMAP2+: Add SSI driver configuration
  2013-08-23 13:58       ` Linus Walleij
@ 2013-08-23 18:20         ` Sebastian Reichel
  2013-08-26  8:52           ` Tony Lindgren
  0 siblings, 1 reply; 15+ messages in thread
From: Sebastian Reichel @ 2013-08-23 18:20 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Tony Lindgren, linux-kernel, Linux-OMAP, Kevin Hilman,
	Shubhrajyoti Datta, Carlos Chinea

[-- Attachment #1: Type: text/plain, Size: 461 bytes --]

On Fri, Aug 23, 2013 at 03:58:30PM +0200, Linus Walleij wrote:
> On Mon, Aug 12, 2013 at 10:30 AM, Tony Lindgren <tony@atomide.com> wrote:
> > * Sebastian Reichel <sre@debian.org> [130811 09:25]:
> >> From: Sebastian Reichel <sre@ring0.de>
> >>
> >> This patch configures and activates the OMAP SSI driver on the RX-51.
> >
> > Hmm, I'd rather see this be DT only driver at this point.
> 
> +1 on this.

I'm already working on this.

-- Sebastian

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/3] ARM: OMAP2+: hwmod-data: Add SSI information
  2013-08-21  1:22   ` Paul Walmsley
@ 2013-08-23 18:29     ` Sebastian Reichel
  2013-08-23 18:53       ` Paul Walmsley
  0 siblings, 1 reply; 15+ messages in thread
From: Sebastian Reichel @ 2013-08-23 18:29 UTC (permalink / raw)
  To: Paul Walmsley
  Cc: linux-kernel, linux-omap, linus.walleij, Kevin Hilman,
	Shubhrajyoti Datta, Carlos Chinea

[-- Attachment #1: Type: text/plain, Size: 1275 bytes --]

Hi Paul,

On Wed, Aug 21, 2013 at 01:22:09AM +0000, Paul Walmsley wrote:
> > From: Sebastian Reichel <sre@ring0.de>
> > This patch adds Synchronous Serial Interface (SSI) hwmod support for
> > OMAP34xx SoCs.
> 
> a few comments:
> 
> - please add your Signed-off-by: to the patch description, per 
> Documentation/SubmittingPatches

I assumed this was only important, when I actually want to submit
patches.  This was merely a first RFC. I will do this for the next
round of patches.

> - please drop "omap34xx_ssi_irqs" and "omap34xx_ssi_addrs" - that data 
> should go into DT now

I'm currently working on converting the driver to use DT.
I will send another patch when I'm done.

> [...]
> FYI, this is stretching my recollection, and is not directly related to 
> this patch, but I seem to recall that there was a problem with auto-idle 
> when SSI was active on the 3430.  Basically, if the SSI was active, but 
> other CORE_L3/CORE_L4 clockdomain devices weren't, I think that DPLL3 
> would be incorrectly shut down.  You'll probably need to check the Nokia 
> kernel source for details.  I thought we had a hwmod flag for this, but I 
> guess not.  Anyway, in case you notice odd behavior...

OK. Thanks for the hint.

-- Sebastian

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/3] ARM: OMAP2+: hwmod-data: Add SSI information
  2013-08-23 18:29     ` Sebastian Reichel
@ 2013-08-23 18:53       ` Paul Walmsley
  0 siblings, 0 replies; 15+ messages in thread
From: Paul Walmsley @ 2013-08-23 18:53 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: linux-kernel, linux-omap, linus.walleij, Kevin Hilman,
	Shubhrajyoti Datta, Carlos Chinea

Hi

On Fri, 23 Aug 2013, Sebastian Reichel wrote:

> On Wed, Aug 21, 2013 at 01:22:09AM +0000, Paul Walmsley wrote:
> > > From: Sebastian Reichel <sre@ring0.de>
> > > This patch adds Synchronous Serial Interface (SSI) hwmod support for
> > > OMAP34xx SoCs.
> > 
> > a few comments:
> > 
> > - please add your Signed-off-by: to the patch description, per 
> > Documentation/SubmittingPatches
> 
> I assumed this was only important, when I actually want to submit
> patches.  This was merely a first RFC. I will do this for the next
> round of patches.

OK makes sense.  Just FYI, usually when folks send out RFCs, they put 
"RFC" in the patch description for each patch.  So the subject line might 
read:

[RFC PATCH 1/3] ARM: OMAP2+: hwmod-data: Add SSI information

> > - please drop "omap34xx_ssi_irqs" and "omap34xx_ssi_addrs" - that data 
> > should go into DT now
> 
> I'm currently working on converting the driver to use DT.
> I will send another patch when I'm done.

OK great.


- Paul

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/3] ARM: OMAP2+: Add SSI driver configuration
  2013-08-23 18:20         ` Sebastian Reichel
@ 2013-08-26  8:52           ` Tony Lindgren
  0 siblings, 0 replies; 15+ messages in thread
From: Tony Lindgren @ 2013-08-26  8:52 UTC (permalink / raw)
  To: Linus Walleij, linux-kernel, Linux-OMAP, Kevin Hilman,
	Shubhrajyoti Datta, Carlos Chinea

* Sebastian Reichel <sre@debian.org> [130823 11:27]:
> On Fri, Aug 23, 2013 at 03:58:30PM +0200, Linus Walleij wrote:
> > On Mon, Aug 12, 2013 at 10:30 AM, Tony Lindgren <tony@atomide.com> wrote:
> > > * Sebastian Reichel <sre@debian.org> [130811 09:25]:
> > >> From: Sebastian Reichel <sre@ring0.de>
> > >>
> > >> This patch configures and activates the OMAP SSI driver on the RX-51.
> > >
> > > Hmm, I'd rather see this be DT only driver at this point.
> > 
> > +1 on this.
> 
> I'm already working on this.

Thanks good to hear!

Tony

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2013-08-26  8:52 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-08-11 16:13 [RFC PATCH 0/3] OMAP SSI driver Sebastian Reichel
2013-08-11 16:17 ` [PATCH 1/3] ARM: OMAP2+: hwmod-data: Add SSI information Sebastian Reichel
2013-08-11 16:17   ` [PATCH 2/3] ARM: OMAP2+: HSI: Introduce OMAP SSI driver Sebastian Reichel
2013-08-12  8:28     ` Tony Lindgren
2013-08-23 13:57     ` Linus Walleij
2013-08-23 18:17       ` Sebastian Reichel
2013-08-11 16:17   ` [PATCH 3/3] ARM: OMAP2+: Add SSI driver configuration Sebastian Reichel
2013-08-12  8:30     ` Tony Lindgren
2013-08-23 13:58       ` Linus Walleij
2013-08-23 18:20         ` Sebastian Reichel
2013-08-26  8:52           ` Tony Lindgren
2013-08-12  8:26   ` [PATCH 1/3] ARM: OMAP2+: hwmod-data: Add SSI information Tony Lindgren
2013-08-21  1:22   ` Paul Walmsley
2013-08-23 18:29     ` Sebastian Reichel
2013-08-23 18:53       ` Paul Walmsley

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).