linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC][PATCH 0/5] OMAP Synchronous Serial Interface (SSI) driver
@ 2008-10-03 11:50 Carlos Chinea
  2008-10-03 11:52 ` [RFC][PATCH 1/5] OMAP SSI hardware interface definitions Carlos Chinea
  0 siblings, 1 reply; 14+ messages in thread
From: Carlos Chinea @ 2008-10-03 11:50 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-omap

Hi guys !

I'm working on adding support for Nokia HSPA modems to OMAP.

Please consider integrating the following patch set into the
linux-omap tree. The patch set implements a generic device driver
for the OMAP Synchronous Serial Interface. 

The Synchronous Serial Interface (SSI) is a high speed communication
interface that is used for connecting OMAP to a cellular modem engine.

The patch set is based on linux-omap 2.6.27-rc7.

Any comments will be appreciated.

Br,
Carlos



 Documentation/arm/OMAP/ssi/board-ssi.c.example     |  216 ++++++++
 Documentation/arm/OMAP/ssi/ssi                     |  232 +++++++++
 arch/arm/plat-omap/include/mach/ssi/ssi_gdd_reg.h  |  145 ++++++
 .../plat-omap/include/mach/ssi/ssi_reg_common.h    |   73 +++
 arch/arm/plat-omap/include/mach/ssi/ssi_ssr_reg.h  |   56 +++
 arch/arm/plat-omap/include/mach/ssi/ssi_sst_reg.h  |   65 +++
 arch/arm/plat-omap/include/mach/ssi/ssi_sys_reg.h  |  107 ++++
 drivers/misc/Kconfig                               |    2 +
 drivers/misc/Makefile                              |    1 +
 drivers/misc/ssi/Kconfig                           |   11 +
 drivers/misc/ssi/Makefile                          |    7 +
 drivers/misc/ssi/ssi_driver.c                      |  513 ++++++++++++++++++++
 drivers/misc/ssi/ssi_driver.h                      |  211 ++++++++
 drivers/misc/ssi/ssi_driver_bus.c                  |  192 ++++++++
 drivers/misc/ssi/ssi_driver_dma.c                  |  406 ++++++++++++++++
 drivers/misc/ssi/ssi_driver_if.c                   |  335 +++++++++++++
 drivers/misc/ssi/ssi_driver_int.c                  |  232 +++++++++
 include/linux/ssi_driver_if.h                      |  137 ++++++
 18 files changed, 2941 insertions(+), 0 deletions(-)



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

* [RFC][PATCH 1/5] OMAP SSI hardware interface definitions
  2008-10-03 11:50 [RFC][PATCH 0/5] OMAP Synchronous Serial Interface (SSI) driver Carlos Chinea
@ 2008-10-03 11:52 ` Carlos Chinea
  2008-10-03 11:52   ` [RFC][PATCH 2/5] OMAP SSI driver interface Carlos Chinea
  2008-10-06 23:16   ` [RFC][PATCH 1/5] OMAP SSI hardware interface definitions Felipe Balbi
  0 siblings, 2 replies; 14+ messages in thread
From: Carlos Chinea @ 2008-10-03 11:52 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-omap


Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
---
 arch/arm/plat-omap/include/mach/ssi/ssi_gdd_reg.h  |  145 ++++++++++++++++++++
 .../plat-omap/include/mach/ssi/ssi_reg_common.h    |   73 ++++++++++
 arch/arm/plat-omap/include/mach/ssi/ssi_ssr_reg.h  |   56 ++++++++
 arch/arm/plat-omap/include/mach/ssi/ssi_sst_reg.h  |   65 +++++++++
 arch/arm/plat-omap/include/mach/ssi/ssi_sys_reg.h  |  107 ++++++++++++++
 5 files changed, 446 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/plat-omap/include/mach/ssi/ssi_gdd_reg.h
 create mode 100644 arch/arm/plat-omap/include/mach/ssi/ssi_reg_common.h
 create mode 100644 arch/arm/plat-omap/include/mach/ssi/ssi_ssr_reg.h
 create mode 100644 arch/arm/plat-omap/include/mach/ssi/ssi_sst_reg.h
 create mode 100644 arch/arm/plat-omap/include/mach/ssi/ssi_sys_reg.h

diff --git a/arch/arm/plat-omap/include/mach/ssi/ssi_gdd_reg.h b/arch/arm/plat-omap/include/mach/ssi/ssi_gdd_reg.h
new file mode 100644
index 0000000..5ed91cc
--- /dev/null
+++ b/arch/arm/plat-omap/include/mach/ssi/ssi_gdd_reg.h
@@ -0,0 +1,145 @@
+/*
+ * ssi_gdd_reg.h
+ *
+ * Hardware defintions for SSI Controller GDD registers.
+ *
+ * HARDWARE: OMAP 2420, OMAP 3430
+ *
+ * Copyright (C) 2007-2008 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 __SSI_GDD_REG_H__
+#define __SSI_GDD_REG_H__
+
+#include "ssi_reg_common.h"
+
+#define SSI_GDD_HW_ID_REG		SSI_GDD_REG32(0x0000)
+#define SSI_GDD_PPORT_ID_REG		SSI_GDD_REG32(0x0010)
+#define SSI_GDD_MPORT_ID_REG		SSI_GDD_REG32(0x0014)
+
+#define SSI_GDD_PPORT_SR_REG		SSI_GDD_REG32(0x0020)
+#	define SSI_PPORT_ACTIVE_LCH_NUMBER_MASK	0xFF
+
+#define SSI_GDD_MPORT_SR_REG		SSI_GDD_REG32(0x0024)
+#	define SSI_MPORT_ACTIVE_LCH_NUMBER_MASK	0xFF
+
+#define SSI_GDD_TEST_REG 		SSI_GDD_REG32(0x0040)
+#	define SSI_TEST			0x1
+
+#define SSI_GDD_GCR_REG			SSI_GDD_REG32(0x0100)
+#	define	SSI_CLK_AUTOGATING_ON	(1<<3)
+#	define	SSI_FREE		(1<<2)
+#	define	SSI_SWITCH_OFF		0x1
+
+#define SSI_GDD_GRST_REG 		SSI_GDD_REG32(0x0200)
+#	define SSI_SWRESET		0x1
+
+#define SSI_GDD_CSDP_BASE		0x0800
+#define SSI_GDD_CSDP_OFFSET		0x40
+#define SSI_GDD_CSDP_REG(channel) 	SSI_GDD_REG16(SSI_GDD_CSDP_BASE +\
+					 (channel*SSI_GDD_CSDP_OFFSET))
+#	define SSI_DST_BURST_EN_MASK	0xC000
+#	define SSI_DST_SINGLE_ACCESS0	0x0
+#	define SSI_DST_SINGLE_ACCESS	(0x1<<14)
+#	define SSI_DST_BURST_4X32_BIT	(0x2<<14)
+#	define SSI_DST_BURST_8x32_BIT	(0x3<<14)	/*NOTE: NOT SUPPORTED */
+
+#	define SSI_DST_MASK		0x1E00
+#	define SSI_DST_MEMORY_PORT	(0x8<<9)
+#	define SSI_DST_PERIPHERAL_PORT	(0x9<<9)
+
+#	define SSI_SRC_BURST_EN_MASK	0x0180
+#	define SSI_SRC_SINGLE_ACCESS0	0x0
+#	define SSI_SRC_SINGLE_ACCESS	(0x1<<7)
+#	define SSI_SRC_BURST_4x32_BIT	(0x2<<7)
+#	define SSI_SRC_BURST_8x32_BIT	(0x3<<7)	/*NOTE: NOT SUPPORTED */
+
+#	define SSI_SRC_MASK		0x003C
+#	define SSI_SRC_MEMORY_PORT	(0x8<<2)
+#	define SSI_SRC_PERIPHERAL_PORT	(0x9<<2)
+
+#	define SSI_DATA_TYPE_MASK	0x0003
+#	define SSI_DATA_TYPE_S32	0x2
+
+#define SSI_GDD_CCR_BASE 		0x0802
+#define SSI_GDD_CCR_OFFSET 		0x40
+#define SSI_GDD_CCR_REG(channel)	SSI_GDD_REG16(SSI_GDD_CCR_BASE +\
+					 (channel*SSI_GDD_CCR_OFFSET))
+#	define SSI_DST_AMODE_MASK	(0x3<<14)
+#	define SSI_DST_AMODE_CONST	0x0
+#	define SSI_DST_AMODE_POSTINC	(0x1<<12)
+
+#	define SSI_SRC_AMODE_MASK	(0x3<<12)
+#	define SSI_SRC_AMODE_CONST	0x0
+#	define SSI_SRC_AMODE_POSTINC	(0x1<<12)
+
+#	define SSI_CCR_ENABLE		(0x1<<7)
+
+#	define SSI_CCR_SYNC_MASK	0x001F
+
+#define SSI_GDD_CICR_BASE 		0x0804
+#define SSI_GDD_CICR_OFFSET		0x40
+#define SSI_GDD_CICR_REG(channel)	SSI_GDD_REG16(SSI_GDD_CICR_BASE +\
+					 (channel*SSI_GDD_CICR_OFFSET))
+#	define SSI_BLOCK_IE		(0x1<<5)
+#	define SSI_HALF_IE		(0x1<<2)
+#	define SSI_TOUT_IE		0x1
+
+#define SSI_GDD_CSR_BASE 		0x0806
+#define SSI_GDD_CSR_OFFSET 		0x40
+#define SSI_GDD_CSR_REG(channel)	SSI_GDD_REG16(SSI_GDD_CSR_BASE +\
+					 (channel*SSI_GDD_CSR_OFFSET))
+#	define SSI_CSR_SYNC		(0x1<<6)
+#	define SSI_CSR_BLOCK		(0x1<<5)
+#	define SSI_CSR_HALF		(0x1<<2)
+#	define SSI_CSR_TOUR		0x1
+
+#define SSI_GDD_CSSA_BASE 		0x0808
+#define SSI_GDD_CSSA_OFFSET		0x40
+#define SSI_GDD_CSSA_REG(channel)	SSI_GDD_REG32(SSI_GDD_CSSA_BASE +\
+					 (channel*SSI_GDD_CSSA_OFFSET))
+
+#define SSI_GDD_CDSA_BASE 		0x080C
+#define SSI_GDD_CDSA_OFFSET		0x40
+#define SSI_GDD_CDSA_REG(channel)	SSI_GDD_REG32(SSI_GDD_CDSA_BASE +\
+					 (channel*SSI_GDD_CDSA_OFFSET))
+
+#define SSI_GDD_CEN_BASE		0x0810
+#define SSI_GDD_CEN_OFFSET		0x40
+#define SSI_GDD_CEN_REG(channel)	SSI_GDD_REG16(SSI_GDD_CEN_BASE +\
+					 (channel*SSI_GDD_CEN_OFFSET))
+
+#define SSI_GDD_CSAC_BASE 		0x0818
+#define SSI_GDD_CSAC_OFFSET		0x40
+#define SSI_GDD_CSAC_REG(channel) 	SSI_GDD_REG16(SSI_GDD_CSAC_BASE +\
+					 (channel*SSI_GDD_CSAC_OFFSET))
+
+#define SSI_GDD_CDAC_BASE 		0x081A
+#define SSI_GDD_CDAC_OFFSET		0x40
+#define SSI_GDD_CDAC_REG(channel) 	SSI_GDD_REG16(SSI_GDD_CDAC_BASE +\
+					 (channel*SSI_GDD_CDAC_OFFSET))
+
+#define SSI_GDD_CLNK_CTRL_BASE		0x0828
+#define SSI_GDD_CLNK_CTRL_OFFSET	0x40
+#define SSI_GDD_CLNK_CTRL_REG(channel)	SSI_GDD_REG16(SSI_GDD_CLNK_CTRL_BASE +\
+					 (channel*SSI_GDD_CLNK_CTRL_OFFSET))
+#	define SSI_ENABLE_LNK		(0x1<<15)
+#	define SSI_STOP_LNK		(0x1<<14)
+#	define NEXT_CH_ID_MASK		0xF
+
+#endif
diff --git a/arch/arm/plat-omap/include/mach/ssi/ssi_reg_common.h b/arch/arm/plat-omap/include/mach/ssi/ssi_reg_common.h
new file mode 100644
index 0000000..e66fb43
--- /dev/null
+++ b/arch/arm/plat-omap/include/mach/ssi/ssi_reg_common.h
@@ -0,0 +1,73 @@
+/*
+ * ssi_reg_common.h
+ *
+ * Common hardware definitions for SSI.
+ *
+ * HARDWARE: OMAP 2420, 3430
+ *
+ * Copyright (C) 2007-2008 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 __SSI_REG_COMMON_H__
+#define __SSI_REG_COMMON_H__
+
+#define SSI_COMMON_BASE_ADDR		0x48050000
+
+/* SSI system registers */
+#define SSI_SYS_OFFSET			0x8000
+#define SSI_SYS_REG32(offset)		(SSI_SYS_OFFSET + (offset))
+/* SSI GDD registers */
+#define SSI_GDD_OFFSET			0x9000
+#define SSI_GDD_REG32(offset)		(SSI_GDD_OFFSET + (offset))
+#define SSI_GDD_REG16(offset)		(SSI_GDD_OFFSET + (offset))
+
+/* SSI SST registers */
+/* General offset of SST port 1. First SST port register.*/
+#define SSI_SST1_OFFSET			0xA000
+/* General offset of SST port 2.*/
+#define SSI_SST2_OFFSET			0xB000
+/* Offset among the SST ports.*/
+#define SSI_SST_PORT_OFFSET		0x1000
+#define SSI_SST_OFFSET(port)		(SSI_SST1_OFFSET +\
+					 ((port-1)*(SSI_SST_PORT_OFFSET)))
+#define SSI_SST_REG(port, offset)	(SSI_SST_OFFSET(port) + (offset))
+
+/* SSI SSR registers */
+/* General offset of SSR port 1. First SSR port register.*/
+#define SSI_SSR1_OFFSET			0xA800
+/* General offset of SSR port 2.*/
+#define SSI_SSR2_OFFSET			0xB800
+/* Offset among the SSR ports.*/
+#define SSI_SSR_PORT_OFFSET		0x1000
+#define SSI_SSR_OFFSET(port)		(SSI_SSR1_OFFSET +\
+					 ((port-1)*(SSI_SSR_PORT_OFFSET)))
+#define SSI_SSR_REG(port, offset)	(SSI_SSR_OFFSET(port) + (offset))
+
+#define SSI_IOMEM_BASE_ADDR		SSI_COMMON_BASE_ADDR
+#define SSI_IOMEM_SIZE			0x3C00
+
+/*
+ * FIXME: Following definitions to be removed.
+ * They are used for checking that the SSI clocks are stable before accessing
+ * the SSI registers.
+ */
+#define OMAP_COMMON_BASE                0x48000000
+#define CM_IDLEST1_CORE_REG		0x4A20
+#define ST_SSI				1
+#endif
diff --git a/arch/arm/plat-omap/include/mach/ssi/ssi_ssr_reg.h b/arch/arm/plat-omap/include/mach/ssi/ssi_ssr_reg.h
new file mode 100644
index 0000000..b272047
--- /dev/null
+++ b/arch/arm/plat-omap/include/mach/ssi/ssi_ssr_reg.h
@@ -0,0 +1,56 @@
+/*
+ * ssi_sst_reg.h
+ *
+ * Hardware definitions for SSI controller SSR registers.
+ *
+ * HARDWARE: OMAP 2420, 3430
+ *
+ * Copyright (C) 2007-2008 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 __SSI_SSR_REG_H__
+#define __SSI_SSR_REG_H__
+
+#include "ssi_reg_common.h"
+
+#define SSI_SSR_ID_REG(port)			SSI_SSR_REG(port, 0x0000)
+#define SSI_SSR_MODE_REG(port)			SSI_SSR_REG(port, 0x0004)
+#define SSI_SSR_FRAMESIZE_REG(port)		SSI_SSR_REG(port, 0x0008)
+#define SSI_SSR_RXSTATE_REG(port)  		SSI_SSR_REG(port, 0x000C)
+#define SSI_SSR_BUFSTATE_REG(port) 		SSI_SSR_REG(port, 0x0010)
+#	define NOTEMPTY(channel)		(1<<channel)
+#define SSI_SSR_BREAK_REG(port)			SSI_SSR_REG(port, 0x001C)
+#define SSI_SSR_ERROR_REG(port)			SSI_SSR_REG(port, 0x0020)
+#define SSI_SSR_ERRORACK_REG(port)		SSI_SSR_REG(port, 0x0024)
+#define SSI_SSR_OVERRUN_REG(port)		SSI_SSR_REG(port, 0x002C)
+#define SSI_SSR_OVERRUNACK_REG(port)		SSI_SSR_REG(port, 0x0030)
+#define SSI_SSR_TIMEOUT_REG(port)		SSI_SSR_REG(port, 0x0030)
+#	define SSI_TIMEOUT_DEFAULT		0
+#define SSI_SSR_CHANNELS_REG(port)		SSI_SSR_REG(port, 0x0028)
+
+#define SSI_SSR_BUFFER_OFFSET_BASE		0x0080
+#define SSI_SSR_BUFFER_CH_REG(port, channel)	SSI_SSR_REG(port, \
+						(SSI_SSR_BUFFER_OFFSET_BASE +\
+						 (channel * 0x04)))
+
+#define SSI_SSR_SWAPBUFFER_OFFSET_BASE		0x00C0
+#define SSI_SSR_SWAPBUFFER_CH_REG(port, channel) SSI_SSR_REG(port, \
+						(SSI_SSR_SWAPBUFFER_OFFSET_BASE\
+						 + (channel * 0x04)))
+#endif
diff --git a/arch/arm/plat-omap/include/mach/ssi/ssi_sst_reg.h b/arch/arm/plat-omap/include/mach/ssi/ssi_sst_reg.h
new file mode 100644
index 0000000..ed15908
--- /dev/null
+++ b/arch/arm/plat-omap/include/mach/ssi/ssi_sst_reg.h
@@ -0,0 +1,65 @@
+/*
+ * ssi_sst_reg.h
+ *
+ * Hardware definitions for SSI controller SST registers.
+ *
+ * HARDWARE: OMAP 2420, 3430
+ *
+ * Copyright (C) 2007-2008 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 __SSI_SST_REG_H__
+#define __SSI_SST_REG_H__
+
+#include "ssi_reg_common.h"
+
+#define SSI_SST_ID_REG(port)			SSI_SST_REG(port, 0x0000)
+#define SSI_SST_MODE_REG(port)			SSI_SST_REG(port, 0x0004)
+#	define SSI_MODE_VAL_MASK		0x3
+#	define SSI_MODE_SLEEP			0x0
+#	define SSI_MODE_STREAM			0x1
+#	define SSI_MODE_FRAME			0x2
+#	define SSI_MODE_MULTIPOINTS		0x3
+#define SSI_SST_FRAMESIZE_REG(port)		SSI_SST_REG(port, 0x0008)
+#	define SSI_FRAMESIZE_DEFAULT		31
+#define SSI_SST_TXSTATE_REG(port)  		SSI_SST_REG(port, 0x000C)
+#	define	TXSTATE_IDLE			0x0
+#define SSI_SST_BUFSTATE_REG(port) 		SSI_SST_REG(port, 0x0010)
+#	define 	NOTFULL(channel)		(1<<channel)
+#define SSI_SST_DIVISOR_REG(port)		SSI_SST_REG(port, 0x0018)
+#	define SSI_DIVISOR_DEFAULT		1
+
+#define SSI_SST_BREAK_REG(port)			SSI_SST_REG(port, 0x0020)
+#define SSI_SST_CHANNELS_REG(port)		SSI_SST_REG(port, 0x0024)
+#	define SSI_CHANNELS_DEFAULT		4
+
+#define SSI_SST_ARBMODE_REG(port)		SSI_SST_REG(port, 0x0028)
+#	define SSI_ARBMODE_ROUNDROBIN		0x0
+#	define SSI_ARBMODE_PRIORITY		0x1
+
+#define SSI_SST_BUFFER_OFFSET_BASE		0x0080
+#define SSI_SST_BUFFER_CH_REG(port, channel)	SSI_SST_REG(port, \
+						(SSI_SST_BUFFER_OFFSET_BASE +\
+						 (channel * 0x4)))
+
+#define SSI_SST_SWAPBUF_OFFSET_BASE		0x00C0
+#define SSI_SST_SWAPBUF_CH_REG(port, channel)	SSI_SST_REG(port, \
+						(SSI_SST_SWAPBUF_OFFSET_BASE +\
+						(channel * 0x4)))
+#endif
diff --git a/arch/arm/plat-omap/include/mach/ssi/ssi_sys_reg.h b/arch/arm/plat-omap/include/mach/ssi/ssi_sys_reg.h
new file mode 100644
index 0000000..2f1e1f5
--- /dev/null
+++ b/arch/arm/plat-omap/include/mach/ssi/ssi_sys_reg.h
@@ -0,0 +1,107 @@
+/*
+ * ssi_sys_reg.h
+ *
+ * Hardware defintions for SSI Controller system registers.
+ *
+ * HARDWARE: OMAP 2420, 3430
+ *
+ * Copyright (C) 2007-2008 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 __SSI_SYS_REG_H__
+#define __SSI_SYS_REG_H__
+
+#include "ssi_reg_common.h"
+
+#define SSI_SYS_REVISION_REG		SSI_SYS_REG32(0x0000)
+#	define SSI_REV_MASK		0x000000FF
+#	define SSI_REV_MAJOR		0xF0
+#	define SSI_REV_MINOR		0x0F
+
+#define SSI_SYS_SYSCONFIG_REG		SSI_SYS_REG32(0x0010)
+#	define SSI_AUTOIDLE		1
+#	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	0x00000018
+#	define SSI_MIDLEMODE_FORCE	0
+#	define SSI_MIDLEMODE_NO		(1<<12)
+#	define SSI_MIDLEMODE_SMART	(1<<13)
+#	define SSI_MIDLEMODE_MASK	0x00003000
+
+#define SSI_SYS_SYSSTATUS_REG		SSI_SYS_REG32(0x0014)
+#	define SSI_RESETDONE		1
+
+#define SSI_SYS_MPU_STATUS_BASE		0x0808
+#define SSI_SYS_MPU_STATUS_PORT_OFFSET	0x10
+#define SSI_SYS_MPU_STATUS_IRQ_OFFSET	0x2
+#define SSI_SYS_MPU_STATUS_REG(port, irq) \
+		SSI_SYS_REG32(SSI_SYS_MPU_STATUS_BASE +\
+		 (((port-1)*SSI_SYS_MPU_STATUS_PORT_OFFSET) +\
+		 (irq*SSI_SYS_MPU_STATUS_IRQ_OFFSET)))
+
+#define SSI_SYS_MPU_ENABLE_BASE		0x080C
+#define SSI_SYS_MPU_ENABLE_PORT_OFFSET	0x10
+#define SSI_SYS_MPU_ENABLE_IRQ_OFFSET	0x8
+#define SSI_SYS_MPU_ENABLE_REG(port, irq) \
+		SSI_SYS_REG32(SSI_SYS_MPU_ENABLE_BASE +\
+		 (((port-1)*SSI_SYS_MPU_ENABLE_PORT_OFFSET) +\
+		 (irq*SSI_SYS_MPU_ENABLE_IRQ_OFFSET)))
+
+#define SSI_SYS_DSP_STATUS_BASE		0x0830
+#define SSI_SYS_DSP_STATUS_PORT_OFFSET	0x10
+#define SSI_SYS_DSP_STATUS_IRQ_OFFSET	0x8
+#define SSI_SYS_DSP_STATUS_REG(port, irq) \
+		SSI_SYS_REG32(SSI_SYS_DSP_STATUS_BASE +\
+		 (((port-1)*SSI_SYS_DSP_STATUS_PORT_OFFSET) +\
+		 (irq*SSI_SYS_DSP_STATUS_IRQ_OFFSET)))
+
+#define SSI_SYS_DSP_ENABLE_BASE		0x0834
+#define SSI_SYS_DSP_ENABLE_PORT_OFFSET	0x10
+#define SSI_SYS_DSP_ENABLE_IRQ_OFFSET	0x8
+#define SSI_SYS_DSP_ENABLE_REG(port, irq) \
+		SSI_SYS_REG32(SSI_SYS_DSP_ENABLE_BASE +\
+		 (((port-1)*SSI_SYS_DSP_ENABLE_PORT_OFFSET) +\
+		 (irq*SSI_SYS_DSP_ENABLE_IRQ_OFFSET)))
+#	define SSI_SST_DATAACCEPT(channel)	(1<<channel)
+#	define SSI_SSR_DATAAVAILABLE(channel)	(1<<(channel + 8))
+#	define SSI_SSR_DATAOVERRUN(channel)	(1<<(channel + 16))
+#	define SSI_ERROROCCURED			(1<<24)
+#	define SSI_BREAKDETECTED		(1<<25)
+
+#define SSI_SYS_GDD_MPU_IRQ_STATUS_REG	SSI_SYS_REG32(0x0800)
+#define SSI_SYS_GDD_MPU_IRQ_ENABLE_REG	SSI_SYS_REG32(0x0804)
+#define SSI_SYS_GDD_DSP_IRQ_STATUS_REG 	SSI_SYS_REG32(0x0828)
+#define SSI_SYS_GDD_DSP_IRQ_ENABLE_REG 	SSI_SYS_REG32(0x082C)
+#	define SSI_GDD_LCH(channel)	(1<<channel)
+
+#define SSI_SYS_WAKE_OFFSET		0x10
+#define SSI_SYS_WAKE_BASE		0x0C00
+#define SSI_SYS_WAKE_REG(port) 		SSI_SYS_REG32(SSI_SYS_WAKE_BASE +\
+					 ((port-1)*SSI_SYS_WAKE_OFFSET))
+#define SSI_SYS_CLEAR_WAKE_BASE		0x0C04
+#define SSI_SYS_CLEAR_WAKE_REG(port)	SSI_SYS_REG32(SSI_SYS_CLEAR_WAKE_BASE +\
+					 ((port-1)*SSI_SYS_WAKE_OFFSET))
+#define SSI_SYS_SET_WAKE_BASE		0x0C08
+#define SSI_SYS_SET_WAKE_REG(port)	SSI_SYS_REG32(SSI_SYS_SET_WAKE_BASE +\
+					 ((port-1)*SSI_SYS_WAKE_OFFSET))
+#	define SSI_WAKE(channel)	(1<<channel)
+#	define SSI_WAKE_MASK		0xFF
+#endif
-- 
1.5.3.6


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

* [RFC][PATCH 2/5] OMAP SSI driver interface
  2008-10-03 11:52 ` [RFC][PATCH 1/5] OMAP SSI hardware interface definitions Carlos Chinea
@ 2008-10-03 11:52   ` Carlos Chinea
  2008-10-03 11:52     ` [RFC][PATCH 3/5] OMAP SSI driver code Carlos Chinea
  2008-10-06 23:29     ` [RFC][PATCH 2/5] OMAP SSI driver interface Felipe Balbi
  2008-10-06 23:16   ` [RFC][PATCH 1/5] OMAP SSI hardware interface definitions Felipe Balbi
  1 sibling, 2 replies; 14+ messages in thread
From: Carlos Chinea @ 2008-10-03 11:52 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-omap


Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
---
 include/linux/ssi_driver_if.h |  137 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 137 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/ssi_driver_if.h

diff --git a/include/linux/ssi_driver_if.h b/include/linux/ssi_driver_if.h
new file mode 100644
index 0000000..3379dd0
--- /dev/null
+++ b/include/linux/ssi_driver_if.h
@@ -0,0 +1,137 @@
+/*
+ * ssi_driver_if.h
+ *
+ * Header for the SSI driver low level interface.
+ *
+ * Copyright (C) 2007-2008 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 __SSI_DRIVER_IF_H__
+#define __SSI_DRIVER_IF_H__
+
+#include <linux/device.h>
+
+#define SSI_IOMEM_NAME		"SSI_IO_MEM"
+#define SSI_P1_MPU_IRQ0_NAME	"SSI_P1_MPU_IRQ0"
+#define SSI_P2_MPU_IRQ0_NAME	"SSI_P2_MPU_IRQ0"
+#define SSI_P1_MPU_IRQ1_NAME	"SSI_P1_MPU_IRQ1"
+#define SSI_P2_MPU_IRQ1_NAME	"SSI_P2_MPU_IRQ1"
+#define SSI_GDD_MPU_IRQ_NAME	"GDD_MPU_IRQ"
+
+/* IRQ values */
+#define SSI_P1_MPU_IRQ0		67
+#define SSI_P2_MPU_IRQ0		68
+#define SSI_P1_MPU_IRQ1		69
+#define SSI_P2_MPU_IRQ1		70
+#define SSI_GDD_MPU_IRQ		71
+
+/* The number of ports handled by the driver. (MAX:2) */
+#define SSI_MAX_PORTS		1
+
+/*
+ * Masks used to enable or disable the reception of certain hardware events
+ * for the ssi_device_drivers
+ */
+#define SSI_EVENT_CLEAR			0x00
+#define SSI_EVENT_MASK			0xFF
+#define SSI_EVENT_BREAK_DETECTED_MASK	0x01
+#define SSI_EVENT_ERROR_MASK		0x02
+
+#define ANY_SSI_CONTROLLER	-1
+#define ANY_CHANNEL		-1
+#define CHANNEL(channel)	(1<<channel)
+
+enum {
+	SSI_EVENT_BREAK_DETECTED = 0,
+	SSI_EVENT_ERROR,
+};
+
+enum {
+	SSI_IOCTL_WAKE_UP,
+	SSI_IOCTL_WAKE_DOWN,
+	SSI_IOCTL_SEND_BREAK,
+	SSI_IOCTL_WAKE,
+};
+
+/* Forward references */
+struct ssi_device;
+struct ssi_dev;
+struct ssi_port;
+struct ssi_channel;
+
+struct ssi_port_pd {
+	u32 tx_mode;
+	u32 tx_frame_size;
+	u32 divisor;
+	u32 tx_ch;
+	u32 arb_mode;
+	u32 rx_mode;
+	u32 rx_frame_size;
+	u32 rx_ch;
+	u32 timeout;
+	u8 n_irq;
+};
+
+struct ssi_platform_data {
+	unsigned char *clk_name;
+	struct ssi_dev *ssi_ctrl;
+	struct ssi_port_pd *ports;
+	u8 num_ports;
+};
+
+struct ssi_device {
+	int n_ctrl;
+	unsigned int n_p;
+	unsigned int n_ch;
+	char modalias[BUS_ID_SIZE];
+	struct ssi_channel *ch;
+	struct device device;
+};
+
+#define to_ssi_device(dev)	container_of(dev, struct ssi_device, device)
+
+struct ssi_device_driver {
+	unsigned long		ctrl_mask;
+	unsigned long		ch_mask[SSI_MAX_PORTS];
+	unsigned long		event_mask;
+	void 			(*port_event) (int c_id, unsigned int port,
+						unsigned int event, void *arg);
+	int			(*probe)(struct ssi_device *dev);
+	int			(*remove)(struct ssi_device *dev);
+	int			(*suspend)(struct ssi_device *dev,
+						pm_message_t mesg);
+	int			(*resume)(struct ssi_device *dev);
+	struct device_driver 	driver;
+};
+
+#define to_ssi_device_driver(drv) container_of(drv, \
+						struct ssi_device_driver, \
+						driver)
+
+int register_ssi_driver(struct ssi_device_driver *driver);
+void unregister_ssi_driver(struct ssi_device_driver *driver);
+int ssi_open(struct ssi_device *dev);
+int ssi_write(struct ssi_device *dev, u32 *data, unsigned int count);
+void ssi_write_cancel(struct ssi_device *dev);
+int ssi_read(struct ssi_device *dev, u32 *data, unsigned int w_count);
+void ssi_read_cancel(struct ssi_device *dev);
+int ssi_ioctl(struct ssi_device *dev, unsigned int command, void *arg);
+void ssi_close(struct ssi_device *dev);
+void ssi_dev_set_cb(struct ssi_device *dev, void (*r_cb)(struct ssi_device *dev)
+					, void (*w_cb)(struct ssi_device *dev));
+#endif
-- 
1.5.3.6


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

* [RFC][PATCH 3/5] OMAP SSI driver code
  2008-10-03 11:52   ` [RFC][PATCH 2/5] OMAP SSI driver interface Carlos Chinea
@ 2008-10-03 11:52     ` Carlos Chinea
  2008-10-03 11:52       ` [RFC][PATCH 4/5] OMAP SSI integration into misc drivers Carlos Chinea
  2008-10-07  0:03       ` [RFC][PATCH 3/5] OMAP SSI driver code Felipe Balbi
  2008-10-06 23:29     ` [RFC][PATCH 2/5] OMAP SSI driver interface Felipe Balbi
  1 sibling, 2 replies; 14+ messages in thread
From: Carlos Chinea @ 2008-10-03 11:52 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-omap


Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
---
 drivers/misc/ssi/Kconfig          |   11 +
 drivers/misc/ssi/Makefile         |    7 +
 drivers/misc/ssi/ssi_driver.c     |  513 +++++++++++++++++++++++++++++++++++++
 drivers/misc/ssi/ssi_driver.h     |  211 +++++++++++++++
 drivers/misc/ssi/ssi_driver_bus.c |  192 ++++++++++++++
 drivers/misc/ssi/ssi_driver_dma.c |  406 +++++++++++++++++++++++++++++
 drivers/misc/ssi/ssi_driver_if.c  |  335 ++++++++++++++++++++++++
 drivers/misc/ssi/ssi_driver_int.c |  232 +++++++++++++++++
 8 files changed, 1907 insertions(+), 0 deletions(-)
 create mode 100644 drivers/misc/ssi/Kconfig
 create mode 100644 drivers/misc/ssi/Makefile
 create mode 100644 drivers/misc/ssi/ssi_driver.c
 create mode 100644 drivers/misc/ssi/ssi_driver.h
 create mode 100644 drivers/misc/ssi/ssi_driver_bus.c
 create mode 100644 drivers/misc/ssi/ssi_driver_dma.c
 create mode 100644 drivers/misc/ssi/ssi_driver_if.c
 create mode 100644 drivers/misc/ssi/ssi_driver_int.c

diff --git a/drivers/misc/ssi/Kconfig b/drivers/misc/ssi/Kconfig
new file mode 100644
index 0000000..5e0842c
--- /dev/null
+++ b/drivers/misc/ssi/Kconfig
@@ -0,0 +1,11 @@
+#
+# OMAP SSI HW kernel configuration
+#
+config OMAP_SSI
+	tristate "OMAP SSI hardware driver"
+	depends on ARCH_OMAP
+	default n
+	---help---
+	  If you say Y here, you will enable the OMAP SSI hardware driver.
+
+	  If unsure, say N.
diff --git a/drivers/misc/ssi/Makefile b/drivers/misc/ssi/Makefile
new file mode 100644
index 0000000..2b74e02
--- /dev/null
+++ b/drivers/misc/ssi/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for SSI drivers
+#
+omap_ssi-objs := 	ssi_driver.o ssi_driver_dma.o ssi_driver_int.o \
+			ssi_driver_if.o ssi_driver_bus.o
+
+obj-$(CONFIG_OMAP_SSI)	+= omap_ssi.o
diff --git a/drivers/misc/ssi/ssi_driver.c b/drivers/misc/ssi/ssi_driver.c
new file mode 100644
index 0000000..292e868
--- /dev/null
+++ b/drivers/misc/ssi/ssi_driver.c
@@ -0,0 +1,513 @@
+/*
+ * ssi_driver.c
+ *
+ * Implements SSI module interface, initialization, and PM related functions.
+ *
+ * Copyright (C) 2007-2008 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/err.h>
+#include "ssi_driver.h"
+
+static void ssi_dev_release(struct device *dev)
+{
+}
+
+static void __init ssi_undo_reg_dev(struct platform_device *pd,
+								int p, int ch)
+{
+	struct ssi_platform_data *p_data =
+		(struct ssi_platform_data *) pd->dev.platform_data;
+	struct ssi_port *ssi_p;
+	int port;
+	int channel;
+
+	for (port = p; port >= 0; port--) {
+		ssi_p = &p_data->ssi_ctrl->ssi_port[port];
+		for (channel = ch; channel >= 0 ; channel--)
+			device_unregister(&ssi_p->ssi_channel[channel].dev
+								->device);
+	}
+}
+
+static int __init reg_ssi_dev_ch(struct platform_device *pd,
+						unsigned int p, unsigned int ch)
+{
+	struct ssi_device *dev;
+	struct ssi_platform_data *p_data =
+		(struct ssi_platform_data *) pd->dev.platform_data;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	dev->n_ctrl = pd->id;
+	dev->n_p = p;
+	dev->n_ch = ch;
+	dev->ch = &p_data->ssi_ctrl->ssi_port[p].ssi_channel[ch];
+	p_data->ssi_ctrl->ssi_port[p].ssi_channel[ch].dev = dev;
+	dev->device.bus = &ssi_bus_type;
+	dev->device.parent = &pd->dev;
+	dev->device.release = ssi_dev_release;
+	if (dev->n_ctrl < 0)
+		snprintf(dev->device.bus_id, sizeof(dev->device.bus_id),
+			"omap_ssi-p%u.c%u", p, ch);
+	else
+		snprintf(dev->device.bus_id, sizeof(dev->device.bus_id),
+			"omap_ssi%d-p%u.c%u", dev->n_ctrl, p, ch);
+
+	return device_register(&dev->device);
+}
+
+static int __init register_ssi_devices(struct platform_device *pd)
+{
+	struct ssi_platform_data *p_data =
+		(struct ssi_platform_data *) pd->dev.platform_data;
+	unsigned int n_ch = 0;
+	int port;
+	int ch = 0;
+	int err = 0;
+
+	for (port = 0; ((port < p_data->num_ports) && (err >= 0)); port++) {
+		n_ch = max(p_data->ports[port].tx_ch,
+						p_data->ports[port].rx_ch);
+		for (ch = 0; ((ch < n_ch) && (err >= 0)); ch++)
+			err = reg_ssi_dev_ch(pd, port, ch);
+	}
+
+	if (err < 0) {
+		port--;
+		ch--;
+		dev_err(&pd->dev, "Error registering ssi device channel "
+					"p%d-c%d : %d\n", port, ch, err);
+		if ((port == 0) && (ch == 0))
+			return err;
+		else if ((port > 0) && (ch == 0))
+			ch = n_ch;
+		ssi_undo_reg_dev(pd, port, ch);
+	}
+	return err;
+}
+
+static int __init ssi_softreset(struct ssi_dev *ssi_ctrl)
+{
+	int ind = 0;
+	void __iomem *base = ssi_ctrl->base;
+	u32 status;
+
+	ssi_outl_or(SSI_SOFTRESET, base, SSI_SYS_SYSCONFIG_REG);
+
+	status = ssi_inl(base, SSI_SYS_SYSSTATUS_REG);
+	while ((!(status & SSI_RESETDONE)) && (ind < SSI_RESETDONE_RETRIES)) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(msecs_to_jiffies(SSI_RESETDONE_TIMEOUT));
+		status = ssi_inl(base, SSI_SYS_SYSSTATUS_REG);
+		ind++;
+	}
+
+	if (ind >= SSI_RESETDONE_RETRIES)
+		return -EIO;
+
+	/* Reseting GDD */
+	ssi_outl_or(SSI_SWRESET, base, SSI_GDD_GRST_REG);
+
+	return 0;
+}
+
+static void __init set_ssi_ports_default(
+						struct platform_device *pd)
+{
+	struct ssi_port_pd *cfg = NULL;
+	struct ssi_platform_data *p_data =
+		(struct ssi_platform_data *) pd->dev.platform_data;
+	unsigned int port = 0;
+	void __iomem *base = p_data->ssi_ctrl->base;
+
+	for (port = 1; port <= p_data->num_ports; port++) {
+		cfg = &p_data->ports[port - 1];
+		ssi_outl(cfg->tx_mode, base, SSI_SST_MODE_REG(port));
+		ssi_outl(cfg->tx_frame_size, base, SSI_SST_FRAMESIZE_REG(port));
+		ssi_outl(cfg->divisor, base, SSI_SST_DIVISOR_REG(port));
+		ssi_outl(cfg->tx_ch, base, SSI_SST_CHANNELS_REG(port));
+		ssi_outl(cfg->arb_mode, base, SSI_SST_ARBMODE_REG(port));
+
+		ssi_outl(cfg->rx_mode, base, SSI_SSR_MODE_REG(port));
+		ssi_outl(cfg->rx_frame_size, base, SSI_SSR_FRAMESIZE_REG(port));
+		ssi_outl(cfg->rx_ch, base, SSI_SSR_CHANNELS_REG(port));
+		ssi_outl(cfg->timeout, base, SSI_SSR_TIMEOUT_REG(port));
+	}
+}
+
+static int __init ssi_port_channels_init(
+			struct platform_device *pd, unsigned int lport)
+{
+	struct ssi_platform_data *p_data =
+		(struct ssi_platform_data *) pd->dev.platform_data;
+	struct ssi_channel *ch;
+	struct ssi_port *port;
+	unsigned int n_ch;
+	unsigned int ch_i;
+
+	n_ch = max(p_data->ports[lport].tx_ch, p_data->ports[lport].rx_ch);
+	port =  &p_data->ssi_ctrl->ssi_port[lport];
+	for (ch_i = 0; ch_i < n_ch; ch_i++) {
+		ch = &port->ssi_channel[ch_i];
+		ch->channel_number = ch_i;
+		ch->flags = 0;
+		ch->ssi_port = port;
+		ch->read_data.addr = NULL;
+		ch->read_data.size = 0;
+		ch->read_data.lch = -1;
+		ch->write_data.addr = NULL;
+		ch->write_data.size = 0;
+		ch->write_data.lch = -1;
+		spin_lock_init(&ch->ssi_ch_lock);
+	}
+
+	return 0;
+}
+
+static int __init ssi_ports_init(struct platform_device *pd)
+{
+	struct ssi_platform_data *p_data =
+		(struct ssi_platform_data *) pd->dev.platform_data;
+	struct ssi_port *ssi_p = NULL;
+	unsigned int port = 0;
+	unsigned int err = 0;
+	unsigned int n_ports;
+
+	n_ports = min(p_data->num_ports, (u8)SSI_MAX_PORTS);
+
+	for (port = 0; ((port < n_ports) && (err >= 0)); port++) {
+		ssi_p = &p_data->ssi_ctrl->ssi_port[port];
+		ssi_p->port_number = port + 1;
+		ssi_p->ssi_controller = p_data->ssi_ctrl;
+		ssi_p->max_ch = max(p_data->ports[port].tx_ch,
+						p_data->ports[port].rx_ch);
+		ssi_p->max_ch = min(ssi_p->max_ch, (u8)SSI_PORT_MAX_CH);
+		ssi_p->n_irq = p_data->ports[port].n_irq;
+		ssi_p->irq = 0;
+		spin_lock_init(&ssi_p->lock);
+		err = ssi_port_channels_init(pd, port);
+	}
+
+	return 0;
+}
+
+static int __init ssi_controller_init(struct platform_device *pd)
+{
+	struct ssi_platform_data *p_data =
+		(struct ssi_platform_data *) pd->dev.platform_data;
+	struct ssi_dev *ssi_ctrl = p_data->ssi_ctrl;
+	int err;
+
+	ssi_ctrl->id = pd->id;
+	ssi_ctrl->max_p = p_data->num_ports;
+	ssi_ctrl->pdev = pd;
+	spin_lock_init(&ssi_ctrl->lock);
+	ssi_ctrl->ssi_clk = clk_get(&pd->dev, p_data->clk_name);
+	if (IS_ERR(ssi_ctrl->ssi_clk)) {
+		dev_err(&pd->dev, "Unable to get SSI clocks");
+		return PTR_ERR(ssi_ctrl->ssi_clk);
+	}
+
+	err = ssi_ports_init(pd);
+	if (err < 0) {
+		dev_err(&pd->dev, "Error on ssi_controller initialization");
+		clk_put(ssi_ctrl->ssi_clk);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static void ssi_controller_exit(struct platform_device *pd)
+{
+	struct ssi_platform_data *p_data =
+		(struct ssi_platform_data *) pd->dev.platform_data;
+	struct ssi_dev *ssi_ctrl = p_data->ssi_ctrl;
+
+	p_data->ssi_ctrl = NULL;
+	ssi_ctrl->pdev = NULL;
+	clk_put(ssi_ctrl->ssi_clk);
+
+}
+
+static int __init request_ssi_irqs(struct platform_device *pd)
+{
+	struct ssi_platform_data *p_data = pd->dev.platform_data;
+	struct ssi_dev *ssi_ctrl = p_data->ssi_ctrl;
+	struct ssi_port *ssi_p;
+	struct resource *mpu_irq;
+	int i;
+	int j;
+	int err = 0;
+
+	for (i = 0; ((i < p_data->num_ports) && (!err)); i++) {
+		mpu_irq = platform_get_resource(pd, IORESOURCE_IRQ, i*2);
+		if (!mpu_irq) {
+			dev_err(&pd->dev, "SSI misses info for MPU IRQ"
+							" on port %d", i + 1);
+			err = -ENODEV;
+		} else {
+			ssi_p = &ssi_ctrl->ssi_port[i];
+			ssi_p->n_irq = 0; /* We only use one irq line */
+			ssi_p->irq = mpu_irq->start;
+			tasklet_init(&ssi_p->ssi_tasklet,
+					do_ssi_tasklet,	(unsigned long)ssi_p);
+			err = request_irq(mpu_irq->start, ssi_mpu_handler,
+				IRQF_DISABLED, mpu_irq->name, ssi_p);
+			if (err < 0) {
+				dev_err(&pd->dev, "FAILED request IRQ (%d)",
+							 mpu_irq->start);
+				err = -EBUSY;
+			}
+		}
+	}
+
+	if (err < 0) {
+		/* Let's free the reserved resources if there are any errors */
+		for (j = 0; (j > (i-1)); j++) {
+			printk(KERN_DEBUG LOG_NAME "Free resources on port %d",
+									j+1);
+			ssi_p = &ssi_ctrl->ssi_port[i];
+			tasklet_disable(&ssi_p->ssi_tasklet);
+			free_irq(ssi_p->irq, ssi_p);
+		}
+	}
+
+	return err;
+}
+
+static int __init request_ssi_gdd_irq(struct platform_device *pd)
+{
+	struct ssi_platform_data *p_data = pd->dev.platform_data;
+	struct ssi_dev *ssi_ctrl = p_data->ssi_ctrl;
+	struct resource *gdd_irq;
+	int err = 0;
+
+	gdd_irq = platform_get_resource(pd, IORESOURCE_IRQ, 4);
+	if (!gdd_irq) {
+		dev_err(&pd->dev, "SSI device has no info about GDD IRQ");
+		return -ENODEV;
+	}
+
+	ssi_ctrl->gdd_irq = gdd_irq->start;
+	err = request_irq(gdd_irq->start, ssi_gdd_mpu_handler,
+		IRQF_DISABLED, gdd_irq->name, ssi_ctrl);
+	if (err < 0) {
+		dev_err(&pd->dev, "FAILED to request IRQ NUMBER (%d)",
+							gdd_irq->start);
+		return -EBUSY;
+	}
+	tasklet_init(&ssi_ctrl->ssi_gdd_tasklet, do_ssi_gdd_tasklet,
+			(unsigned long)ssi_ctrl);
+
+	return err;
+}
+
+static void free_ssi_irqs(struct ssi_dev *ssi_ctrl)
+{
+	struct ssi_port *ssi_p;
+	int i;
+
+	for (i = 0; (i < ssi_ctrl->max_p); i++) {
+		ssi_p = &ssi_ctrl->ssi_port[i];
+		tasklet_disable(&ssi_p->ssi_tasklet);
+		free_irq(ssi_p->irq, ssi_p);
+	}
+}
+
+static void free_ssi_gdd_irq(struct ssi_dev *ssi_ctrl)
+{
+	tasklet_disable(&ssi_ctrl->ssi_gdd_tasklet);
+	free_irq(ssi_ctrl->gdd_irq, ssi_ctrl);
+}
+
+static int __init ssi_probe(struct platform_device *pd)
+{
+	struct resource *mem, *ioarea;
+	struct ssi_dev *ssi_ctrl = NULL;
+	struct ssi_platform_data *p_data = NULL;
+	u32 revision = 0;
+	int err = 0;
+
+
+	if ((pd == NULL) || (pd->dev.platform_data == NULL)) {
+		pr_err(LOG_NAME "No device/platform_data found on "
+								"ssi device\n");
+		return -ENODEV;
+	}
+
+	ssi_ctrl = kzalloc(sizeof(*ssi_ctrl), GFP_KERNEL);
+	if (ssi_ctrl == NULL) {
+		dev_err(&pd->dev, "Could not allocate memory for"
+					" struct ssi_dev\n");
+		return -ENOMEM;
+	}
+
+	p_data = (struct ssi_platform_data *) pd->dev.platform_data;
+	p_data->ssi_ctrl = ssi_ctrl;
+
+	mem = platform_get_resource(pd, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(&pd->dev, "SSI device does not have "
+					"SSI IO memory region information\n");
+		err = -ENODEV;
+		goto rback5;
+	}
+
+	err = ssi_controller_init(pd);
+	if (err < 0) {
+		dev_err(&pd->dev, "Could not initialize ssi controller:"
+						" %d\n", err);
+		goto rback5;
+	}
+
+	ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
+		 pd->dev.bus_id);
+	if (!ioarea) {
+		dev_err(&pd->dev, "Unable to request SSI IO memory "
+								"region\n");
+		err = -EBUSY;
+		goto rollback4;
+	}
+
+	ssi_ctrl->base = (void __iomem *)mem->start;
+
+	clk_enable(ssi_ctrl->ssi_clk);
+
+	err = request_ssi_irqs(pd);
+	if (err < 0)
+		goto rollback1;
+
+	err = request_ssi_gdd_irq(pd);
+	if (err < 0)
+		goto rollback2;
+
+	err = ssi_softreset(ssi_ctrl);
+	if (err < 0)
+		goto rollback3;
+
+	/* Set default PM settings */
+	ssi_outl((SSI_AUTOIDLE | SSI_SIDLEMODE_SMART | SSI_MIDLEMODE_SMART),
+			ssi_ctrl->base,  SSI_SYS_SYSCONFIG_REG);
+	ssi_outl(SSI_CLK_AUTOGATING_ON, ssi_ctrl->base, SSI_GDD_GCR_REG);
+
+	set_ssi_ports_default(pd);
+
+	/* Gather info from registers for the driver.(REVISION) */
+	revision = ssi_inl(ssi_ctrl->base, SSI_SYS_REVISION_REG);
+	dev_info(&pd->dev, "SSI Hardware REVISION %d.%d\n",
+		 (revision & SSI_REV_MAJOR) >> 4, (revision & SSI_REV_MINOR));
+
+	err = register_ssi_devices(pd);
+	if (err < 0)
+		goto rollback3;
+
+	clk_disable(ssi_ctrl->ssi_clk);
+
+	return err;
+
+rollback3:
+	free_ssi_gdd_irq(ssi_ctrl);
+rollback2:
+	free_ssi_irqs(ssi_ctrl);
+rollback1:
+	release_mem_region(mem->start, mem->end - mem->start + 1);
+	clk_disable(ssi_ctrl->ssi_clk);
+rollback4:
+	ssi_controller_exit(pd);
+rback5:
+	kfree(ssi_ctrl);
+	return err;
+}
+
+static void __exit close_all_ch(struct ssi_dev *ssi_ctrl)
+{
+	struct ssi_port *ssi_p;
+	unsigned int port;
+	unsigned int ch;
+
+	for (port = 0; port < ssi_ctrl->max_p; port++) {
+		ssi_p = &ssi_ctrl->ssi_port[port];
+		for (ch = 0; ch < ssi_p->max_ch; ch++)
+			ssi_close(ssi_p->ssi_channel[ch].dev);
+	}
+}
+
+static int __exit ssi_remove(struct platform_device *pd)
+{
+	struct resource *mem;
+	struct ssi_platform_data *p_data =
+		(struct ssi_platform_data *) pd->dev.platform_data;
+	struct ssi_dev *ssi_ctrl = p_data->ssi_ctrl;
+
+	close_all_ch(ssi_ctrl);
+	free_ssi_gdd_irq(ssi_ctrl);
+	free_ssi_irqs(ssi_ctrl);
+	mem = platform_get_resource(pd, IORESOURCE_MEM, 0);
+	if (mem)
+		release_mem_region(mem->start, mem->end - mem->start + 1);
+	ssi_controller_exit(pd);
+	kfree(ssi_ctrl);
+
+	return 0;
+}
+
+static struct platform_driver ssi_pdriver = {
+	.probe = ssi_probe,
+	.remove = __exit_p(ssi_remove),
+	.driver = {
+		.name = "omap_ssi",
+		.owner = THIS_MODULE,
+	}
+};
+
+static int __init ssi_driver_init(void)
+{
+	int err = 0;
+
+	pr_info("SSI DRIVER Version " SSI_DRIVER_VERSION "\n");
+
+	ssi_bus_init();
+	err = platform_driver_probe(&ssi_pdriver, ssi_probe);
+	if (err < 0) {
+		pr_err(LOG_NAME "Platform DRIVER register FAILED: %d\n", err);
+		ssi_bus_exit();
+		return err;
+	}
+
+	return 0;
+}
+
+static void __exit ssi_driver_exit(void)
+{
+	ssi_bus_exit();
+	platform_driver_unregister(&ssi_pdriver);
+
+	pr_info("SSI DRIVER removed\n");
+}
+
+module_init(ssi_driver_init);
+module_exit(ssi_driver_exit);
+
+MODULE_ALIAS("platform:omap_ssi");
+MODULE_AUTHOR(SSI_DRIVER_AUTHOR);
+MODULE_DESCRIPTION(SSI_DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/ssi/ssi_driver.h b/drivers/misc/ssi/ssi_driver.h
new file mode 100644
index 0000000..3c6d849
--- /dev/null
+++ b/drivers/misc/ssi/ssi_driver.h
@@ -0,0 +1,211 @@
+/*
+ * ssi_driver.h
+ *
+ * Header file for the SSI driver low level interface.
+ *
+ * Copyright (C) 2007-2008 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 __SSI_DRIVER_H__
+#define __SSI_DRIVER_H__
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include <mach/ssi/ssi_reg_common.h>
+#include <mach/ssi/ssi_sys_reg.h>
+#include <mach/ssi/ssi_ssr_reg.h>
+#include <mach/ssi/ssi_sst_reg.h>
+#include <mach/ssi/ssi_gdd_reg.h>
+
+#include <linux/ssi_driver_if.h>
+
+#define	SSI_DRIVER_VERSION	"1.1-rc2"
+#define SSI_DRIVER_AUTHOR	"Carlos Chinea / Nokia"
+#define SSI_DRIVER_DESC		"Synchronous Serial Interface Driver"
+#define SSI_DRIVER_LINCESE	"GPL"
+#define SSI_DRIVER_NAME		"ssi_driver"
+
+#define SSI_DEVICE_NAME		"ssi_device"
+
+/* 10 ms */
+#define SSI_RESETDONE_TIMEOUT	10
+/* Let's retry 20 times.=>20x10 ms=200 ms */
+#define SSI_RESETDONE_RETRIES	20
+
+/* Channel states */
+#define	SSI_CH_OPEN		0x01
+
+/*
+ * The number of channels to use by the driver in the ports, or the highest
+ * port channel number (+1) used. (MAX:8)
+ */
+#define SSI_PORT_MAX_CH		4
+/* Number of logical channel in GDD */
+#define SSI_NUM_LCH		8
+
+#define LOG_NAME		"OMAP SSI: "
+#define SSI_PREFIX		"ssi:"
+
+/*
+ * Callbacks use by the SSI upper layer (SSI protocol) to receive data
+ * from the port channels.
+ */
+struct ssi_channel_ops {
+	void (*write_done) (struct ssi_device *dev);
+	void (*read_done) (struct ssi_device *dev);
+};
+
+struct ssi_data {
+	/* Pointer to the data to be sent/received */
+	u32 *addr;
+	/*
+	 * Number of words to be sent or space reserved for data to be
+	 * received.
+	 */
+	unsigned int size;
+	/* Holds GDD logical channed number */
+	int lch;
+};
+
+struct ssi_channel {
+	struct ssi_channel_ops ops;
+	struct ssi_data read_data;
+	struct ssi_data write_data;
+	struct ssi_port *ssi_port;
+	u8 flags;
+	u8 channel_number;
+	spinlock_t ssi_ch_lock;
+	struct ssi_device *dev;
+	void *priv;
+};
+
+/* Holds information related to SSI port */
+struct ssi_port {
+	struct ssi_channel ssi_channel[SSI_PORT_MAX_CH];
+	struct ssi_dev *ssi_controller;
+	u8 port_number;
+	u8 max_ch;
+	u8 n_irq; /* IRQ0 or IRQ1 */
+	int irq	    /* Actual IRQ number */;
+	spinlock_t lock;
+	struct tasklet_struct ssi_tasklet;
+};
+
+/*
+ * Struct definition to hold information about the clocks, ssi controller
+ * and the ssi ports.
+ */
+struct ssi_dev {
+	/* Holds reference to PORT 1 (and PORT2 if defined) */
+	struct ssi_port ssi_port[SSI_MAX_PORTS];
+	int id;
+	u8 flags;
+	u8 max_p;
+	struct clk *ssi_clk;
+	void __iomem *base;
+	spinlock_t lock;
+	int gdd_irq;
+	struct tasklet_struct ssi_gdd_tasklet;
+	struct platform_device *pdev;
+};
+
+/* SSI Bus */
+struct ssi_port_event {
+	struct ssi_port *ssi_port;
+	unsigned int event;
+	void *priv;
+};
+extern struct bus_type ssi_bus_type;
+
+int ssi_port_event_handler(struct ssi_port *p, unsigned int event, void *arg);
+int ssi_bus_init(void);
+void ssi_bus_exit(void);
+/* End SSI Bus */
+
+int ssi_driver_read_interrupt(struct ssi_channel *ssi_channel, u32 *data);
+int ssi_driver_write_interrupt(struct ssi_channel *ssi_channel, u32 *data);
+int ssi_driver_read_dma(struct ssi_channel *ssi_channel, u32 *data,
+			unsigned int count);
+int ssi_driver_write_dma(struct ssi_channel *ssi_channel, u32 *data,
+			unsigned int count);
+
+void ssi_driver_cancel_write_interrupt(struct ssi_channel *ch);
+void ssi_driver_cancel_read_interrupt(struct ssi_channel *ch);
+void ssi_driver_cancel_write_dma(struct ssi_channel *ch);
+void ssi_driver_cancel_read_dma(struct ssi_channel *ch);
+
+irqreturn_t ssi_mpu_handler(int irq, void *ssi_port);
+irqreturn_t ssi_gdd_mpu_handler(int irq, void *ssi_controller);
+
+void do_ssi_tasklet(unsigned long ssi_port);
+void do_ssi_gdd_tasklet(unsigned long device);
+
+static inline u32 ssi_inl(void __iomem *base, u32 offset)
+{
+	return inl(OMAP2_IO_ADDRESS(base + offset));
+}
+
+static inline void ssi_outl(u32 data, void __iomem *base, u32 offset)
+{
+	outl(data, OMAP2_IO_ADDRESS(base + offset));
+}
+
+static inline void ssi_outl_or(u32 data, void __iomem *base, u32 offset)
+{
+	u32 tmp = ssi_inl(base, offset);
+	ssi_outl((tmp | data), base, offset);
+}
+
+static inline void ssi_outl_and(u32 data, void __iomem *base, u32 offset)
+{
+	u32 tmp = ssi_inl(base, offset);
+	ssi_outl((tmp & data), base, offset);
+}
+
+static inline u16 ssi_inw(void __iomem *base, u32 offset)
+{
+	return inw(OMAP2_IO_ADDRESS(base + offset));
+}
+
+static inline void ssi_outw(u16 data, void __iomem *base, u32 offset)
+{
+	outw(data, OMAP2_IO_ADDRESS(base + offset));
+}
+
+static inline void ssi_outw_or(u16 data, void __iomem *base, u32 offset)
+{
+	u16 tmp = ssi_inw(base, offset);
+	ssi_outw((tmp | data), base, offset);
+}
+
+static inline void ssi_outw_and(u16 data, void __iomem *base, u32 offset)
+{
+	u16 tmp = ssi_inw(base, offset);
+	ssi_outw((tmp & data), base, offset);
+}
+#endif
diff --git a/drivers/misc/ssi/ssi_driver_bus.c b/drivers/misc/ssi/ssi_driver_bus.c
new file mode 100644
index 0000000..6a07ee0
--- /dev/null
+++ b/drivers/misc/ssi/ssi_driver_bus.c
@@ -0,0 +1,192 @@
+/*
+ * ssi_driver_bus.c
+ *
+ * Implements SSI bus, device and driver interface.
+ *
+ * Copyright (C) 2007-2008 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/device.h>
+#include "ssi_driver.h"
+
+/* LDM. defintions for the ssi bus, ssi device, and ssi_device driver */
+struct bus_type ssi_bus_type;
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
+								char *buf)
+{
+	return snprintf(buf, BUS_ID_SIZE + 1, "%s%s\n", SSI_PREFIX,
+								dev->bus_id);
+}
+
+static struct device_attribute ssi_dev_attrs[] = {
+	__ATTR_RO(modalias),
+	__ATTR_NULL,
+};
+
+static int ssi_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	add_uevent_var(env, "MODALIAS=%s%s", SSI_PREFIX, dev->bus_id);
+	return 0;
+}
+
+/* NOTE: Function called in interrupt context */
+static int ssi_e_handler(struct device_driver *drv, void *p_event)
+{
+	struct ssi_port_event *event = (struct ssi_port_event *)p_event;
+	struct ssi_device_driver *ssi_drv =  to_ssi_device_driver(drv);
+	struct ssi_port *p = event->ssi_port;
+
+	BUG_ON(p_event == NULL);
+
+	if ((ssi_drv->port_event) &&
+		(test_bit(event->event, &ssi_drv->event_mask)) &&
+		((p->ssi_controller->id == -1) ||
+		(test_bit(p->ssi_controller->id, &ssi_drv->ctrl_mask))) &&
+		(ssi_drv->ch_mask[p->port_number - 1] != 0)) {
+
+		(*ssi_drv->port_event)(p->ssi_controller->id, p->port_number,
+					event->event, event->priv);
+	}
+
+	return 0;
+}
+
+int ssi_port_event_handler(struct ssi_port *p, unsigned int event, void *arg)
+{
+	int err = 0;
+	struct ssi_port_event p_ev = {
+		.ssi_port = p,
+		.event = event,
+		.priv = arg
+	};
+
+	BUG_ON(p == NULL);
+
+	err = bus_for_each_drv(&ssi_bus_type, NULL, &p_ev, ssi_e_handler);
+
+	return err;
+}
+
+static int ssi_bus_match(struct device *device, struct device_driver *driver)
+{
+	struct ssi_device *dev = to_ssi_device(device);
+	struct ssi_device_driver *drv = to_ssi_device_driver(driver);
+
+	if (!test_bit(dev->n_ctrl, &drv->ctrl_mask))
+		return 0;
+
+	if (!test_bit(dev->n_ch, &drv->ch_mask[dev->n_p]))
+		return 0;
+
+	return 1;
+}
+
+int ssi_bus_unreg_dev(struct device *device, void *p)
+{
+	device->release(device);
+	device_unregister(device);
+
+	return 0;
+}
+
+int __init ssi_bus_init(void)
+{
+
+	return bus_register(&ssi_bus_type);
+
+}
+
+void ssi_bus_exit(void)
+{
+	bus_for_each_dev(&ssi_bus_type, NULL, NULL, ssi_bus_unreg_dev);
+	bus_unregister(&ssi_bus_type);
+}
+
+static int ssi_driver_probe(struct device *dev)
+{
+	struct ssi_device_driver *drv = to_ssi_device_driver(dev->driver);
+
+	return 	drv->probe(to_ssi_device(dev));
+}
+
+static int ssi_driver_remove(struct device *dev)
+{
+	struct ssi_device_driver *drv = to_ssi_device_driver(dev->driver);
+
+	return drv->remove(to_ssi_device(dev));
+}
+
+static int ssi_driver_suspend(struct device *dev, pm_message_t mesg)
+{
+	struct ssi_device_driver *drv = to_ssi_device_driver(dev->driver);
+
+	return drv->suspend(to_ssi_device(dev), mesg);
+}
+
+static int ssi_driver_resume(struct device *dev)
+{
+	struct ssi_device_driver *drv = to_ssi_device_driver(dev->driver);
+
+	return drv->resume(to_ssi_device(dev));
+}
+
+struct bus_type ssi_bus_type = {
+	.name = "ssi",
+	.dev_attrs = ssi_dev_attrs,
+	.match = ssi_bus_match,
+	.uevent = ssi_bus_uevent,
+};
+
+/**
+ * register_ssi_driver - Register SSI device driver
+ * @driver - reference to the SSI device driver.
+ */
+int register_ssi_driver(struct ssi_device_driver *driver)
+{
+	int ret = 0;
+
+	BUG_ON(driver == NULL);
+
+	driver->driver.bus = &ssi_bus_type;
+	if (driver->probe)
+		driver->driver.probe = ssi_driver_probe;
+	if (driver->remove)
+		driver->driver.remove = ssi_driver_remove;
+	if (driver->suspend)
+		driver->driver.suspend = ssi_driver_suspend;
+	if (driver->resume)
+		driver->driver.resume = ssi_driver_resume;
+
+	ret = driver_register(&driver->driver);
+
+	return ret;
+}
+EXPORT_SYMBOL(register_ssi_driver);
+
+/**
+ * unregister_ssi_driver - Unregister SSI device driver
+ * @driver - reference to the SSI device driver.
+ */
+void unregister_ssi_driver(struct ssi_device_driver *driver)
+{
+	BUG_ON(driver == NULL);
+
+	driver_unregister(&driver->driver);
+}
+EXPORT_SYMBOL(unregister_ssi_driver);
diff --git a/drivers/misc/ssi/ssi_driver_dma.c b/drivers/misc/ssi/ssi_driver_dma.c
new file mode 100644
index 0000000..2524a12
--- /dev/null
+++ b/drivers/misc/ssi/ssi_driver_dma.c
@@ -0,0 +1,406 @@
+/*
+ * ssi_driver_dma.c
+ *
+ * Implements SSI low level interface driver functionality with DMA support.
+ *
+ * Copyright (C) 2007-2008 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/dma-mapping.h>
+#include "ssi_driver.h"
+
+#define SSI_SYNC_WRITE	0
+#define SSI_SYNC_READ  	1
+
+static unsigned char sync_table[2][2][8] = {
+	{
+	 {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
+	 {0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00}
+	 },
+	{
+	 {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17},
+	 {0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}
+	 }
+};
+
+static unsigned int get_sync_type(unsigned int sync)
+{
+	return (sync & 0x10) ? SSI_SYNC_READ : SSI_SYNC_WRITE;
+}
+
+static unsigned int get_sync_port(unsigned int sync)
+{
+	if (((sync >= 0x01) && (sync <= 0x08)) ||
+					((sync >= 0x10) && (sync <= 0x17)))
+		return 1;
+	else if (((sync >= 0x09) && (sync <= 0x0F)) ||
+					((sync >= 0x18) && (sync <= 0x1E)))
+		return 2;
+	else
+		return 3;
+}
+
+static unsigned int get_sync_channel(unsigned int sync)
+{
+	if ((sync == 0x00) || (sync == 0x1F))
+		return 8;
+
+	if (sync & 0x10)
+		return (sync & 0x0F) % 8;
+	else
+		return (sync - 1) % 8;
+}
+
+static unsigned int get_sync(unsigned int port,
+			     unsigned int channel, unsigned int type)
+{
+	if ((port == 0) || (port > SSI_MAX_PORTS) ||
+			(channel >= SSI_PORT_MAX_CH) || (type > SSI_SYNC_READ))
+		return 0x00;
+
+	return sync_table[type][port - 1][channel];
+}
+
+static void rst_ch_read(struct ssi_dev *ssi_ctrl,
+				unsigned int n_p, unsigned int n_ch)
+{
+	struct ssi_channel *ch =
+		&ssi_ctrl->ssi_port[n_p - 1].ssi_channel[n_ch];
+
+	ch->read_data.addr = NULL;
+	ch->read_data.size = 0;
+	ch->read_data.lch = -1;
+}
+
+static void rst_ch_write(struct ssi_dev *ssi_ctrl,
+				unsigned int n_p, unsigned int n_ch)
+{
+	struct ssi_channel *ch =
+		&ssi_ctrl->ssi_port[n_p - 1].ssi_channel[n_ch];
+
+	ch->write_data.addr = NULL;
+	ch->write_data.size = 0;
+	ch->write_data.lch = -1;
+}
+
+/*
+ * get_free_lch - Get a free GDD(DMA)logical channel
+ * @ssi_ctrl- SSI controller of the GDD.
+ *
+ * Needs to be called holding the gdd controller lock
+ */
+static unsigned int get_free_lch(struct ssi_dev *ssi_ctrl)
+{
+	unsigned int enable_reg;
+	unsigned int lch = 0;
+
+	enable_reg = ssi_inl(ssi_ctrl->base, SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+	while ((lch < SSI_NUM_LCH) && (enable_reg & SSI_GDD_LCH(lch)))
+		lch++;
+
+	return lch;
+}
+
+/*
+ * ssi_driver_write_dma - Program GDD [DMA] to write data from memory to
+ * the ssi channel buffer.
+ * @ssi_channel - pointer to the ssi_channel to write data to.
+ * @data - 32-bit word pointer to the data.
+ * @size - Number of 32bit words to be transfered.
+ *
+ * ssi_controller lock must be hold before calling this function.
+ */
+int ssi_driver_write_dma(struct ssi_channel *ssi_channel, u32 *data,
+			 unsigned int size)
+{
+	struct ssi_dev *ssi_ctrl = ssi_channel->ssi_port->ssi_controller;
+	void __iomem *base = ssi_ctrl->base;
+	unsigned int port = ssi_channel->ssi_port->port_number;
+	unsigned int channel = ssi_channel->channel_number;
+	unsigned int sync;
+	int lch;
+	dma_addr_t dma_data;
+	u32 s_addr;
+	u16 tmp;
+
+	if ((size < 1) || (data == NULL))
+		return -EINVAL;
+
+	clk_enable(ssi_ctrl->ssi_clk);
+
+	lch = get_free_lch(ssi_ctrl);
+	if (lch >= SSI_NUM_LCH) {
+		dev_err(&ssi_ctrl->pdev->dev, "No free GDD logical "
+								"channels.\n");
+		clk_disable(ssi_ctrl->ssi_clk);
+		return -EBUSY;	/* No free GDD logical channels. */
+	}
+	/* NOTE: Gettting a free gdd logical channel and
+	 * reserve it must be done atomicaly. */
+	ssi_channel->write_data.lch = lch;
+
+	sync = get_sync(port, channel, SSI_SYNC_WRITE);
+	dma_data = dma_map_single(NULL, data, size * 4, DMA_TO_DEVICE);
+	dma_sync_single(NULL, dma_data, size * 4, DMA_TO_DEVICE);
+
+	tmp = SSI_SRC_SINGLE_ACCESS0 |
+		SSI_SRC_MEMORY_PORT |
+		SSI_DST_SINGLE_ACCESS0 |
+		SSI_DST_PERIPHERAL_PORT |
+		SSI_DATA_TYPE_S32;
+	ssi_outw(tmp, base, SSI_GDD_CSDP_REG(lch));
+	tmp = SSI_SRC_AMODE_POSTINC | SSI_DST_AMODE_CONST | sync;
+	ssi_outw(tmp, base, SSI_GDD_CCR_REG(lch));
+	ssi_outw((SSI_BLOCK_IE | SSI_TOUT_IE), base, SSI_GDD_CICR_REG(lch));
+	s_addr = (u32)base + SSI_SST_BUFFER_CH_REG(port, channel);
+	ssi_outl(s_addr, base, SSI_GDD_CDSA_REG(lch));
+	ssi_outl(dma_data, base, SSI_GDD_CSSA_REG(lch));
+	ssi_outw(size, base, SSI_GDD_CEN_REG(lch));
+	ssi_outl_or(SSI_GDD_LCH(lch), base, SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+	ssi_outw_or(SSI_CCR_ENABLE, base, SSI_GDD_CCR_REG(lch));
+
+	return 0;
+}
+
+/*
+ * ssi_driver_read_dma - Program GDD [DMA] to write data to memory from
+ * the ssi channel buffer.
+ * @ssi_channel - pointer to the ssi_channel to read data from.
+ * @data - 32-bit word pointer where to store the incoming data.
+ * @size - Number of 32bit words to be transfered to the buffer.
+ *
+ * ssi_controller lock must be hold before calling this function.
+ */
+int ssi_driver_read_dma(struct ssi_channel *ssi_channel, u32 *data,
+			unsigned int count)
+{
+	struct ssi_dev *ssi_ctrl = ssi_channel->ssi_port->ssi_controller;
+	void __iomem *base = ssi_ctrl->base;
+	unsigned int port = ssi_channel->ssi_port->port_number;
+	unsigned int channel = ssi_channel->channel_number;
+	unsigned int sync;
+	unsigned int lch;
+	dma_addr_t dma_data;
+	u32 d_addr;
+	u16 tmp;
+
+	clk_enable(ssi_ctrl->ssi_clk);
+	lch = get_free_lch(ssi_ctrl);
+	if (lch >= SSI_NUM_LCH) {
+		dev_err(&ssi_ctrl->pdev->dev, "No free GDD logical "
+								"channels.\n");
+		clk_disable(ssi_ctrl->ssi_clk);
+		return -EBUSY;	/* No free GDD logical channels. */
+	}
+	/*
+	 * NOTE: Gettting a free gdd logical channel and
+	 * reserve it must be done atomicaly.
+	 */
+	ssi_channel->read_data.lch = lch;
+
+	sync = get_sync(port, channel, SSI_SYNC_READ);
+
+	dma_data = dma_map_single(NULL, data, count * 4, DMA_FROM_DEVICE);
+
+	tmp = SSI_DST_SINGLE_ACCESS0 |
+		SSI_DST_MEMORY_PORT |
+		SSI_SRC_SINGLE_ACCESS0 |
+		SSI_SRC_PERIPHERAL_PORT |
+		SSI_DATA_TYPE_S32;
+	ssi_outw(tmp, base, SSI_GDD_CSDP_REG(lch));
+	tmp = SSI_DST_AMODE_POSTINC | SSI_SRC_AMODE_CONST | sync;
+	ssi_outw(tmp, base, SSI_GDD_CCR_REG(lch));
+	ssi_outw((SSI_BLOCK_IE | SSI_TOUT_IE), base, SSI_GDD_CICR_REG(lch));
+	d_addr = (u32)base + SSI_SSR_BUFFER_CH_REG(port, channel);
+	ssi_outl(d_addr, base, SSI_GDD_CSSA_REG(lch));
+	ssi_outl((u32)dma_data, base, SSI_GDD_CDSA_REG(lch));
+	ssi_outw(count, base, SSI_GDD_CEN_REG(lch));
+
+	ssi_outl_or(SSI_GDD_LCH(lch), base, SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+	ssi_outw_or(SSI_CCR_ENABLE, base, SSI_GDD_CCR_REG(lch));
+
+	return 0;
+}
+
+void ssi_driver_cancel_write_dma(struct ssi_channel *ssi_ch)
+{
+	int lch = ssi_ch->write_data.lch;
+	unsigned int port = ssi_ch->ssi_port->port_number;
+	unsigned int channel = ssi_ch->channel_number;
+	struct ssi_dev *ssi_ctrl = ssi_ch->ssi_port->ssi_controller;
+	u32 ccr;
+
+	if (lch < 0)
+		return;
+
+	clk_enable(ssi_ctrl->ssi_clk);
+	ccr = ssi_inw(ssi_ctrl->base, SSI_GDD_CCR_REG(lch));
+	if (!(ccr & SSI_CCR_ENABLE)) {
+		dev_dbg(&ssi_ch->dev->device, LOG_NAME "Write cancel on not "
+		"enabled logical channel %d CCR REG 0x%08X\n", lch, ccr);
+		clk_disable(ssi_ctrl->ssi_clk);
+		return;
+	}
+
+	ssi_outw_and(~SSI_CCR_ENABLE, ssi_ctrl->base, SSI_GDD_CCR_REG(lch));
+	ssi_outl_and(~SSI_GDD_LCH(lch), ssi_ctrl->base,
+						SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+	ssi_outl(SSI_GDD_LCH(lch), ssi_ctrl->base,
+						SSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+
+	ssi_outl_and(~NOTFULL(channel), ssi_ctrl->base,
+						SSI_SST_BUFSTATE_REG(port));
+
+
+	ssi_ch->write_data.addr = NULL;
+	ssi_ch->write_data.size = 0;
+	ssi_ch->write_data.lch = -1;
+	clk_disable(ssi_ctrl->ssi_clk);
+	clk_disable(ssi_ctrl->ssi_clk);
+}
+
+void ssi_driver_cancel_read_dma(struct ssi_channel *ssi_ch)
+{
+	int lch = ssi_ch->read_data.lch;
+	struct ssi_dev *ssi_ctrl = ssi_ch->ssi_port->ssi_controller;
+	unsigned int port = ssi_ch->ssi_port->port_number;
+	unsigned int channel = ssi_ch->channel_number;
+	u32 reg;
+
+	if (lch < 0)
+		return;
+
+	clk_enable(ssi_ctrl->ssi_clk);
+	reg = ssi_inw(ssi_ctrl->base, SSI_GDD_CCR_REG(lch));
+	if (!(reg & SSI_CCR_ENABLE)) {
+		dev_dbg(&ssi_ch->dev->device, LOG_NAME "Read cancel on not "
+		"enable logical channel %d CCR REG 0x%08X\n", lch, reg);
+		clk_disable(ssi_ctrl->ssi_clk);
+		return;
+	}
+
+	ssi_outw_and(~SSI_CCR_ENABLE, ssi_ctrl->base, SSI_GDD_CCR_REG(lch));
+	ssi_outl_and(~SSI_GDD_LCH(lch), ssi_ctrl->base,
+						SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+	ssi_outl(SSI_GDD_LCH(lch), ssi_ctrl->base,
+						SSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+
+	ssi_outl_and(~NOTEMPTY(channel), ssi_ctrl->base,
+						SSI_SSR_BUFSTATE_REG(port));
+
+	ssi_ch->read_data.addr = NULL;
+	ssi_ch->read_data.size = 0;
+	ssi_ch->read_data.lch = -1;
+	clk_disable(ssi_ctrl->ssi_clk);
+	clk_disable(ssi_ctrl->ssi_clk);
+}
+
+static void dma_read_cb(struct ssi_dev *ctrl, unsigned int port,
+							unsigned int channel)
+{
+	struct ssi_channel *ch = &ctrl->ssi_port[port - 1].ssi_channel[channel];
+
+	ch->ops.read_done(ch->dev);
+}
+
+static void dma_write_cb(struct ssi_dev *ctrl, unsigned int port,
+							unsigned int channel)
+{
+	struct ssi_channel *ch = &ctrl->ssi_port[port - 1].ssi_channel[channel];
+
+	ch->ops.write_done(ch->dev);
+}
+
+static void do_gdd_lch(struct ssi_dev *ssi_ctrl, unsigned int gdd_lch)
+{
+	void __iomem *base = ssi_ctrl->base;
+	unsigned int port;
+	unsigned int channel;
+	u32 sync;
+	u32 gdd_csr;
+	dma_addr_t dma_h;
+	size_t size;
+
+	sync = ssi_inw(base, SSI_GDD_CCR_REG(gdd_lch)) & SSI_CCR_SYNC_MASK;
+	port = get_sync_port(sync);
+	channel = get_sync_channel(sync);
+
+	spin_lock(&ssi_ctrl->lock);
+
+	ssi_outl_and(~SSI_GDD_LCH(gdd_lch), base,
+						SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+	gdd_csr = ssi_inw(base, SSI_GDD_CSR_REG(gdd_lch));
+
+	if (!(gdd_csr & SSI_CSR_TOUR)) {
+		if (get_sync_type(sync) == SSI_SYNC_READ) {
+			dma_h = ssi_inl(base, SSI_GDD_CDSA_REG(gdd_lch));
+			size = ssi_inw(base, SSI_GDD_CEN_REG(gdd_lch)) * 4;
+			dma_sync_single(NULL, dma_h, size, DMA_FROM_DEVICE);
+			rst_ch_read(ssi_ctrl, port, channel);
+			spin_unlock(&ssi_ctrl->lock);
+			dma_read_cb(ssi_ctrl, port, channel);
+		} else {
+			rst_ch_write(ssi_ctrl, port, channel);
+			spin_unlock(&ssi_ctrl->lock);
+			dma_write_cb(ssi_ctrl, port, channel);
+		}
+	} else {
+		dev_err(&ssi_ctrl->pdev->dev, "Error  on GDD transfer "
+				"on gdd channel %d port %d channel %d\n",
+						gdd_lch, port, channel);
+		spin_unlock(&ssi_ctrl->lock);
+		ssi_port_event_handler(&ssi_ctrl->ssi_port[port - 1],
+							SSI_EVENT_ERROR, NULL);
+	}
+}
+
+void do_ssi_gdd_tasklet(unsigned long device)
+{
+	struct ssi_dev *ssi_ctrl = (struct ssi_dev *)device;
+	void __iomem *base = ssi_ctrl->base;
+	unsigned int gdd_lch = 0;
+	u32 status_reg = 0;
+	u32 lch_served = 0;
+
+	clk_enable(ssi_ctrl->ssi_clk);
+
+	status_reg = ssi_inl(base, SSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+
+	for (gdd_lch = 0; gdd_lch < SSI_NUM_LCH; gdd_lch++) {
+		if (status_reg & SSI_GDD_LCH(gdd_lch)) {
+			do_gdd_lch(ssi_ctrl, gdd_lch);
+			lch_served |= SSI_GDD_LCH(gdd_lch);
+			clk_disable(ssi_ctrl->ssi_clk);
+		}
+	}
+
+	ssi_outl(lch_served, base, SSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+	clk_disable(ssi_ctrl->ssi_clk);
+
+	enable_irq(ssi_ctrl->gdd_irq);
+}
+
+irqreturn_t ssi_gdd_mpu_handler(int irq, void *ssi_controller)
+{
+	struct ssi_dev *ssi_ctrl = (struct ssi_dev *)ssi_controller;
+
+	tasklet_hi_schedule(&ssi_ctrl->ssi_gdd_tasklet);
+	disable_irq_nosync(ssi_ctrl->gdd_irq);
+
+	return IRQ_HANDLED;
+}
diff --git a/drivers/misc/ssi/ssi_driver_if.c b/drivers/misc/ssi/ssi_driver_if.c
new file mode 100644
index 0000000..385467e
--- /dev/null
+++ b/drivers/misc/ssi/ssi_driver_if.c
@@ -0,0 +1,335 @@
+/*
+ * ssi_driver_if.c
+ *
+ * Implements SSI hardware driver interfaces for the upper layers.
+ *
+ * Copyright (C) 2007-2008 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 "ssi_driver.h"
+
+/**
+ * ssi_open - open a ssi device channel.
+ * @dev - Reference to the ssi device channel to be openned.
+ *
+ * Returns 0 on success, -EINVAL on bad parameters, -EBUSY if is already opened.
+ */
+int ssi_open(struct ssi_device *dev)
+{
+	struct ssi_channel *ch;
+	struct ssi_port *port;
+	struct ssi_dev *ssi_ctrl;
+
+	if (!dev || !dev->ch) {
+		pr_err(LOG_NAME "Wrong SSI device %p\n", dev);
+		return -EINVAL;
+	}
+
+	ch = dev->ch;
+	if (!ch->ops.read_done || !ch->ops.write_done) {
+		dev_err(&dev->device, "Trying to open with no callbacks "
+								"registered\n");
+		return -EINVAL;
+	}
+	port = ch->ssi_port;
+	ssi_ctrl = port->ssi_controller;
+	spin_lock_bh(&ssi_ctrl->lock);
+	if (ch->flags & SSI_CH_OPEN) {
+		dev_err(&dev->device, "Port %d Channel %d already OPENED\n",
+							dev->n_p, dev->n_ch);
+		spin_unlock_bh(&ssi_ctrl->lock);
+		return -EBUSY;
+	}
+	clk_enable(ssi_ctrl->ssi_clk);
+	ch->flags |= SSI_CH_OPEN;
+	ssi_outl_or(SSI_ERROROCCURED | SSI_BREAKDETECTED, ssi_ctrl->base,
+		SSI_SYS_MPU_ENABLE_REG(port->port_number, port->n_irq));
+	clk_disable(ssi_ctrl->ssi_clk);
+	spin_unlock_bh(&ssi_ctrl->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(ssi_open);
+
+/**
+ * ssi_write - write data into the ssi device channel
+ * @dev - reference to the ssi device channel  to write into.
+ * @data - pointer to a 32-bit word data to be written.
+ * @count - number of 32-bit word to be written.
+ *
+ * Return 0 on sucess, a negative value on failure.
+ * A success values only indicates that the request has been accepted.
+ * Transfer is only completed when the write_done callback is called.
+ *
+ */
+int ssi_write(struct ssi_device *dev, u32 *data, unsigned int count)
+{
+	struct ssi_channel *ch;
+	int err;
+
+	if (unlikely(!dev || !dev->ch || !data || (count <= 0))) {
+		dev_err(&dev->device, "Wrong paramenters "
+			"ssi_device %p data %p count %d", dev, data, count);
+		return -EINVAL;
+	}
+	if (unlikely(!(dev->ch->flags & SSI_CH_OPEN))) {
+		dev_err(&dev->device, "SSI device NOT open\n");
+		return -EINVAL;
+	}
+
+	ch = dev->ch;
+	spin_lock_bh(&ch->ssi_port->ssi_controller->lock);
+	ch->write_data.addr = data;
+	ch->write_data.size = count;
+
+	if (count == 1)
+		err = ssi_driver_write_interrupt(ch, data);
+	else
+		err = ssi_driver_write_dma(ch, data, count);
+
+	if (unlikely(err < 0)) {
+		ch->write_data.addr = NULL;
+		ch->write_data.size = 0;
+	}
+	spin_unlock_bh(&ch->ssi_port->ssi_controller->lock);
+
+	return err;
+
+}
+EXPORT_SYMBOL(ssi_write);
+
+/**
+ * ssi_read - read data from the ssi device channel
+ * @dev - ssi device channel reference to read data from.
+ * @data - pointer to a 32-bit word data to store the data.
+ * @count - number of 32-bit word to be stored.
+ *
+ * Return 0 on sucess, a negative value on failure.
+ * A success values only indicates that the request has been accepted.
+ * Data is only available in the buffer when the read_done callback is called.
+ *
+ */
+int ssi_read(struct ssi_device *dev, u32 *data, unsigned int count)
+{
+	struct ssi_channel *ch;
+	int err;
+
+	if (unlikely(!dev || !dev->ch || !data || (count <= 0))) {
+		dev_err(&dev->device, "Wrong paramenters "
+			"ssi_device %p data %p count %d", dev, data, count);
+		return -EINVAL;
+	}
+	if (unlikely(!(dev->ch->flags & SSI_CH_OPEN))) {
+		dev_err(&dev->device, "SSI device NOT open\n");
+		return -EINVAL;
+	}
+
+	ch = dev->ch;
+	spin_lock_bh(&ch->ssi_port->ssi_controller->lock);
+	ch->read_data.addr = data;
+	ch->read_data.size = count;
+
+	if (count == 1)
+		err = ssi_driver_read_interrupt(ch, data);
+	else
+		err = ssi_driver_read_dma(ch, data, count);
+
+	if (unlikely(err < 0)) {
+		ch->read_data.addr = NULL;
+		ch->read_data.size = 0;
+	}
+	spin_unlock_bh(&ch->ssi_port->ssi_controller->lock);
+
+	return err;
+}
+EXPORT_SYMBOL(ssi_read);
+
+void __ssi_write_cancel(struct ssi_channel *ch)
+{
+	if (ch->write_data.size == 1)
+		ssi_driver_cancel_write_interrupt(ch);
+	else if (ch->write_data.size > 1)
+		ssi_driver_cancel_write_dma(ch);
+
+}
+/**
+ * ssi_write_cancel - Cancel pending write request.
+ * @dev - ssi device channel where to cancel the pending write.
+ *
+ * write_done() callback will not be called after sucess of this function.
+ */
+void ssi_write_cancel(struct ssi_device *dev)
+{
+	if (unlikely(!dev || !dev->ch)) {
+		pr_err(LOG_NAME "Wrong SSI device %p\n", dev);
+		return;
+	}
+	if (unlikely(!(dev->ch->flags & SSI_CH_OPEN))) {
+		dev_err(&dev->device, "SSI device NOT open\n");
+		return;
+	}
+
+	spin_lock_bh(&dev->ch->ssi_port->ssi_controller->lock);
+	__ssi_write_cancel(dev->ch);
+	spin_unlock_bh(&dev->ch->ssi_port->ssi_controller->lock);
+}
+EXPORT_SYMBOL(ssi_write_cancel);
+
+void __ssi_read_cancel(struct ssi_channel *ch)
+{
+	if (ch->read_data.size == 1)
+		ssi_driver_cancel_read_interrupt(ch);
+	else if (ch->read_data.size > 1)
+		ssi_driver_cancel_read_dma(ch);
+}
+
+/**
+ * ssi_read_cancel - Cancel pending read request.
+ * @dev - ssi device channel where to cancel the pending read.
+ *
+ * read_done() callback will not be called after sucess of this function.
+ */
+void ssi_read_cancel(struct ssi_device *dev)
+{
+	if (unlikely(!dev || !dev->ch)) {
+		pr_err(LOG_NAME "Wrong SSI device %p\n", dev);
+		return;
+	}
+
+	if (unlikely(!(dev->ch->flags & SSI_CH_OPEN))) {
+		dev_err(&dev->device, "SSI device NOT open\n");
+		return;
+	}
+
+	spin_lock_bh(&dev->ch->ssi_port->ssi_controller->lock);
+	__ssi_read_cancel(dev->ch);
+	spin_unlock_bh(&dev->ch->ssi_port->ssi_controller->lock);
+
+}
+EXPORT_SYMBOL(ssi_read_cancel);
+
+/**
+ * ssi_ioctl - SSI I/O control
+ * @dev - ssi device channel reference to apply the I/O control
+ * 						(or port associated to it)
+ * @command - SSI I/O control command
+ * @arg - parameter associated to the control command. NULL, if no parameter.
+ *
+ * Return 0 on sucess, a negative value on failure.
+ *
+ */
+int ssi_ioctl(struct ssi_device *dev, unsigned int command, void *arg)
+{
+	struct ssi_channel *ch;
+	struct ssi_dev *ssi_ctrl;
+	void __iomem *base;
+	unsigned int port, channel;
+	u32 wake;
+	int err = 0;
+
+	if (unlikely((!dev) ||
+		(!dev->ch) ||
+		(!dev->ch->ssi_port) ||
+		(!dev->ch->ssi_port->ssi_controller)) ||
+		(!(dev->ch->flags & SSI_CH_OPEN))) {
+		pr_err(LOG_NAME "SSI IOCTL Invalid parameter\n");
+		return -EINVAL;
+	}
+
+
+	ch = dev->ch;
+	ssi_ctrl = ch->ssi_port->ssi_controller;
+	port = ch->ssi_port->port_number;
+	channel = ch->channel_number;
+	base = ssi_ctrl->base;
+	clk_enable(ssi_ctrl->ssi_clk);
+
+	switch (command) {
+	case SSI_IOCTL_WAKE_UP:
+		/* We only claim once the wake line per channel */
+		wake = ssi_inl(base, SSI_SYS_WAKE_REG(port));
+		if (!(wake & SSI_WAKE(channel))) {
+			clk_enable(ssi_ctrl->ssi_clk);
+			ssi_outl(SSI_WAKE(channel), base,
+					SSI_SYS_SET_WAKE_REG(port));
+		}
+		break;
+	case SSI_IOCTL_WAKE_DOWN:
+		wake = ssi_inl(base, SSI_SYS_WAKE_REG(port));
+		if ((wake & SSI_WAKE(channel))) {
+			ssi_outl(SSI_WAKE(channel), base,
+						SSI_SYS_CLEAR_WAKE_REG(port));
+			clk_disable(ssi_ctrl->ssi_clk);
+		}
+		break;
+	case SSI_IOCTL_SEND_BREAK:
+		ssi_outl(1, base, SSI_SST_BREAK_REG(port));
+		break;
+	case SSI_IOCTL_WAKE:
+		if (arg == NULL)
+			err = -EINVAL;
+		else
+			*(u32 *)arg = ssi_inl(base, SSI_SYS_WAKE_REG(port));
+		break;
+	default:
+		err = -ENOIOCTLCMD;
+		break;
+	}
+
+	clk_disable(ssi_ctrl->ssi_clk);
+
+	return err;
+}
+EXPORT_SYMBOL(ssi_ioctl);
+
+/**
+ * ssi_close - close given ssi device channel
+ * @dev - reference to ssi device channel.
+ */
+void ssi_close(struct ssi_device *dev)
+{
+	if (!dev || !dev->ch) {
+		pr_err(LOG_NAME "Trying to close wrong SSI device %p\n", dev);
+		return;
+	}
+
+	spin_lock_bh(&dev->ch->ssi_port->ssi_controller->lock);
+	if (dev->ch->flags & SSI_CH_OPEN) {
+		dev->ch->flags &= ~SSI_CH_OPEN;
+		__ssi_write_cancel(dev->ch);
+		__ssi_read_cancel(dev->ch);
+	}
+	spin_unlock_bh(&dev->ch->ssi_port->ssi_controller->lock);
+
+}
+EXPORT_SYMBOL(ssi_close);
+
+/**
+ * ssi_dev_set_cb - register read_done() and write_done() callbacks.
+ * @dev - reference to ssi device channel where callbacks are associated.
+ * @r_cb - callback to signal read transfer completed.
+ * @w_cb - callback to signal write transfer completed.
+ */
+void ssi_dev_set_cb(struct ssi_device *dev, void (*r_cb)(struct ssi_device *dev)
+					, void (*w_cb)(struct ssi_device *dev))
+{
+	dev->ch->ops.read_done = r_cb;
+	dev->ch->ops.write_done = w_cb;
+}
+EXPORT_SYMBOL(ssi_dev_set_cb);
diff --git a/drivers/misc/ssi/ssi_driver_int.c b/drivers/misc/ssi/ssi_driver_int.c
new file mode 100644
index 0000000..6491e48
--- /dev/null
+++ b/drivers/misc/ssi/ssi_driver_int.c
@@ -0,0 +1,232 @@
+/*
+ * ssi_driver_int.c
+ *
+ * Implements SSI interrupt functionality.
+ *
+ * Copyright (C) 2007-2008 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 "ssi_driver.h"
+
+static void reset_ch_read(struct ssi_channel *ch)
+{
+	ch->read_data.addr = NULL;
+	ch->read_data.size = 0;
+	ch->read_data.lch = -1;
+}
+
+static void reset_ch_write(struct ssi_channel *ch)
+{
+	ch->write_data.addr = NULL;
+	ch->write_data.size = 0;
+	ch->write_data.lch = -1;
+}
+
+int ssi_driver_write_interrupt(struct ssi_channel *ch, u32 *data)
+{
+	struct ssi_port *p = ch->ssi_port;
+	unsigned int port = p->port_number;
+	unsigned int channel = ch->channel_number;
+
+	clk_enable(p->ssi_controller->ssi_clk);
+	ssi_outl_or(SSI_SST_DATAACCEPT(channel), p->ssi_controller->base,
+					SSI_SYS_MPU_ENABLE_REG(port, p->n_irq));
+
+
+	return 0;
+}
+
+int ssi_driver_read_interrupt(struct ssi_channel *ch, u32 *data)
+{
+	struct ssi_port *p = ch->ssi_port;
+	unsigned int port = p->port_number;
+	unsigned int channel = ch->channel_number;
+
+	clk_enable(p->ssi_controller->ssi_clk);
+
+	ssi_outl_or(SSI_SSR_DATAAVAILABLE(channel), p->ssi_controller->base,
+					SSI_SYS_MPU_ENABLE_REG(port, p->n_irq));
+
+	clk_disable(p->ssi_controller->ssi_clk);
+
+	return 0;
+}
+
+void ssi_driver_cancel_write_interrupt(struct ssi_channel *ch)
+{
+	struct ssi_port *p = ch->ssi_port;
+	unsigned int port = p->port_number;
+	unsigned int channel = ch->channel_number;
+	void __iomem *base = p->ssi_controller->base;
+	u32 enable;
+
+	clk_enable(p->ssi_controller->ssi_clk);
+
+	enable = ssi_inl(base, SSI_SYS_MPU_ENABLE_REG(port, p->n_irq));
+	if (!(enable & SSI_SST_DATAACCEPT(channel))) {
+		dev_dbg(&ch->dev->device, LOG_NAME "Write cancel on not "
+		"enabled channel %d ENABLE REG 0x%08X", channel, enable);
+		clk_disable(p->ssi_controller->ssi_clk);
+		return;
+	}
+	ssi_outl_and(~SSI_SST_DATAACCEPT(channel), base,
+				SSI_SYS_MPU_ENABLE_REG(port, p->n_irq));
+	ssi_outl_and(~NOTFULL(channel), base, SSI_SST_BUFSTATE_REG(port));
+	reset_ch_write(ch);
+
+	clk_disable(p->ssi_controller->ssi_clk);
+	clk_disable(p->ssi_controller->ssi_clk);
+
+}
+
+void ssi_driver_cancel_read_interrupt(struct ssi_channel *ch)
+{
+	struct ssi_port *p = ch->ssi_port;
+	unsigned int port = p->port_number;
+	unsigned int channel = ch->channel_number;
+	void __iomem *base = p->ssi_controller->base;
+
+	clk_enable(p->ssi_controller->ssi_clk);
+
+	ssi_outl_and(~SSI_SSR_DATAAVAILABLE(channel), base,
+					SSI_SYS_MPU_ENABLE_REG(port, p->n_irq));
+	ssi_outl_and(~NOTEMPTY(channel), base, SSI_SSR_BUFSTATE_REG(port));
+	reset_ch_read(ch);
+
+	clk_disable(p->ssi_controller->ssi_clk);
+}
+
+static void do_channel_tx(struct ssi_channel *ch)
+{
+	struct ssi_dev *ssi_ctrl = ch->ssi_port->ssi_controller;
+	void __iomem *base = ssi_ctrl->base;
+	unsigned int n_ch;
+	unsigned int n_p;
+	unsigned int irq;
+
+	n_ch = ch->channel_number;
+	n_p = ch->ssi_port->port_number;
+	irq = ch->ssi_port->n_irq;
+
+	spin_lock(&ssi_ctrl->lock);
+
+	if (ch->write_data.addr == NULL) {
+		ssi_outl_and(~SSI_SST_DATAACCEPT(n_ch), base,
+					SSI_SYS_MPU_ENABLE_REG(n_p, irq));
+		reset_ch_write(ch);
+		spin_unlock(&ssi_ctrl->lock);
+		clk_disable(ssi_ctrl->ssi_clk);
+		(*ch->ops.write_done)(ch->dev);
+	} else {
+		ssi_outl(*(ch->write_data.addr), base,
+					SSI_SST_BUFFER_CH_REG(n_p, n_ch));
+		ch->write_data.addr = NULL;
+		spin_unlock(&ssi_ctrl->lock);
+	}
+}
+
+static void do_channel_rx(struct ssi_channel *ch)
+{
+	struct ssi_dev *ssi_ctrl = ch->ssi_port->ssi_controller;
+	void __iomem *base = ch->ssi_port->ssi_controller->base;
+	unsigned int n_ch;
+	unsigned int n_p;
+	unsigned int irq;
+
+	n_ch = ch->channel_number;
+	n_p = ch->ssi_port->port_number;
+	irq = ch->ssi_port->n_irq;
+
+	spin_lock(&ssi_ctrl->lock);
+
+	*(ch->read_data.addr) = ssi_inl(base, SSI_SSR_BUFFER_CH_REG(n_p, n_ch));
+
+	ssi_outl_and(~SSI_SSR_DATAAVAILABLE(n_ch), base,
+					SSI_SYS_MPU_ENABLE_REG(n_p, irq));
+	reset_ch_read(ch);
+
+	spin_unlock(&ssi_ctrl->lock);
+
+	(*ch->ops.read_done)(ch->dev);
+}
+
+void do_ssi_tasklet(unsigned long ssi_port)
+{
+	struct ssi_port *pport = (struct ssi_port *)ssi_port;
+	struct ssi_dev *ssi_ctrl = pport->ssi_controller;
+	void __iomem *base = ssi_ctrl->base;
+	unsigned int port = pport->port_number;
+	unsigned int channel = 0;
+	unsigned int irq = pport->n_irq;
+	u32 status_reg;
+	u32 enable_reg;
+	u32 ssr_err_reg;
+	u32 channels_served;
+
+	clk_enable(ssi_ctrl->ssi_clk);
+
+	channels_served = 0;
+	status_reg = ssi_inl(base, SSI_SYS_MPU_STATUS_REG(port, irq));
+	enable_reg = ssi_inl(base, SSI_SYS_MPU_ENABLE_REG(port, irq));
+
+	for (channel = 0; channel < pport->max_ch; channel++) {
+		if ((status_reg & SSI_SST_DATAACCEPT(channel)) &&
+		    (enable_reg & SSI_SST_DATAACCEPT(channel))) {
+			do_channel_tx(&pport->ssi_channel[channel]);
+			channels_served |= SSI_SST_DATAACCEPT(channel);
+		}
+
+		if ((status_reg & SSI_SSR_DATAAVAILABLE(channel)) &&
+		    (enable_reg & SSI_SSR_DATAAVAILABLE(channel))) {
+			do_channel_rx(&pport->ssi_channel[channel]);
+			channels_served |= SSI_SSR_DATAAVAILABLE(channel);
+		}
+	}
+
+	if ((status_reg & SSI_BREAKDETECTED) &&
+	    (enable_reg & SSI_BREAKDETECTED)) {
+		dev_info(&ssi_ctrl->pdev->dev,
+					"Hardware BREAK on port %d\n", port);
+		ssi_outl(0, base, SSI_SSR_BREAK_REG(port));
+		ssi_port_event_handler(pport, SSI_EVENT_BREAK_DETECTED, NULL);
+	}
+
+	if (status_reg & SSI_ERROROCCURED) {
+		ssr_err_reg = ssi_inl(base, SSI_SSR_ERROR_REG(port));
+		dev_err(&ssi_ctrl->pdev->dev, "SSI ERROR Port %d: 0x%02x\n",
+							port, ssr_err_reg);
+		ssi_outl(ssr_err_reg, base, SSI_SSR_ERRORACK_REG(port));
+		ssi_port_event_handler(pport, SSI_EVENT_ERROR, NULL);
+	}
+
+	ssi_outl((channels_served | SSI_ERROROCCURED | SSI_BREAKDETECTED), base,
+					SSI_SYS_MPU_STATUS_REG(port, irq));
+
+	clk_disable(ssi_ctrl->ssi_clk);
+	enable_irq(pport->irq);
+}
+
+irqreturn_t ssi_mpu_handler(int irq, void *ssi_port)
+{
+	struct ssi_port *p = (struct ssi_port *)ssi_port;
+
+	tasklet_hi_schedule(&p->ssi_tasklet);
+	disable_irq_nosync(p->irq);
+
+	return IRQ_HANDLED;
+}
-- 
1.5.3.6


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

* [RFC][PATCH 4/5] OMAP SSI integration into misc drivers
  2008-10-03 11:52     ` [RFC][PATCH 3/5] OMAP SSI driver code Carlos Chinea
@ 2008-10-03 11:52       ` Carlos Chinea
  2008-10-03 11:52         ` [RFC][PATCH 5/5] OMAP SSI API documentation Carlos Chinea
  2008-10-07  0:08         ` [RFC][PATCH 4/5] OMAP SSI integration into misc drivers Felipe Balbi
  2008-10-07  0:03       ` [RFC][PATCH 3/5] OMAP SSI driver code Felipe Balbi
  1 sibling, 2 replies; 14+ messages in thread
From: Carlos Chinea @ 2008-10-03 11:52 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-omap


Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
---
 drivers/misc/Kconfig  |    2 ++
 drivers/misc/Makefile |    1 +
 2 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index f0202ee..b09dc68 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -488,4 +488,6 @@ config SGI_GRU_DEBUG
 	This option enables addition debugging code for the SGI GRU driver. If
 	you are unsure, say N.
 
+source "drivers/misc/ssi/Kconfig"
+
 endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index b6167e7..39e153d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -31,3 +31,4 @@ obj-$(CONFIG_KGDB_TESTS)	+= kgdbts.o
 obj-$(CONFIG_SGI_XP)		+= sgi-xp/
 obj-$(CONFIG_SGI_GRU)		+= sgi-gru/
 obj-$(CONFIG_HP_ILO)		+= hpilo.o
+obj-$(CONFIG_OMAP_SSI)		+= ssi/
-- 
1.5.3.6


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

* [RFC][PATCH 5/5] OMAP SSI API documentation
  2008-10-03 11:52       ` [RFC][PATCH 4/5] OMAP SSI integration into misc drivers Carlos Chinea
@ 2008-10-03 11:52         ` Carlos Chinea
  2008-10-09 16:47           ` Felipe Balbi
  2008-10-07  0:08         ` [RFC][PATCH 4/5] OMAP SSI integration into misc drivers Felipe Balbi
  1 sibling, 1 reply; 14+ messages in thread
From: Carlos Chinea @ 2008-10-03 11:52 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-omap


Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
---
 Documentation/arm/OMAP/ssi/board-ssi.c.example |  216 ++++++++++++++++++++++
 Documentation/arm/OMAP/ssi/ssi                 |  232 ++++++++++++++++++++++++
 2 files changed, 448 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/arm/OMAP/ssi/board-ssi.c.example
 create mode 100644 Documentation/arm/OMAP/ssi/ssi

diff --git a/Documentation/arm/OMAP/ssi/board-ssi.c.example b/Documentation/arm/OMAP/ssi/board-ssi.c.example
new file mode 100644
index 0000000..a346628
--- /dev/null
+++ b/Documentation/arm/OMAP/ssi/board-ssi.c.example
@@ -0,0 +1,216 @@
+/*
+ * board-ssi.c.example
+ *
+ * Copyright (C) 2007-2008 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
+ */
+
+#ifdef CONFIG_OMAP_SSI
+
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/ssi_driver_if.h>
+#include <mach/ssi/ssi_sys_reg.h>
+#include <mach/ssi/ssi_ssr_reg.h>
+#include <mach/ssi/ssi_sst_reg.h>
+
+#include "clock.h"
+
+struct ssi_internal_clk {
+	struct clk clk;
+	struct clk **childs;
+	int n_childs;
+	struct platform_device *pdev;
+};
+
+static struct ssi_internal_clk ssi_clock;
+
+static void ssi_pdev_release(struct device *dev)
+{
+}
+
+static struct ssi_port_pd ssi_ports[] = {
+	[0] = {
+		.tx_mode = SSI_MODE_FRAME,
+		.tx_frame_size = SSI_FRAMESIZE_DEFAULT,
+		.divisor = SSI_DIVISOR_DEFAULT,
+		.tx_ch = SSI_CHANNELS_DEFAULT,
+		.arb_mode = SSI_ARBMODE_ROUNDROBIN,
+		.rx_mode = SSI_MODE_FRAME,
+		.rx_frame_size = SSI_FRAMESIZE_DEFAULT,
+		.rx_ch = SSI_CHANNELS_DEFAULT,
+		.timeout = SSI_TIMEOUT_DEFAULT,
+		.n_irq = 0,
+		},
+};
+
+static struct ssi_platform_data ssi_p_d = {
+	.clk_name = "ssi_clk",
+	.num_ports = ARRAY_SIZE(ssi_ports),
+	.ports = ssi_ports,
+};
+
+static struct resource ssi_resources[] = {
+	[0] = {
+		.start = SSI_IOMEM_BASE_ADDR,
+		.end = SSI_IOMEM_BASE_ADDR + SSI_IOMEM_SIZE,
+		.name = SSI_IOMEM_NAME,
+		.flags = IORESOURCE_MEM,
+		},
+	[1] = 	{
+		.start = SSI_P1_MPU_IRQ0,
+		.end = SSI_P1_MPU_IRQ0,
+		.name = SSI_P1_MPU_IRQ0_NAME,
+		.flags = IORESOURCE_IRQ,
+		},
+	[2] = 	{
+		.start = SSI_P1_MPU_IRQ1,
+		.end = SSI_P1_MPU_IRQ1,
+		.name = SSI_P1_MPU_IRQ1_NAME,
+		.flags = IORESOURCE_IRQ,
+		},
+	[3] = 	{
+		.start = SSI_P2_MPU_IRQ0,
+		.end = SSI_P2_MPU_IRQ0,
+		.name = SSI_P2_MPU_IRQ0_NAME,
+		.flags = IORESOURCE_IRQ,
+		},
+	[4] = 	{
+		.start = SSI_P2_MPU_IRQ1,
+		.end = SSI_P2_MPU_IRQ1,
+		.name = SSI_P2_MPU_IRQ1_NAME,
+		.flags = IORESOURCE_IRQ,
+		},
+	[5] = 	{
+		.start = SSI_GDD_MPU_IRQ,
+		.end = SSI_GDD_MPU_IRQ,
+		.name = SSI_GDD_MPU_IRQ_NAME,
+		.flags = IORESOURCE_IRQ,
+		},
+};
+
+static struct platform_device ssi_pdev = {
+	.name = "omap_ssi",
+	.id = -1,
+	.num_resources = ARRAY_SIZE(ssi_resources),
+	.resource = ssi_resources,
+	.dev = 	{
+		.release = ssi_pdev_release,
+		.platform_data = &ssi_p_d,
+		},
+};
+
+static void set_ssi_mode(struct platform_device *pdev, u32 mode)
+{
+	void __iomem *base = (void __iomem *)pdev->resource[0].start;
+	int port;
+	int num_ports = ((struct ssi_platform_data *)
+					(pdev->dev.platform_data))->num_ports;
+
+	for (port = 0; port < num_ports; port++) {
+		outl(mode, OMAP2_IO_ADDRESS(base + SSI_SST_MODE_REG(port)));
+		outl(mode, OMAP2_IO_ADDRESS(base + SSI_SSR_MODE_REG(port)));
+	}
+}
+
+static int ssi_clk_init(struct ssi_internal_clk *ssi_clk)
+{
+	const char *clk_names[] = { "ssi_ick", "ssi_ssr_fck" };
+	int i;
+	int j;
+
+	ssi_clk->n_childs = ARRAY_SIZE(clk_names);
+	ssi_clk->childs = kzalloc(ssi_clk->n_childs * sizeof(*ssi_clk->childs),
+								GFP_KERNEL);
+	if (!ssi_clk->childs)
+		return -ENOMEM;
+
+	for (i = 0; i < ssi_clk->n_childs; i++) {
+		ssi_clk->childs[i] = clk_get(NULL, clk_names[i]);
+		if (IS_ERR(ssi_clk->childs[i])) {
+			pr_err("Unable to get SSI clock: %s", clk_names[i]);
+			for (j = i - 1; j >= 0; j--)
+				clk_put(ssi_clk->childs[j]);
+			return -ENODEV;
+		}
+	}
+
+	return 0;
+}
+
+static int ssi_clk_enable(struct clk *clk)
+{
+	struct ssi_internal_clk *ssi_clk =
+				container_of(clk, struct ssi_internal_clk, clk);
+	int err = 0;
+	int i;
+	int j;
+
+	for (i = 0; ((i < ssi_clk->n_childs) && (err >= 0)); i++)
+		err = omap2_clk_enable(ssi_clk->childs[i]);
+
+	if (unlikely(err < 0)) {
+		pr_err("Error on SSI clk %d\n", i);
+		for (j = i - 1; j >= 0; j--)
+			omap2_clk_disable(ssi_clk->childs[j]);
+	} else {
+		if (ssi_clk->clk.usecount == 1)
+			set_ssi_mode(ssi_clk->pdev, SSI_MODE_FRAME);
+	}
+
+	return err;
+}
+
+static void ssi_clk_disable(struct clk *clk)
+{
+	struct ssi_internal_clk *ssi_clk =
+				container_of(clk, struct ssi_internal_clk, clk);
+
+	int i;
+
+	if (ssi_clk->clk.usecount == 0)
+		set_ssi_mode(ssi_clk->pdev, SSI_MODE_SLEEP);
+
+	for (i = 0; i < ssi_clk->n_childs; i++)
+		omap2_clk_disable(ssi_clk->childs[i]);
+}
+
+static struct ssi_internal_clk ssi_clock = {
+	.clk = {
+		.name = "ssi_clk",
+		.id = -1,
+		.enable = ssi_clk_enable,
+		.disable = ssi_clk_disable,
+	},
+	.pdev = &ssi_pdev,
+};
+
+void __init ssi_init(void)
+{
+	int err;
+
+	ssi_clk_init(&ssi_clock);
+	clk_register(&ssi_clock.clk);
+
+	err = platform_device_register(&ssi_pdev);
+	if (err < 0)
+		pr_err("Unable to register SSI platform device: %d\n", err);
+}
+#endif
diff --git a/Documentation/arm/OMAP/ssi/ssi b/Documentation/arm/OMAP/ssi/ssi
new file mode 100644
index 0000000..990ae48
--- /dev/null
+++ b/Documentation/arm/OMAP/ssi/ssi
@@ -0,0 +1,232 @@
+OMAP SSI API's How To
+=====================
+
+The Synchronous Serial Interface (SSI) is a high speed communication interface
+that is used for connecting OMAP to a cellular modem engine.
+
+The SSI interface supports full duplex communication over multiple channels and
+is capable of reaching speeds up to 110 Mbit/s
+
+I OMAP SSI driver API overview
+-----------------------------
+
+A) SSI Bus, SSI channels and protocol drivers overview. 
+
+The OMAP SSI driver is intended to be used inside the kernel by protocol drivers.
+
+The OMAP SSI abstracts the concept of SSI channels by creating an SSI bus an
+attaching SSI channel devices to it.(see Figure 1)
+
+Protocol drivers will then claim one or more SSI channels, after registering with the OMAP SSI driver.
+
+	+---------------------+		+----------------+
+	+  SSI channel device +		+  SSI protocol  +
+	+  (omap_ssi.pX-cY)   +	<-------+  driver        +
+	+---------------------+		+----------------+
+		|				|
+(/sys/bus/ssi/devices/omap_ssi.pX-cy)	(/sys/bus/ssi/drivers/ssi_protocol)
+		|				|
++---------------------------------------------------------------+
++			SSI bus					+	
++---------------------------------------------------------------+	
+	
+			Figure 1.
+
+(NOTE: omap_ssi.pX-cY represents the SSI channel Y on port X from the omap_ssi
+device)
+
+B) Data transfers
+
+The OMAP SSI driver exports an asynchronous interface for sending and receiving
+data over the SSI channels. Protocol drivers will register a set of read and write
+completion callbacks for each SSI channel they use.
+
+Protocol drivers call ssi_write/ssi_read functions to signal the OMAP SSI driver
+that is willing to write/read data to/from a channel. Transfers are completed only
+when the OMAP SSI driver calls the completion callback.
+
+An SSI channel can simultaneously have both a read and a write request
+pending, however, requests cannot be queued.
+
+It is safe to call ssi_write/ssi_read functions inside the callbacks functions.
+In fact, a protocol driver should normally re-issue the read request from within
+the read callback, in order to not miss any incoming messages.
+
+C) Error handling
+
+SSI is a multi channel interface but the channels share the same physical wires.
+Therefore, any transmission error potentially affects all the protocol drivers
+that sit on top of the SSI driver. Whenever an error occurs, it is broadcasted to
+all protocol drivers.
+
+Errors are signaled to the protocol drivers through the port_event callback.
+Protocol drivers can avoid receiving those notifications by not setting the
+SSI_EVENT_ERROR in the event_mask field.(see struct ssi_device_driver)
+
+Completion callbacks functions are only called when a transfer has succeed.
+
+II OMAP SSI API's
+-----------------
+
+A) Include
+
+#include<linux/ssi_driver_if.h>
+
+B) int register_ssi_driver(struct ssi_device_driver *driver);
+
+Description: Register an SSI protocol driver
+
+Parameter: A protocol driver declaration (see struct ssi_device_driver)
+
+B) void unregister_ssi_driver(struct ssi_device_driver *driver);
+
+Description: Unregister an SSI protocol driver
+
+Parameter: A protocol driver declaration (see struct ssi_device_driver)
+
+C) int ssi_open(struct ssi_device *dev);
+
+Description: Open an SSI device channel
+
+Parameter: The SSI channel
+
+D) int ssi_write(struct ssi_device *dev, u32 *data, unsigned int count);
+
+Description: Send data through an SSI channel. The transfer is only completed
+when the write_complete callback is called
+
+Parameters:
+	- dev: SSI channel
+	- data: pointer to the data to send
+	- count: number of 32-bit words to be sent
+
+E) void ssi_write_cancel(struct ssi_device *dev);
+
+Description: Cancel current pending write operation
+
+Parameters: SSI channel
+	
+F) int ssi_read(struct ssi_device *dev, u32 *data, unsigned int w_count);
+
+Description: Receive data through an SSI channel. The transfer is only completed
+when the read_complete callback is called
+
+Parameters:
+	- dev: SSI channel
+	- data: pointer where to store the data
+	- count: number of 32-bit words to be read
+
+
+G) void ssi_read_cancel(struct ssi_device *dev);
+
+Description: Cancel current pending read operation
+
+Parameters: SSI channel
+
+H) int ssi_ioctl(struct ssi_device *dev, unsigned int command, void *arg);
+
+Description: Apply some control command to the port associated to the given
+SSI channel
+
+Parameters:
+	- dev: SSI channel
+	- command: command to execute
+	- arg: parameter for the control command
+
+Commands:
+	- SSI_IOCTL_WAKE_UP: 
+		Description: Set SSI wakeup line for the channel
+		Parameters: None
+	- SSI_IOCTL_WAKE_DOWN:
+		Description: Unset SSI wakeup line for the channel
+		Parameters: None
+	- SSI_IOCTL_SEND_BREAK:
+		Description: Send a HW BREAK frame in FRAME mode
+		Parameters: None
+	- SSI_IOCTL_WAKE:
+		Description: Get wakeup line status
+		Parameters: Pointer to a u32 variable to return result
+		(Result: 0 means wakeline DOWN, other result means wakeline UP)
+
+I)void ssi_close(struct ssi_device *dev);
+
+Description: Close an SSI channel
+
+Parameters: The SSI channel to close
+
+J) void ssi_dev_set_cb(	struct ssi_device *dev,
+			void (*r_cb)(struct ssi_device *dev),
+			void (*w_cb)(struct ssi_device *dev));
+
+Description: Set the read and write callbacks for the SSI channel. This
+function is usually called in the probe function of the SSI protocol driver to
+set completion callbacks for the asynchronous read and write transfer
+
+Parameters:
+	- dev: SSI channel
+	- r_cb: Pointer to a callback function to signal that a read transfer is
+		completed
+	- w_cb: Pointer to a callback function to signal that a write transfer
+		is completed
+
+H) struct ssi_device_driver
+
+Description: Protocol drivers pass this struct to the register_ssi_driver function
+in order to register with the OMAP SSI driver. Among other things it tells the
+OMAP SSI driver which channels the protocol driver wants to allocate for its use
+
+Declaration:
+struct ssi_device_driver {
+	unsigned long		ctrl_mask;
+	unsigned long		ch_mask[SSI_MAX_PORTS];
+	unsigned long		event_mask;
+	void 			(*port_event) (int c_id, unsigned int port,
+						unsigned int event, void *arg);
+	int			(*probe)(struct ssi_device *dev);
+	int			(*remove)(struct ssi_device *dev);
+	int			(*suspend)(struct ssi_device *dev,
+						pm_message_t mesg);
+	int			(*resume)(struct ssi_device *dev);
+	struct device_driver 	driver;
+};
+
+Fields description:
+	ctrl_mask: SSI block ids to use
+	ch_mask[SSI_MAX_PORTS]: SSI channels to use
+	event_mask: SSI events to be notified
+	port_event: Function callback for notifying SSI events
+		   (i.e.: error transfer)
+		Parameters:
+			c_id: SSI Block id which generate the event
+			port: Port number which generate the event
+			event: Event code
+	probe: Probe function
+		Parameters: SSI channel
+	remove: Remove function
+		Parameters: SSI channel
+
+Example:
+
+static struct ssi_device_driver ssi_protocol_driver = {
+	.ctrl_mask = ANY_SSI_CONTROLLER,
+	.ch_mask[0] = CHANNEL(0) | CHANNEL(1),
+	.event_mask = SSI_EVENT_ERROR_MASK,
+	.port_event = port_event_callback,
+	.probe = ssi_proto_probe,
+	.remove = __devexit_p(ssi_proto_remove),
+	.driver = {
+			.name = "ssi_protocol",
+	},
+};
+
+
+III OMAP SSI platform_device
+----------------------------
+
+You can find a example of how to define an SSI platform device in:
+
+Documentation/arm/OMAP/ssi/board-ssi.c.example
+
+=================================================
+Contact: Carlos Chinea <carlos.chinea@nokia.com>
+Copyright (C) 2008 Nokia Corporation.
-- 
1.5.3.6


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

* Re: [RFC][PATCH 1/5] OMAP SSI hardware interface definitions
  2008-10-03 11:52 ` [RFC][PATCH 1/5] OMAP SSI hardware interface definitions Carlos Chinea
  2008-10-03 11:52   ` [RFC][PATCH 2/5] OMAP SSI driver interface Carlos Chinea
@ 2008-10-06 23:16   ` Felipe Balbi
  1 sibling, 0 replies; 14+ messages in thread
From: Felipe Balbi @ 2008-10-06 23:16 UTC (permalink / raw)
  To: Carlos Chinea; +Cc: linux-kernel, linux-omap

On Fri, Oct 03, 2008 at 02:52:26PM +0300, Carlos Chinea wrote:
> 
> Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
> ---
>  arch/arm/plat-omap/include/mach/ssi/ssi_gdd_reg.h  |  145 ++++++++++++++++++++
>  .../plat-omap/include/mach/ssi/ssi_reg_common.h    |   73 ++++++++++
>  arch/arm/plat-omap/include/mach/ssi/ssi_ssr_reg.h  |   56 ++++++++
>  arch/arm/plat-omap/include/mach/ssi/ssi_sst_reg.h  |   65 +++++++++
>  arch/arm/plat-omap/include/mach/ssi/ssi_sys_reg.h  |  107 ++++++++++++++
>  5 files changed, 446 insertions(+), 0 deletions(-)
>  create mode 100644 arch/arm/plat-omap/include/mach/ssi/ssi_gdd_reg.h
>  create mode 100644 arch/arm/plat-omap/include/mach/ssi/ssi_reg_common.h
>  create mode 100644 arch/arm/plat-omap/include/mach/ssi/ssi_ssr_reg.h
>  create mode 100644 arch/arm/plat-omap/include/mach/ssi/ssi_sst_reg.h
>  create mode 100644 arch/arm/plat-omap/include/mach/ssi/ssi_sys_reg.h
> 
> diff --git a/arch/arm/plat-omap/include/mach/ssi/ssi_gdd_reg.h b/arch/arm/plat-omap/include/mach/ssi/ssi_gdd_reg.h
> new file mode 100644
> index 0000000..5ed91cc
> --- /dev/null
> +++ b/arch/arm/plat-omap/include/mach/ssi/ssi_gdd_reg.h
> @@ -0,0 +1,145 @@
> +/*
> + * ssi_gdd_reg.h
> + *
> + * Hardware defintions for SSI Controller GDD registers.
> + *
> + * HARDWARE: OMAP 2420, OMAP 3430

This is unnecessary, if we happen to use the same in omap4 we're gonna
have to update. Please remove.

> + * Copyright (C) 2007-2008 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 __SSI_GDD_REG_H__
> +#define __SSI_GDD_REG_H__
> +
> +#include "ssi_reg_common.h"
> +
> +#define SSI_GDD_HW_ID_REG		SSI_GDD_REG32(0x0000)

this is not a good practice, use the correct offsets for all registers

> +#define SSI_GDD_PPORT_ID_REG		SSI_GDD_REG32(0x0010)
> +#define SSI_GDD_MPORT_ID_REG		SSI_GDD_REG32(0x0014)
> +
> +#define SSI_GDD_PPORT_SR_REG		SSI_GDD_REG32(0x0020)
> +#	define SSI_PPORT_ACTIVE_LCH_NUMBER_MASK	0xFF
> +
> +#define SSI_GDD_MPORT_SR_REG		SSI_GDD_REG32(0x0024)
> +#	define SSI_MPORT_ACTIVE_LCH_NUMBER_MASK	0xFF
> +
> +#define SSI_GDD_TEST_REG 		SSI_GDD_REG32(0x0040)
			   ^ trailing whitespace

> +#	define SSI_TEST			0x1
> +
> +#define SSI_GDD_GCR_REG			SSI_GDD_REG32(0x0100)
> +#	define	SSI_CLK_AUTOGATING_ON	(1<<3)
					  ^^ add spaces around <<

> +#	define	SSI_FREE		(1<<2)
> +#	define	SSI_SWITCH_OFF		0x1
> +
> +#define SSI_GDD_GRST_REG 		SSI_GDD_REG32(0x0200)
			   ^ trailing whitespace

> +#	define SSI_SWRESET		0x1
> +
> +#define SSI_GDD_CSDP_BASE		0x0800
> +#define SSI_GDD_CSDP_OFFSET		0x40
> +#define SSI_GDD_CSDP_REG(channel) 	SSI_GDD_REG16(SSI_GDD_CSDP_BASE +\
				    ^ trailing whitespace
> +					 (channel*SSI_GDD_CSDP_OFFSET))
						 ^ add spaces around *

> +#	define SSI_DST_BURST_EN_MASK	0xC000
> +#	define SSI_DST_SINGLE_ACCESS0	0x0
> +#	define SSI_DST_SINGLE_ACCESS	(0x1<<14)
> +#	define SSI_DST_BURST_4X32_BIT	(0x2<<14)
> +#	define SSI_DST_BURST_8x32_BIT	(0x3<<14)	/*NOTE: NOT SUPPORTED */

(1 << 14), (2 << 14) and (3 << 14) (up to 9) is enough.	 ^ add a space
before NOTE

> +
> +#	define SSI_DST_MASK		0x1E00

make all the hex numbers lower case

> +#	define SSI_DST_MEMORY_PORT	(0x8<<9)
> +#	define SSI_DST_PERIPHERAL_PORT	(0x9<<9)
> +
> +#	define SSI_SRC_BURST_EN_MASK	0x0180
> +#	define SSI_SRC_SINGLE_ACCESS0	0x0
> +#	define SSI_SRC_SINGLE_ACCESS	(0x1<<7)
> +#	define SSI_SRC_BURST_4x32_BIT	(0x2<<7)
> +#	define SSI_SRC_BURST_8x32_BIT	(0x3<<7)	/*NOTE: NOT SUPPORTED */
> +
> +#	define SSI_SRC_MASK		0x003C
> +#	define SSI_SRC_MEMORY_PORT	(0x8<<2)
> +#	define SSI_SRC_PERIPHERAL_PORT	(0x9<<2)
> +
> +#	define SSI_DATA_TYPE_MASK	0x0003
> +#	define SSI_DATA_TYPE_S32	0x2
> +
> +#define SSI_GDD_CCR_BASE 		0x0802
			   ^ trailing whitespace

> +#define SSI_GDD_CCR_OFFSET 		0x40
			     ^ trailing whitespace

> +#define SSI_GDD_CCR_REG(channel)	SSI_GDD_REG16(SSI_GDD_CCR_BASE +\
> +					 (channel*SSI_GDD_CCR_OFFSET))
						 ^ missing space

> +#	define SSI_DST_AMODE_MASK	(0x3<<14)
> +#	define SSI_DST_AMODE_CONST	0x0
> +#	define SSI_DST_AMODE_POSTINC	(0x1<<12)
> +
> +#	define SSI_SRC_AMODE_MASK	(0x3<<12)
> +#	define SSI_SRC_AMODE_CONST	0x0
> +#	define SSI_SRC_AMODE_POSTINC	(0x1<<12)
> +
> +#	define SSI_CCR_ENABLE		(0x1<<7)
> +
> +#	define SSI_CCR_SYNC_MASK	0x001F
> +
> +#define SSI_GDD_CICR_BASE 		0x0804
			    ^ trailing whitespace

> +#define SSI_GDD_CICR_OFFSET		0x40
> +#define SSI_GDD_CICR_REG(channel)	SSI_GDD_REG16(SSI_GDD_CICR_BASE +\
> +					 (channel*SSI_GDD_CICR_OFFSET))
> +#	define SSI_BLOCK_IE		(0x1<<5)
					(1 << 5)

> +#	define SSI_HALF_IE		(0x1<<2)
					(1 << 2)

> +#	define SSI_TOUT_IE		0x1
					(1 << 0)

> +
> +#define SSI_GDD_CSR_BASE 		0x0806
			   ^ trailing whitespace

> +#define SSI_GDD_CSR_OFFSET 		0x40
			     ^ trailing whitespace

> +#define SSI_GDD_CSR_REG(channel)	SSI_GDD_REG16(SSI_GDD_CSR_BASE +\
> +					 (channel*SSI_GDD_CSR_OFFSET))
> +#	define SSI_CSR_SYNC		(0x1<<6)
					(1 << 6)

> +#	define SSI_CSR_BLOCK		(0x1<<5)
					(1 << 5)

> +#	define SSI_CSR_HALF		(0x1<<2)
					(1 << 2)

> +#	define SSI_CSR_TOUR		0x1
> +
> +#define SSI_GDD_CSSA_BASE 		0x0808
			    ^ trailing whitespace

> +#define SSI_GDD_CSSA_OFFSET		0x40
> +#define SSI_GDD_CSSA_REG(channel)	SSI_GDD_REG32(SSI_GDD_CSSA_BASE +\
> +					 (channel*SSI_GDD_CSSA_OFFSET))
						 ^ missing space
> +
> +#define SSI_GDD_CDSA_BASE 		0x080C
			    ^ trailing whitespace

> +#define SSI_GDD_CDSA_OFFSET		0x40
> +#define SSI_GDD_CDSA_REG(channel)	SSI_GDD_REG32(SSI_GDD_CDSA_BASE +\
> +					 (channel*SSI_GDD_CDSA_OFFSET))
						 ^ missing space

> +
> +#define SSI_GDD_CEN_BASE		0x0810
> +#define SSI_GDD_CEN_OFFSET		0x40
> +#define SSI_GDD_CEN_REG(channel)	SSI_GDD_REG16(SSI_GDD_CEN_BASE +\
> +					 (channel*SSI_GDD_CEN_OFFSET))
						 ^ missing space

> +
> +#define SSI_GDD_CSAC_BASE 		0x0818
			    ^ trailing whitespace

> +#define SSI_GDD_CSAC_OFFSET		0x40
> +#define SSI_GDD_CSAC_REG(channel) 	SSI_GDD_REG16(SSI_GDD_CSAC_BASE +\
				    ^ trailing whitespace

> +					 (channel*SSI_GDD_CSAC_OFFSET))
						 ^ missing space

> +
> +#define SSI_GDD_CDAC_BASE 		0x081A
			    ^ trailing whitespace

> +#define SSI_GDD_CDAC_OFFSET		0x40
> +#define SSI_GDD_CDAC_REG(channel) 	SSI_GDD_REG16(SSI_GDD_CDAC_BASE +\
				    ^ trailing whitespace

> +					 (channel*SSI_GDD_CDAC_OFFSET))
						 ^ missing space
> +
> +#define SSI_GDD_CLNK_CTRL_BASE		0x0828
> +#define SSI_GDD_CLNK_CTRL_OFFSET	0x40
> +#define SSI_GDD_CLNK_CTRL_REG(channel)	SSI_GDD_REG16(SSI_GDD_CLNK_CTRL_BASE +\
> +					 (channel*SSI_GDD_CLNK_CTRL_OFFSET))
						 ^ missing space
> +#	define SSI_ENABLE_LNK		(0x1<<15)
					(1 << 15)

> +#	define SSI_STOP_LNK		(0x1<<14)
					(1 << 14)
> +#	define NEXT_CH_ID_MASK		0xF
> +
> +#endif
> diff --git a/arch/arm/plat-omap/include/mach/ssi/ssi_reg_common.h b/arch/arm/plat-omap/include/mach/ssi/ssi_reg_common.h
> new file mode 100644
> index 0000000..e66fb43
> --- /dev/null
> +++ b/arch/arm/plat-omap/include/mach/ssi/ssi_reg_common.h
> @@ -0,0 +1,73 @@
> +/*
> + * ssi_reg_common.h
> + *
> + * Common hardware definitions for SSI.
> + *
> + * HARDWARE: OMAP 2420, 3430

unnecessary

> + *
> + * Copyright (C) 2007-2008 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 __SSI_REG_COMMON_H__
> +#define __SSI_REG_COMMON_H__
> +
> +#define SSI_COMMON_BASE_ADDR		0x48050000
> +
> +/* SSI system registers */
> +#define SSI_SYS_OFFSET			0x8000
> +#define SSI_SYS_REG32(offset)		(SSI_SYS_OFFSET + (offset))
> +/* SSI GDD registers */
> +#define SSI_GDD_OFFSET			0x9000
> +#define SSI_GDD_REG32(offset)		(SSI_GDD_OFFSET + (offset))
> +#define SSI_GDD_REG16(offset)		(SSI_GDD_OFFSET + (offset))
> +
> +/* SSI SST registers */
> +/* General offset of SST port 1. First SST port register.*/
> +#define SSI_SST1_OFFSET			0xA000
> +/* General offset of SST port 2.*/
> +#define SSI_SST2_OFFSET			0xB000
> +/* Offset among the SST ports.*/
> +#define SSI_SST_PORT_OFFSET		0x1000
> +#define SSI_SST_OFFSET(port)		(SSI_SST1_OFFSET +\
> +					 ((port-1)*(SSI_SST_PORT_OFFSET)))
					       ^  ^ missing spaces

> +#define SSI_SST_REG(port, offset)	(SSI_SST_OFFSET(port) + (offset))
> +
> +/* SSI SSR registers */
> +/* General offset of SSR port 1. First SSR port register.*/
> +#define SSI_SSR1_OFFSET			0xA800
> +/* General offset of SSR port 2.*/
> +#define SSI_SSR2_OFFSET			0xB800
> +/* Offset among the SSR ports.*/
> +#define SSI_SSR_PORT_OFFSET		0x1000
> +#define SSI_SSR_OFFSET(port)		(SSI_SSR1_OFFSET +\
> +					 ((port-1)*(SSI_SSR_PORT_OFFSET)))
						  ^ missing space

> +#define SSI_SSR_REG(port, offset)	(SSI_SSR_OFFSET(port) + (offset))
> +
> +#define SSI_IOMEM_BASE_ADDR		SSI_COMMON_BASE_ADDR
> +#define SSI_IOMEM_SIZE			0x3C00
> +
> +/*
> + * FIXME: Following definitions to be removed.
> + * They are used for checking that the SSI clocks are stable before accessing
> + * the SSI registers.
> + */
> +#define OMAP_COMMON_BASE                0x48000000
> +#define CM_IDLEST1_CORE_REG		0x4A20
> +#define ST_SSI				1
> +#endif
> diff --git a/arch/arm/plat-omap/include/mach/ssi/ssi_ssr_reg.h b/arch/arm/plat-omap/include/mach/ssi/ssi_ssr_reg.h
> new file mode 100644
> index 0000000..b272047
> --- /dev/null
> +++ b/arch/arm/plat-omap/include/mach/ssi/ssi_ssr_reg.h
> @@ -0,0 +1,56 @@
> +/*
> + * ssi_sst_reg.h
> + *
> + * Hardware definitions for SSI controller SSR registers.
> + *
> + * HARDWARE: OMAP 2420, 3430

unnecessary

> + *
> + * Copyright (C) 2007-2008 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 __SSI_SSR_REG_H__
> +#define __SSI_SSR_REG_H__
> +
> +#include "ssi_reg_common.h"
> +
> +#define SSI_SSR_ID_REG(port)			SSI_SSR_REG(port, 0x0000)
> +#define SSI_SSR_MODE_REG(port)			SSI_SSR_REG(port, 0x0004)
> +#define SSI_SSR_FRAMESIZE_REG(port)		SSI_SSR_REG(port, 0x0008)
> +#define SSI_SSR_RXSTATE_REG(port)  		SSI_SSR_REG(port, 0x000C)
				    ^^ trailing whitespaces

> +#define SSI_SSR_BUFSTATE_REG(port) 		SSI_SSR_REG(port, 0x0010)
				     ^ trailing whitespace

> +#	define NOTEMPTY(channel)		(1<<channel)
						  ^^ missing spaces

> +#define SSI_SSR_BREAK_REG(port)			SSI_SSR_REG(port, 0x001C)
> +#define SSI_SSR_ERROR_REG(port)			SSI_SSR_REG(port, 0x0020)
> +#define SSI_SSR_ERRORACK_REG(port)		SSI_SSR_REG(port, 0x0024)
> +#define SSI_SSR_OVERRUN_REG(port)		SSI_SSR_REG(port, 0x002C)
> +#define SSI_SSR_OVERRUNACK_REG(port)		SSI_SSR_REG(port, 0x0030)
> +#define SSI_SSR_TIMEOUT_REG(port)		SSI_SSR_REG(port, 0x0030)
> +#	define SSI_TIMEOUT_DEFAULT		0
> +#define SSI_SSR_CHANNELS_REG(port)		SSI_SSR_REG(port, 0x0028)
> +
> +#define SSI_SSR_BUFFER_OFFSET_BASE		0x0080
> +#define SSI_SSR_BUFFER_CH_REG(port, channel)	SSI_SSR_REG(port, \
> +						(SSI_SSR_BUFFER_OFFSET_BASE +\
> +						 (channel * 0x04)))
> +
> +#define SSI_SSR_SWAPBUFFER_OFFSET_BASE		0x00C0
> +#define SSI_SSR_SWAPBUFFER_CH_REG(port, channel) SSI_SSR_REG(port, \
> +						(SSI_SSR_SWAPBUFFER_OFFSET_BASE\
> +						 + (channel * 0x04)))
> +#endif
> diff --git a/arch/arm/plat-omap/include/mach/ssi/ssi_sst_reg.h b/arch/arm/plat-omap/include/mach/ssi/ssi_sst_reg.h
> new file mode 100644
> index 0000000..ed15908
> --- /dev/null
> +++ b/arch/arm/plat-omap/include/mach/ssi/ssi_sst_reg.h
> @@ -0,0 +1,65 @@
> +/*
> + * ssi_sst_reg.h
> + *
> + * Hardware definitions for SSI controller SST registers.
> + *
> + * HARDWARE: OMAP 2420, 3430

unnecessary

> + *
> + * Copyright (C) 2007-2008 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 __SSI_SST_REG_H__
> +#define __SSI_SST_REG_H__
> +
> +#include "ssi_reg_common.h"
> +
> +#define SSI_SST_ID_REG(port)			SSI_SST_REG(port, 0x0000)
> +#define SSI_SST_MODE_REG(port)			SSI_SST_REG(port, 0x0004)
> +#	define SSI_MODE_VAL_MASK		0x3
> +#	define SSI_MODE_SLEEP			0x0
> +#	define SSI_MODE_STREAM			0x1
> +#	define SSI_MODE_FRAME			0x2
> +#	define SSI_MODE_MULTIPOINTS		0x3
> +#define SSI_SST_FRAMESIZE_REG(port)		SSI_SST_REG(port, 0x0008)
> +#	define SSI_FRAMESIZE_DEFAULT		31
> +#define SSI_SST_TXSTATE_REG(port)  		SSI_SST_REG(port, 0x000C)
				    ^^ trailing whitespaces

> +#	define	TXSTATE_IDLE			0x0
> +#define SSI_SST_BUFSTATE_REG(port) 		SSI_SST_REG(port, 0x0010)
				     ^ trailing whitespace

> +#	define 	NOTFULL(channel)		(1<<channel)
	      ^ trailing whitespace		  ^^ missing spaces

> +#define SSI_SST_DIVISOR_REG(port)		SSI_SST_REG(port, 0x0018)
> +#	define SSI_DIVISOR_DEFAULT		1
> +
> +#define SSI_SST_BREAK_REG(port)			SSI_SST_REG(port, 0x0020)
> +#define SSI_SST_CHANNELS_REG(port)		SSI_SST_REG(port, 0x0024)
> +#	define SSI_CHANNELS_DEFAULT		4
> +
> +#define SSI_SST_ARBMODE_REG(port)		SSI_SST_REG(port, 0x0028)
> +#	define SSI_ARBMODE_ROUNDROBIN		0x0
> +#	define SSI_ARBMODE_PRIORITY		0x1
> +
> +#define SSI_SST_BUFFER_OFFSET_BASE		0x0080
> +#define SSI_SST_BUFFER_CH_REG(port, channel)	SSI_SST_REG(port, \
> +						(SSI_SST_BUFFER_OFFSET_BASE +\
> +						 (channel * 0x4)))
> +
> +#define SSI_SST_SWAPBUF_OFFSET_BASE		0x00C0
> +#define SSI_SST_SWAPBUF_CH_REG(port, channel)	SSI_SST_REG(port, \
> +						(SSI_SST_SWAPBUF_OFFSET_BASE +\
> +						(channel * 0x4)))
> +#endif
> diff --git a/arch/arm/plat-omap/include/mach/ssi/ssi_sys_reg.h b/arch/arm/plat-omap/include/mach/ssi/ssi_sys_reg.h
> new file mode 100644
> index 0000000..2f1e1f5
> --- /dev/null
> +++ b/arch/arm/plat-omap/include/mach/ssi/ssi_sys_reg.h
> @@ -0,0 +1,107 @@
> +/*
> + * ssi_sys_reg.h
> + *
> + * Hardware defintions for SSI Controller system registers.
> + *
> + * HARDWARE: OMAP 2420, 3430

unnecessary

> + *
> + * Copyright (C) 2007-2008 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 __SSI_SYS_REG_H__
> +#define __SSI_SYS_REG_H__
> +
> +#include "ssi_reg_common.h"
> +
> +#define SSI_SYS_REVISION_REG		SSI_SYS_REG32(0x0000)
> +#	define SSI_REV_MASK		0x000000FF
> +#	define SSI_REV_MAJOR		0xF0
> +#	define SSI_REV_MINOR		0x0F
> +
> +#define SSI_SYS_SYSCONFIG_REG		SSI_SYS_REG32(0x0010)
> +#	define SSI_AUTOIDLE		1
> +#	define SSI_SOFTRESET		(1<<1)

add spaces

> +#	define SSI_SIDLEMODE_FORCE	0
> +#	define SSI_SIDLEMODE_NO		(1<<3)

add spaces

> +#	define SSI_SIDLEMODE_SMART	(1<<4)

add spaces

> +#	define SSI_SIDLEMODE_MASK	0x00000018
> +#	define SSI_MIDLEMODE_FORCE	0
> +#	define SSI_MIDLEMODE_NO		(1<<12)

add spaces

> +#	define SSI_MIDLEMODE_SMART	(1<<13)

add spaces

> +#	define SSI_MIDLEMODE_MASK	0x00003000
> +
> +#define SSI_SYS_SYSSTATUS_REG		SSI_SYS_REG32(0x0014)
> +#	define SSI_RESETDONE		1
> +
> +#define SSI_SYS_MPU_STATUS_BASE		0x0808
> +#define SSI_SYS_MPU_STATUS_PORT_OFFSET	0x10
> +#define SSI_SYS_MPU_STATUS_IRQ_OFFSET	0x2
> +#define SSI_SYS_MPU_STATUS_REG(port, irq) \
> +		SSI_SYS_REG32(SSI_SYS_MPU_STATUS_BASE +\
> +		 (((port-1)*SSI_SYS_MPU_STATUS_PORT_OFFSET) +\
> +		 (irq*SSI_SYS_MPU_STATUS_IRQ_OFFSET)))
> +
> +#define SSI_SYS_MPU_ENABLE_BASE		0x080C
> +#define SSI_SYS_MPU_ENABLE_PORT_OFFSET	0x10
> +#define SSI_SYS_MPU_ENABLE_IRQ_OFFSET	0x8
> +#define SSI_SYS_MPU_ENABLE_REG(port, irq) \
> +		SSI_SYS_REG32(SSI_SYS_MPU_ENABLE_BASE +\
> +		 (((port-1)*SSI_SYS_MPU_ENABLE_PORT_OFFSET) +\

add spaces

> +		 (irq*SSI_SYS_MPU_ENABLE_IRQ_OFFSET)))

add spaces

> +
> +#define SSI_SYS_DSP_STATUS_BASE		0x0830
> +#define SSI_SYS_DSP_STATUS_PORT_OFFSET	0x10
> +#define SSI_SYS_DSP_STATUS_IRQ_OFFSET	0x8
> +#define SSI_SYS_DSP_STATUS_REG(port, irq) \
> +		SSI_SYS_REG32(SSI_SYS_DSP_STATUS_BASE +\
> +		 (((port-1)*SSI_SYS_DSP_STATUS_PORT_OFFSET) +\

add spaces

> +		 (irq*SSI_SYS_DSP_STATUS_IRQ_OFFSET)))

add spaces

> +
> +#define SSI_SYS_DSP_ENABLE_BASE		0x0834
> +#define SSI_SYS_DSP_ENABLE_PORT_OFFSET	0x10
> +#define SSI_SYS_DSP_ENABLE_IRQ_OFFSET	0x8
> +#define SSI_SYS_DSP_ENABLE_REG(port, irq) \
> +		SSI_SYS_REG32(SSI_SYS_DSP_ENABLE_BASE +\
> +		 (((port-1)*SSI_SYS_DSP_ENABLE_PORT_OFFSET) +\

add spaces

> +		 (irq*SSI_SYS_DSP_ENABLE_IRQ_OFFSET)))

add spaces

> +#	define SSI_SST_DATAACCEPT(channel)	(1<<channel)

add spaces

> +#	define SSI_SSR_DATAAVAILABLE(channel)	(1<<(channel + 8))

add spaces

> +#	define SSI_SSR_DATAOVERRUN(channel)	(1<<(channel + 16))

add spaces

> +#	define SSI_ERROROCCURED			(1<<24)

add spaces

> +#	define SSI_BREAKDETECTED		(1<<25)

add spaces

> +
> +#define SSI_SYS_GDD_MPU_IRQ_STATUS_REG	SSI_SYS_REG32(0x0800)
> +#define SSI_SYS_GDD_MPU_IRQ_ENABLE_REG	SSI_SYS_REG32(0x0804)
> +#define SSI_SYS_GDD_DSP_IRQ_STATUS_REG 	SSI_SYS_REG32(0x0828)
					 ^ trailing whitespace

> +#define SSI_SYS_GDD_DSP_IRQ_ENABLE_REG 	SSI_SYS_REG32(0x082C)
					 ^ trailing whitespace

> +#	define SSI_GDD_LCH(channel)	(1<<channel)
> +
> +#define SSI_SYS_WAKE_OFFSET		0x10
> +#define SSI_SYS_WAKE_BASE		0x0C00
> +#define SSI_SYS_WAKE_REG(port) 		SSI_SYS_REG32(SSI_SYS_WAKE_BASE +\
				 ^ trailing whitespace

> +					 ((port-1)*SSI_SYS_WAKE_OFFSET))

add spaces

> +#define SSI_SYS_CLEAR_WAKE_BASE		0x0C04
> +#define SSI_SYS_CLEAR_WAKE_REG(port)	SSI_SYS_REG32(SSI_SYS_CLEAR_WAKE_BASE +\
> +					 ((port-1)*SSI_SYS_WAKE_OFFSET))

add spaces

> +#define SSI_SYS_SET_WAKE_BASE		0x0C08
> +#define SSI_SYS_SET_WAKE_REG(port)	SSI_SYS_REG32(SSI_SYS_SET_WAKE_BASE +\
> +					 ((port-1)*SSI_SYS_WAKE_OFFSET))

add spaces

> +#	define SSI_WAKE(channel)	(1<<channel)

add spaces

> +#	define SSI_WAKE_MASK		0xFF

lower case

-- 
balbi

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

* Re: [RFC][PATCH 2/5] OMAP SSI driver interface
  2008-10-03 11:52   ` [RFC][PATCH 2/5] OMAP SSI driver interface Carlos Chinea
  2008-10-03 11:52     ` [RFC][PATCH 3/5] OMAP SSI driver code Carlos Chinea
@ 2008-10-06 23:29     ` Felipe Balbi
  2008-10-07  1:03       ` David Brownell
  1 sibling, 1 reply; 14+ messages in thread
From: Felipe Balbi @ 2008-10-06 23:29 UTC (permalink / raw)
  To: Carlos Chinea; +Cc: linux-kernel, linux-omap

On Fri, Oct 03, 2008 at 02:52:27PM +0300, Carlos Chinea wrote:

add a patch description body here.

> Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
> ---
>  include/linux/ssi_driver_if.h |  137 +++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 137 insertions(+), 0 deletions(-)
>  create mode 100644 include/linux/ssi_driver_if.h
> 
> diff --git a/include/linux/ssi_driver_if.h b/include/linux/ssi_driver_if.h
> new file mode 100644
> index 0000000..3379dd0
> --- /dev/null
> +++ b/include/linux/ssi_driver_if.h

why wouldn't ssi.h be enough as a header name ?

looks much better and much easier to type: #include <linux/ssi.h>

> @@ -0,0 +1,137 @@
> +/*
> + * ssi_driver_if.h
> + *
> + * Header for the SSI driver low level interface.
> + *
> + * Copyright (C) 2007-2008 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 __SSI_DRIVER_IF_H__
> +#define __SSI_DRIVER_IF_H__
> +
> +#include <linux/device.h>
> +
> +#define SSI_IOMEM_NAME		"SSI_IO_MEM"
> +#define SSI_P1_MPU_IRQ0_NAME	"SSI_P1_MPU_IRQ0"
> +#define SSI_P2_MPU_IRQ0_NAME	"SSI_P2_MPU_IRQ0"
> +#define SSI_P1_MPU_IRQ1_NAME	"SSI_P1_MPU_IRQ1"
> +#define SSI_P2_MPU_IRQ1_NAME	"SSI_P2_MPU_IRQ1"
> +#define SSI_GDD_MPU_IRQ_NAME	"GDD_MPU_IRQ"

hmm... I wonder you really need these ? Maybe I have to wait until I
review the other patches but at least for the irq names, they look
weird. Are them used for request_irq() only ? If so, remove them and
pass it in the driver. There's no need for such a global definition.

> +
> +/* IRQ values */
> +#define SSI_P1_MPU_IRQ0		67
> +#define SSI_P2_MPU_IRQ0		68
> +#define SSI_P1_MPU_IRQ1		69
> +#define SSI_P2_MPU_IRQ1		70
> +#define SSI_GDD_MPU_IRQ		71

Most likely this will be platform_specific right ? So pass it to the
driver using a struct resource.

> +
> +/* The number of ports handled by the driver. (MAX:2) */
> +#define SSI_MAX_PORTS		1
> +
> +/*
> + * Masks used to enable or disable the reception of certain hardware events
> + * for the ssi_device_drivers
> + */
> +#define SSI_EVENT_CLEAR			0x00
> +#define SSI_EVENT_MASK			0xFF
> +#define SSI_EVENT_BREAK_DETECTED_MASK	0x01
> +#define SSI_EVENT_ERROR_MASK		0x02
> +
> +#define ANY_SSI_CONTROLLER	-1
> +#define ANY_CHANNEL		-1
> +#define CHANNEL(channel)	(1<<channel)

CHANNEL is not generic enough name, use, at least, SSI_CHANNEL and add
spaces around that left shift.

> +
> +enum {
> +	SSI_EVENT_BREAK_DETECTED = 0,
> +	SSI_EVENT_ERROR,
> +};
> +
> +enum {
> +	SSI_IOCTL_WAKE_UP,
> +	SSI_IOCTL_WAKE_DOWN,
> +	SSI_IOCTL_SEND_BREAK,
> +	SSI_IOCTL_WAKE,
> +};

hmm... ioctls, let's if they're really needed later.

> +
> +/* Forward references */
> +struct ssi_device;
> +struct ssi_dev;
> +struct ssi_port;
> +struct ssi_channel;
> +
> +struct ssi_port_pd {
> +	u32 tx_mode;
> +	u32 tx_frame_size;
> +	u32 divisor;
> +	u32 tx_ch;
> +	u32 arb_mode;
> +	u32 rx_mode;
> +	u32 rx_frame_size;
> +	u32 rx_ch;
> +	u32 timeout;
> +	u8 n_irq;
> +};
> +
> +struct ssi_platform_data {
> +	unsigned char *clk_name;

please don't pass clock names via platform_data. It's ugly and we're
having quite a big amount of work trying to find a solution to clean
omap drivers. Looks like we're gonna introduce omap_clk_associate()
which will associate the user device with the clock structure and
introduce a clk alias name (called function name) to avoid the problem
we have now with different omap versions (different clock names).

> +	struct ssi_dev *ssi_ctrl;
> +	struct ssi_port_pd *ports;
> +	u8 num_ports;
> +};
> +
> +struct ssi_device {
> +	int n_ctrl;
> +	unsigned int n_p;
> +	unsigned int n_ch;
> +	char modalias[BUS_ID_SIZE];
> +	struct ssi_channel *ch;
> +	struct device device;
> +};
> +
> +#define to_ssi_device(dev)	container_of(dev, struct ssi_device, device)
> +
> +struct ssi_device_driver {
> +	unsigned long		ctrl_mask;
> +	unsigned long		ch_mask[SSI_MAX_PORTS];
> +	unsigned long		event_mask;
> +	void 			(*port_event) (int c_id, unsigned int port,
	    ^ trailing whitespace

> +						unsigned int event, void *arg);
> +	int			(*probe)(struct ssi_device *dev);

and then this will be the only bus not using the MODULE_DEVICE_TABLE,
please fix. Introduce a MODULE_DEVICE_TABLE. i2c did it recently and
it's quite simple. Most likely this will have a similar implementation.

> +	int			(*remove)(struct ssi_device *dev);
> +	int			(*suspend)(struct ssi_device *dev,
> +						pm_message_t mesg);
> +	int			(*resume)(struct ssi_device *dev);
> +	struct device_driver 	driver;
			    ^ trailing whitespace
> +};
> +
> +#define to_ssi_device_driver(drv) container_of(drv, \
> +						struct ssi_device_driver, \
> +						driver)
> +
> +int register_ssi_driver(struct ssi_device_driver *driver);

let's try to keep a consistency with several other register and
unregister functions in the kernel and rename this to
ssi_register_driver(), likewise to ssi_unregister_driver()

> +void unregister_ssi_driver(struct ssi_device_driver *driver);
> +int ssi_open(struct ssi_device *dev);
> +int ssi_write(struct ssi_device *dev, u32 *data, unsigned int count);
> +void ssi_write_cancel(struct ssi_device *dev);
> +int ssi_read(struct ssi_device *dev, u32 *data, unsigned int w_count);
> +void ssi_read_cancel(struct ssi_device *dev);
> +int ssi_ioctl(struct ssi_device *dev, unsigned int command, void *arg);
> +void ssi_close(struct ssi_device *dev);
> +void ssi_dev_set_cb(struct ssi_device *dev, void (*r_cb)(struct ssi_device *dev)
> +					, void (*w_cb)(struct ssi_device *dev));
					^ this comma should be in the
					previous line

> +#endif

#endif /* __SSI__ blablabla */

btw, a bit of kerneldoc wouldn't hurt. Please document all these
structures.

-- 
balbi

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

* Re: [RFC][PATCH 3/5] OMAP SSI driver code
  2008-10-03 11:52     ` [RFC][PATCH 3/5] OMAP SSI driver code Carlos Chinea
  2008-10-03 11:52       ` [RFC][PATCH 4/5] OMAP SSI integration into misc drivers Carlos Chinea
@ 2008-10-07  0:03       ` Felipe Balbi
  2008-10-07 22:01         ` Felipe Balbi
  1 sibling, 1 reply; 14+ messages in thread
From: Felipe Balbi @ 2008-10-07  0:03 UTC (permalink / raw)
  To: Carlos Chinea; +Cc: linux-kernel, linux-omap

On Fri, Oct 03, 2008 at 02:52:28PM +0300, Carlos Chinea wrote:

Description. This seems to repeat in all your patches, take a look at
linux kernel patch format: http://linux.yyz.us/patch-format.html

> Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
> ---
>  drivers/misc/ssi/Kconfig          |   11 +
>  drivers/misc/ssi/Makefile         |    7 +
>  drivers/misc/ssi/ssi_driver.c     |  513 +++++++++++++++++++++++++++++++++++++
>  drivers/misc/ssi/ssi_driver.h     |  211 +++++++++++++++
>  drivers/misc/ssi/ssi_driver_bus.c |  192 ++++++++++++++
>  drivers/misc/ssi/ssi_driver_dma.c |  406 +++++++++++++++++++++++++++++
>  drivers/misc/ssi/ssi_driver_if.c  |  335 ++++++++++++++++++++++++
>  drivers/misc/ssi/ssi_driver_int.c |  232 +++++++++++++++++
>  8 files changed, 1907 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/misc/ssi/Kconfig
>  create mode 100644 drivers/misc/ssi/Makefile
>  create mode 100644 drivers/misc/ssi/ssi_driver.c
>  create mode 100644 drivers/misc/ssi/ssi_driver.h
>  create mode 100644 drivers/misc/ssi/ssi_driver_bus.c
>  create mode 100644 drivers/misc/ssi/ssi_driver_dma.c
>  create mode 100644 drivers/misc/ssi/ssi_driver_if.c
>  create mode 100644 drivers/misc/ssi/ssi_driver_int.c
> 
> diff --git a/drivers/misc/ssi/Kconfig b/drivers/misc/ssi/Kconfig
> new file mode 100644
> index 0000000..5e0842c
> --- /dev/null
> +++ b/drivers/misc/ssi/Kconfig
> @@ -0,0 +1,11 @@
> +#
> +# OMAP SSI HW kernel configuration
> +#
> +config OMAP_SSI
> +	tristate "OMAP SSI hardware driver"
> +	depends on ARCH_OMAP
> +	default n
> +	---help---
> +	  If you say Y here, you will enable the OMAP SSI hardware driver.
> +
> +	  If unsure, say N.
> diff --git a/drivers/misc/ssi/Makefile b/drivers/misc/ssi/Makefile
> new file mode 100644
> index 0000000..2b74e02
> --- /dev/null
> +++ b/drivers/misc/ssi/Makefile
> @@ -0,0 +1,7 @@
> +#
> +# Makefile for SSI drivers
> +#
> +omap_ssi-objs := 	ssi_driver.o ssi_driver_dma.o ssi_driver_int.o \
		   ^ trailing whitespace

> +			ssi_driver_if.o ssi_driver_bus.o
> +
> +obj-$(CONFIG_OMAP_SSI)	+= omap_ssi.o
> diff --git a/drivers/misc/ssi/ssi_driver.c b/drivers/misc/ssi/ssi_driver.c
> new file mode 100644
> index 0000000..292e868
> --- /dev/null
> +++ b/drivers/misc/ssi/ssi_driver.c
> @@ -0,0 +1,513 @@
> +/*
> + * ssi_driver.c
> + *
> + * Implements SSI module interface, initialization, and PM related functions.
> + *
> + * Copyright (C) 2007-2008 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/err.h>
> +#include "ssi_driver.h"
> +
> +static void ssi_dev_release(struct device *dev)
> +{
> +}
> +
> +static void __init ssi_undo_reg_dev(struct platform_device *pd,
> +								int p, int ch)

Take a few tabs out of the second line, it'll look better.

> +{
> +	struct ssi_platform_data *p_data =

	normally we call it pdata, but it's your call in this case

> +		(struct ssi_platform_data *) pd->dev.platform_data;

		unnecessary cast. Remove

> +	struct ssi_port *ssi_p;
> +	int port;
> +	int channel;
> +
> +	for (port = p; port >= 0; port--) {
> +		ssi_p = &p_data->ssi_ctrl->ssi_port[port];
> +		for (channel = ch; channel >= 0 ; channel--)
> +			device_unregister(&ssi_p->ssi_channel[channel].dev
> +								->device);

This function should be unnecessary. pdata usage is wrong.

> +	}
> +}
> +
> +static int __init reg_ssi_dev_ch(struct platform_device *pd,
> +						unsigned int p, unsigned int ch)

the following should be probe().

> +{
> +	struct ssi_device *dev;
> +	struct ssi_platform_data *p_data =
> +		(struct ssi_platform_data *) pd->dev.platform_data;

unnecessary cast, remove.

> +
> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> +	if (!dev)
> +		return -ENOMEM;
> +	dev->n_ctrl = pd->id;
> +	dev->n_p = p;
> +	dev->n_ch = ch;
> +	dev->ch = &p_data->ssi_ctrl->ssi_port[p].ssi_channel[ch];
> +	p_data->ssi_ctrl->ssi_port[p].ssi_channel[ch].dev = dev;

pdata usage is wrong. It should be used to initialize a few fields in
the device structure and then vanish.

> +	dev->device.bus = &ssi_bus_type;
> +	dev->device.parent = &pd->dev;
> +	dev->device.release = ssi_dev_release;
> +	if (dev->n_ctrl < 0)
> +		snprintf(dev->device.bus_id, sizeof(dev->device.bus_id),
> +			"omap_ssi-p%u.c%u", p, ch);
> +	else
> +		snprintf(dev->device.bus_id, sizeof(dev->device.bus_id),
> +			"omap_ssi%d-p%u.c%u", dev->n_ctrl, p, ch);
> +
> +	return device_register(&dev->device);
> +}
> +
> +static int __init register_ssi_devices(struct platform_device *pd)
> +{
> +	struct ssi_platform_data *p_data =
> +		(struct ssi_platform_data *) pd->dev.platform_data;

unnecessary cast. Remove

> +	unsigned int n_ch = 0;
> +	int port;
> +	int ch = 0;
> +	int err = 0;
> +
> +	for (port = 0; ((port < p_data->num_ports) && (err >= 0)); port++) {
> +		n_ch = max(p_data->ports[port].tx_ch,
> +						p_data->ports[port].rx_ch);
> +		for (ch = 0; ((ch < n_ch) && (err >= 0)); ch++)
> +			err = reg_ssi_dev_ch(pd, port, ch);
> +	}
> +
> +	if (err < 0) {
> +		port--;
> +		ch--;
> +		dev_err(&pd->dev, "Error registering ssi device channel "
> +					"p%d-c%d : %d\n", port, ch, err);
> +		if ((port == 0) && (ch == 0))
> +			return err;
> +		else if ((port > 0) && (ch == 0))
> +			ch = n_ch;
> +		ssi_undo_reg_dev(pd, port, ch);
> +	}
> +	return err;
> +}
> +
> +static int __init ssi_softreset(struct ssi_dev *ssi_ctrl)
> +{
> +	int ind = 0;
> +	void __iomem *base = ssi_ctrl->base;
> +	u32 status;
> +
> +	ssi_outl_or(SSI_SOFTRESET, base, SSI_SYS_SYSCONFIG_REG);
> +
> +	status = ssi_inl(base, SSI_SYS_SYSSTATUS_REG);
> +	while ((!(status & SSI_RESETDONE)) && (ind < SSI_RESETDONE_RETRIES)) {
> +		set_current_state(TASK_UNINTERRUPTIBLE);
> +		schedule_timeout(msecs_to_jiffies(SSI_RESETDONE_TIMEOUT));
> +		status = ssi_inl(base, SSI_SYS_SYSSTATUS_REG);
> +		ind++;
> +	}
> +
> +	if (ind >= SSI_RESETDONE_RETRIES)
> +		return -EIO;
> +
> +	/* Reseting GDD */
> +	ssi_outl_or(SSI_SWRESET, base, SSI_GDD_GRST_REG);
> +
> +	return 0;
> +}
> +
> +static void __init set_ssi_ports_default(
> +						struct platform_device *pd)
> +{
> +	struct ssi_port_pd *cfg = NULL;
> +	struct ssi_platform_data *p_data =
> +		(struct ssi_platform_data *) pd->dev.platform_data;
> +	unsigned int port = 0;
> +	void __iomem *base = p_data->ssi_ctrl->base;

is this really a virtual address?? then can you use
__raw_{read,write}[blw]() and drop ssi-specific read/write functions?

btw, the base address shouldn't come via pdata, it should come via
struct resource and get ioremap:ed in the driver. Recently we fixed all
the physical/virtual address mess and if we accept this it'll lead to
similar issues. Please fix.

> +
> +	for (port = 1; port <= p_data->num_ports; port++) {
> +		cfg = &p_data->ports[port - 1];
> +		ssi_outl(cfg->tx_mode, base, SSI_SST_MODE_REG(port));
> +		ssi_outl(cfg->tx_frame_size, base, SSI_SST_FRAMESIZE_REG(port));
> +		ssi_outl(cfg->divisor, base, SSI_SST_DIVISOR_REG(port));
> +		ssi_outl(cfg->tx_ch, base, SSI_SST_CHANNELS_REG(port));
> +		ssi_outl(cfg->arb_mode, base, SSI_SST_ARBMODE_REG(port));
> +
> +		ssi_outl(cfg->rx_mode, base, SSI_SSR_MODE_REG(port));
> +		ssi_outl(cfg->rx_frame_size, base, SSI_SSR_FRAMESIZE_REG(port));
> +		ssi_outl(cfg->rx_ch, base, SSI_SSR_CHANNELS_REG(port));
> +		ssi_outl(cfg->timeout, base, SSI_SSR_TIMEOUT_REG(port));
> +	}
> +}
> +
> +static int __init ssi_port_channels_init(
> +			struct platform_device *pd, unsigned int lport)
> +{
> +	struct ssi_platform_data *p_data =
> +		(struct ssi_platform_data *) pd->dev.platform_data;
> +	struct ssi_channel *ch;
> +	struct ssi_port *port;
> +	unsigned int n_ch;
> +	unsigned int ch_i;
> +
> +	n_ch = max(p_data->ports[lport].tx_ch, p_data->ports[lport].rx_ch);
> +	port =  &p_data->ssi_ctrl->ssi_port[lport];
> +	for (ch_i = 0; ch_i < n_ch; ch_i++) {
> +		ch = &port->ssi_channel[ch_i];
> +		ch->channel_number = ch_i;
> +		ch->flags = 0;
> +		ch->ssi_port = port;
> +		ch->read_data.addr = NULL;
> +		ch->read_data.size = 0;
> +		ch->read_data.lch = -1;
> +		ch->write_data.addr = NULL;
> +		ch->write_data.size = 0;
> +		ch->write_data.lch = -1;
> +		spin_lock_init(&ch->ssi_ch_lock);
> +	}
> +
> +	return 0;
> +}
> +
> +static int __init ssi_ports_init(struct platform_device *pd)
> +{
> +	struct ssi_platform_data *p_data =
> +		(struct ssi_platform_data *) pd->dev.platform_data;

unnecessary cast. Remove

> +	struct ssi_port *ssi_p = NULL;
> +	unsigned int port = 0;
> +	unsigned int err = 0;
> +	unsigned int n_ports;
> +
> +	n_ports = min(p_data->num_ports, (u8)SSI_MAX_PORTS);
> +
> +	for (port = 0; ((port < n_ports) && (err >= 0)); port++) {
> +		ssi_p = &p_data->ssi_ctrl->ssi_port[port];
> +		ssi_p->port_number = port + 1;
> +		ssi_p->ssi_controller = p_data->ssi_ctrl;
> +		ssi_p->max_ch = max(p_data->ports[port].tx_ch,
> +						p_data->ports[port].rx_ch);
> +		ssi_p->max_ch = min(ssi_p->max_ch, (u8)SSI_PORT_MAX_CH);
> +		ssi_p->n_irq = p_data->ports[port].n_irq;
> +		ssi_p->irq = 0;
> +		spin_lock_init(&ssi_p->lock);
> +		err = ssi_port_channels_init(pd, port);
> +	}
> +
> +	return 0;
> +}
> +
> +static int __init ssi_controller_init(struct platform_device *pd)
> +{
> +	struct ssi_platform_data *p_data =
> +		(struct ssi_platform_data *) pd->dev.platform_data;
> +	struct ssi_dev *ssi_ctrl = p_data->ssi_ctrl;
> +	int err;
> +
> +	ssi_ctrl->id = pd->id;
> +	ssi_ctrl->max_p = p_data->num_ports;

in the header file you have a define for the max_ports and here you use
platform_data to initialize it. Quite weird. Where are you using that
define ?

> +	ssi_ctrl->pdev = pd;

do not save the entire pdev pointer, save only the dev pointer use
platform_set_drvdata(pdev, ssi_ctrl);

> +	spin_lock_init(&ssi_ctrl->lock);
> +	ssi_ctrl->ssi_clk = clk_get(&pd->dev, p_data->clk_name);

please don't.

> +	if (IS_ERR(ssi_ctrl->ssi_clk)) {
> +		dev_err(&pd->dev, "Unable to get SSI clocks");
> +		return PTR_ERR(ssi_ctrl->ssi_clk);
> +	}
> +
> +	err = ssi_ports_init(pd);
> +	if (err < 0) {
> +		dev_err(&pd->dev, "Error on ssi_controller initialization");
> +		clk_put(ssi_ctrl->ssi_clk);
> +		return -EBUSY;
> +	}
> +
> +	return 0;
> +}
> +
> +static void ssi_controller_exit(struct platform_device *pd)
> +{
> +	struct ssi_platform_data *p_data =
> +		(struct ssi_platform_data *) pd->dev.platform_data;

unnecessary cast, remove.

> +	struct ssi_dev *ssi_ctrl = p_data->ssi_ctrl;
> +
> +	p_data->ssi_ctrl = NULL;
> +	ssi_ctrl->pdev = NULL;
> +	clk_put(ssi_ctrl->ssi_clk);
> +

unnecessary extra white line, remove.

> +}
> +
> +static int __init request_ssi_irqs(struct platform_device *pd)
> +{
> +	struct ssi_platform_data *p_data = pd->dev.platform_data;
> +	struct ssi_dev *ssi_ctrl = p_data->ssi_ctrl;
> +	struct ssi_port *ssi_p;
> +	struct resource *mpu_irq;

unnecessary if it's only to get the irq.

> +	int i;
> +	int j;
> +	int err = 0;
> +
> +	for (i = 0; ((i < p_data->num_ports) && (!err)); i++) {
> +		mpu_irq = platform_get_resource(pd, IORESOURCE_IRQ, i*2);

no

> +		if (!mpu_irq) {
> +			dev_err(&pd->dev, "SSI misses info for MPU IRQ"
> +							" on port %d", i + 1);
> +			err = -ENODEV;

use a goto to create a nice error path, then you remove this else below.

> +		} else {
> +			ssi_p = &ssi_ctrl->ssi_port[i];
> +			ssi_p->n_irq = 0; /* We only use one irq line */
> +			ssi_p->irq = mpu_irq->start;

			ssi_p->irq = platform_get_irq(pdev, 0);
			also gets rid of the unnecessary mpu_irq.

> +			tasklet_init(&ssi_p->ssi_tasklet,
> +					do_ssi_tasklet,	(unsigned long)ssi_p);
> +			err = request_irq(mpu_irq->start, ssi_mpu_handler,
> +				IRQF_DISABLED, mpu_irq->name, ssi_p);
> +			if (err < 0) {
> +				dev_err(&pd->dev, "FAILED request IRQ (%d)",
> +							 mpu_irq->start);
> +				err = -EBUSY;
> +			}
> +		}
> +	}
> +
> +	if (err < 0) {
> +		/* Let's free the reserved resources if there are any errors */
> +		for (j = 0; (j > (i-1)); j++) {
> +			printk(KERN_DEBUG LOG_NAME "Free resources on port %d",
> +									j+1);
> +			ssi_p = &ssi_ctrl->ssi_port[i];
> +			tasklet_disable(&ssi_p->ssi_tasklet);
> +			free_irq(ssi_p->irq, ssi_p);
> +		}
> +	}
> +
> +	return err;
> +}
> +
> +static int __init request_ssi_gdd_irq(struct platform_device *pd)
> +{
> +	struct ssi_platform_data *p_data = pd->dev.platform_data;
> +	struct ssi_dev *ssi_ctrl = p_data->ssi_ctrl;
> +	struct resource *gdd_irq;

no

> +	int err = 0;
> +
> +	gdd_irq = platform_get_resource(pd, IORESOURCE_IRQ, 4);

no

> +	if (!gdd_irq) {
> +		dev_err(&pd->dev, "SSI device has no info about GDD IRQ");
> +		return -ENODEV;
> +	}
> +
> +	ssi_ctrl->gdd_irq = gdd_irq->start;

hell no. Use platform_get_irq() like I said before.

> +	err = request_irq(gdd_irq->start, ssi_gdd_mpu_handler,
> +		IRQF_DISABLED, gdd_irq->name, ssi_ctrl);
> +	if (err < 0) {
> +		dev_err(&pd->dev, "FAILED to request IRQ NUMBER (%d)",
> +							gdd_irq->start);
> +		return -EBUSY;
> +	}
> +	tasklet_init(&ssi_ctrl->ssi_gdd_tasklet, do_ssi_gdd_tasklet,
> +			(unsigned long)ssi_ctrl);
> +
> +	return err;
> +}
> +
> +static void free_ssi_irqs(struct ssi_dev *ssi_ctrl)
> +{
> +	struct ssi_port *ssi_p;
> +	int i;
> +
> +	for (i = 0; (i < ssi_ctrl->max_p); i++) {
> +		ssi_p = &ssi_ctrl->ssi_port[i];
> +		tasklet_disable(&ssi_p->ssi_tasklet);
> +		free_irq(ssi_p->irq, ssi_p);
> +	}
> +}
> +
> +static void free_ssi_gdd_irq(struct ssi_dev *ssi_ctrl)
> +{
> +	tasklet_disable(&ssi_ctrl->ssi_gdd_tasklet);
> +	free_irq(ssi_ctrl->gdd_irq, ssi_ctrl);
> +}
> +
> +static int __init ssi_probe(struct platform_device *pd)
> +{
> +	struct resource *mem, *ioarea;
> +	struct ssi_dev *ssi_ctrl = NULL;
> +	struct ssi_platform_data *p_data = NULL;
> +	u32 revision = 0;
> +	int err = 0;
> +
> +
> +	if ((pd == NULL) || (pd->dev.platform_data == NULL)) {
> +		pr_err(LOG_NAME "No device/platform_data found on "
> +								"ssi device\n");
> +		return -ENODEV;
> +	}

aff... pd will never be NULL if this driver is probing. I'd rather:

struct ssi_platform_data *pdata = pd->dev.platform_data;

if (!pdata) {
	error messages goes here
}

> +
> +	ssi_ctrl = kzalloc(sizeof(*ssi_ctrl), GFP_KERNEL);
> +	if (ssi_ctrl == NULL) {
> +		dev_err(&pd->dev, "Could not allocate memory for"
> +					" struct ssi_dev\n");
> +		return -ENOMEM;
> +	}
> +
> +	p_data = (struct ssi_platform_data *) pd->dev.platform_data;

no cast needed.

> +	p_data->ssi_ctrl = ssi_ctrl;

hell no. platform_set_drvdata(pd, ssi_ctrl);

> +
> +	mem = platform_get_resource(pd, IORESOURCE_MEM, 0);
> +	if (!mem) {
> +		dev_err(&pd->dev, "SSI device does not have "
> +					"SSI IO memory region information\n");
> +		err = -ENODEV;
> +		goto rback5;
> +	}
> +
> +	err = ssi_controller_init(pd);
> +	if (err < 0) {
> +		dev_err(&pd->dev, "Could not initialize ssi controller:"
> +						" %d\n", err);
> +		goto rback5;
> +	}
> +
> +	ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
> +		 pd->dev.bus_id);
> +	if (!ioarea) {
> +		dev_err(&pd->dev, "Unable to request SSI IO memory "
> +								"region\n");
> +		err = -EBUSY;
> +		goto rollback4;
> +	}
> +
> +	ssi_ctrl->base = (void __iomem *)mem->start;

afff... this is terrible. Use ioremap();

> +
> +	clk_enable(ssi_ctrl->ssi_clk);
> +
> +	err = request_ssi_irqs(pd);
> +	if (err < 0)
> +		goto rollback1;
> +
> +	err = request_ssi_gdd_irq(pd);
> +	if (err < 0)
> +		goto rollback2;
> +
> +	err = ssi_softreset(ssi_ctrl);
> +	if (err < 0)
> +		goto rollback3;
> +
> +	/* Set default PM settings */
> +	ssi_outl((SSI_AUTOIDLE | SSI_SIDLEMODE_SMART | SSI_MIDLEMODE_SMART),
> +			ssi_ctrl->base,  SSI_SYS_SYSCONFIG_REG);
> +	ssi_outl(SSI_CLK_AUTOGATING_ON, ssi_ctrl->base, SSI_GDD_GCR_REG);
> +
> +	set_ssi_ports_default(pd);
> +
> +	/* Gather info from registers for the driver.(REVISION) */
> +	revision = ssi_inl(ssi_ctrl->base, SSI_SYS_REVISION_REG);
> +	dev_info(&pd->dev, "SSI Hardware REVISION %d.%d\n",
> +		 (revision & SSI_REV_MAJOR) >> 4, (revision & SSI_REV_MINOR));
> +
> +	err = register_ssi_devices(pd);
> +	if (err < 0)
> +		goto rollback3;
> +
> +	clk_disable(ssi_ctrl->ssi_clk);
> +
> +	return err;
> +
> +rollback3:
> +	free_ssi_gdd_irq(ssi_ctrl);
> +rollback2:
> +	free_ssi_irqs(ssi_ctrl);
> +rollback1:
	add iounmap() here
> +	release_mem_region(mem->start, mem->end - mem->start + 1);
> +	clk_disable(ssi_ctrl->ssi_clk);
> +rollback4:
> +	ssi_controller_exit(pd);
> +rback5:
> +	kfree(ssi_ctrl);
> +	return err;
> +}
> +
> +static void __exit close_all_ch(struct ssi_dev *ssi_ctrl)
> +{
> +	struct ssi_port *ssi_p;
> +	unsigned int port;
> +	unsigned int ch;
> +
> +	for (port = 0; port < ssi_ctrl->max_p; port++) {
> +		ssi_p = &ssi_ctrl->ssi_port[port];
> +		for (ch = 0; ch < ssi_p->max_ch; ch++)
> +			ssi_close(ssi_p->ssi_channel[ch].dev);
> +	}
> +}
> +
> +static int __exit ssi_remove(struct platform_device *pd)
> +{
> +	struct resource *mem;
> +	struct ssi_platform_data *p_data =
> +		(struct ssi_platform_data *) pd->dev.platform_data;
> +	struct ssi_dev *ssi_ctrl = p_data->ssi_ctrl;

This should ssi_ctrl = platform_get_drvdata(pd);

> +
> +	close_all_ch(ssi_ctrl);
> +	free_ssi_gdd_irq(ssi_ctrl);
> +	free_ssi_irqs(ssi_ctrl);
> +	mem = platform_get_resource(pd, IORESOURCE_MEM, 0);
> +	if (mem)
> +		release_mem_region(mem->start, mem->end - mem->start + 1);

when you add proper ioremap(), add iounmap here.

> +	ssi_controller_exit(pd);
> +	kfree(ssi_ctrl);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver ssi_pdriver = {
> +	.probe = ssi_probe,
> +	.remove = __exit_p(ssi_remove),
> +	.driver = {
> +		.name = "omap_ssi",
> +		.owner = THIS_MODULE,
> +	}
> +};
> +
> +static int __init ssi_driver_init(void)
> +{
> +	int err = 0;
> +
> +	pr_info("SSI DRIVER Version " SSI_DRIVER_VERSION "\n");
> +
> +	ssi_bus_init();
> +	err = platform_driver_probe(&ssi_pdriver, ssi_probe);
> +	if (err < 0) {
> +		pr_err(LOG_NAME "Platform DRIVER register FAILED: %d\n", err);
> +		ssi_bus_exit();
> +		return err;
> +	}
> +
> +	return 0;

return platform_driver_register(&ssi_pdriver);
should be enough.

> +}
> +
> +static void __exit ssi_driver_exit(void)
> +{
> +	ssi_bus_exit();
> +	platform_driver_unregister(&ssi_pdriver);
> +
> +	pr_info("SSI DRIVER removed\n");
> +}
> +
> +module_init(ssi_driver_init);
> +module_exit(ssi_driver_exit);
> +
> +MODULE_ALIAS("platform:omap_ssi");
> +MODULE_AUTHOR(SSI_DRIVER_AUTHOR);
> +MODULE_DESCRIPTION(SSI_DRIVER_DESC);
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/misc/ssi/ssi_driver.h b/drivers/misc/ssi/ssi_driver.h
> new file mode 100644
> index 0000000..3c6d849
> --- /dev/null
> +++ b/drivers/misc/ssi/ssi_driver.h
> @@ -0,0 +1,211 @@
> +/*
> + * ssi_driver.h
> + *
> + * Header file for the SSI driver low level interface.
> + *
> + * Copyright (C) 2007-2008 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 __SSI_DRIVER_H__
> +#define __SSI_DRIVER_H__
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/clk.h>
> +#include <linux/ioport.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/spinlock.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +#include <linux/gpio.h>
> +
> +#include <mach/ssi/ssi_reg_common.h>
> +#include <mach/ssi/ssi_sys_reg.h>
> +#include <mach/ssi/ssi_ssr_reg.h>
> +#include <mach/ssi/ssi_sst_reg.h>
> +#include <mach/ssi/ssi_gdd_reg.h>
> +
> +#include <linux/ssi_driver_if.h>
> +
> +#define	SSI_DRIVER_VERSION	"1.1-rc2"
> +#define SSI_DRIVER_AUTHOR	"Carlos Chinea / Nokia"
> +#define SSI_DRIVER_DESC		"Synchronous Serial Interface Driver"
> +#define SSI_DRIVER_LINCESE	"GPL"
> +#define SSI_DRIVER_NAME		"ssi_driver"
> +
> +#define SSI_DEVICE_NAME		"ssi_device"

these should be in the C-file. Nobody needs to access them

> +
> +/* 10 ms */
> +#define SSI_RESETDONE_TIMEOUT	10
> +/* Let's retry 20 times.=>20x10 ms=200 ms */
> +#define SSI_RESETDONE_RETRIES	20
> +
> +/* Channel states */
> +#define	SSI_CH_OPEN		0x01
> +
> +/*
> + * The number of channels to use by the driver in the ports, or the highest
> + * port channel number (+1) used. (MAX:8)
> + */
> +#define SSI_PORT_MAX_CH		4
> +/* Number of logical channel in GDD */
> +#define SSI_NUM_LCH		8
> +
> +#define LOG_NAME		"OMAP SSI: "
> +#define SSI_PREFIX		"ssi:"
> +
> +/*
> + * Callbacks use by the SSI upper layer (SSI protocol) to receive data
> + * from the port channels.
> + */
> +struct ssi_channel_ops {
> +	void (*write_done) (struct ssi_device *dev);
> +	void (*read_done) (struct ssi_device *dev);
> +};
> +
> +struct ssi_data {
> +	/* Pointer to the data to be sent/received */
> +	u32 *addr;
> +	/*
> +	 * Number of words to be sent or space reserved for data to be
> +	 * received.
> +	 */
> +	unsigned int size;
> +	/* Holds GDD logical channed number */
> +	int lch;
> +};
> +
> +struct ssi_channel {
> +	struct ssi_channel_ops ops;
> +	struct ssi_data read_data;
> +	struct ssi_data write_data;
> +	struct ssi_port *ssi_port;
> +	u8 flags;
> +	u8 channel_number;
> +	spinlock_t ssi_ch_lock;
> +	struct ssi_device *dev;
> +	void *priv;
> +};
> +
> +/* Holds information related to SSI port */
> +struct ssi_port {
> +	struct ssi_channel ssi_channel[SSI_PORT_MAX_CH];
> +	struct ssi_dev *ssi_controller;
> +	u8 port_number;
> +	u8 max_ch;
> +	u8 n_irq; /* IRQ0 or IRQ1 */
> +	int irq	    /* Actual IRQ number */;
> +	spinlock_t lock;
> +	struct tasklet_struct ssi_tasklet;
> +};
> +
> +/*
> + * Struct definition to hold information about the clocks, ssi controller
> + * and the ssi ports.
> + */
> +struct ssi_dev {
> +	/* Holds reference to PORT 1 (and PORT2 if defined) */
> +	struct ssi_port ssi_port[SSI_MAX_PORTS];
> +	int id;
> +	u8 flags;
> +	u8 max_p;
> +	struct clk *ssi_clk;
> +	void __iomem *base;
> +	spinlock_t lock;
> +	int gdd_irq;
> +	struct tasklet_struct ssi_gdd_tasklet;
> +	struct platform_device *pdev;
> +};
> +
> +/* SSI Bus */
> +struct ssi_port_event {
> +	struct ssi_port *ssi_port;
> +	unsigned int event;
> +	void *priv;
> +};
> +extern struct bus_type ssi_bus_type;
> +
> +int ssi_port_event_handler(struct ssi_port *p, unsigned int event, void *arg);
> +int ssi_bus_init(void);
> +void ssi_bus_exit(void);
> +/* End SSI Bus */
> +
> +int ssi_driver_read_interrupt(struct ssi_channel *ssi_channel, u32 *data);
> +int ssi_driver_write_interrupt(struct ssi_channel *ssi_channel, u32 *data);
> +int ssi_driver_read_dma(struct ssi_channel *ssi_channel, u32 *data,
> +			unsigned int count);
> +int ssi_driver_write_dma(struct ssi_channel *ssi_channel, u32 *data,
> +			unsigned int count);
> +
> +void ssi_driver_cancel_write_interrupt(struct ssi_channel *ch);
> +void ssi_driver_cancel_read_interrupt(struct ssi_channel *ch);
> +void ssi_driver_cancel_write_dma(struct ssi_channel *ch);
> +void ssi_driver_cancel_read_dma(struct ssi_channel *ch);
> +
> +irqreturn_t ssi_mpu_handler(int irq, void *ssi_port);
> +irqreturn_t ssi_gdd_mpu_handler(int irq, void *ssi_controller);
> +
> +void do_ssi_tasklet(unsigned long ssi_port);
> +void do_ssi_gdd_tasklet(unsigned long device);
> +
> +static inline u32 ssi_inl(void __iomem *base, u32 offset)
> +{
> +	return inl(OMAP2_IO_ADDRESS(base + offset));
> +}
> +
> +static inline void ssi_outl(u32 data, void __iomem *base, u32 offset)
> +{
> +	outl(data, OMAP2_IO_ADDRESS(base + offset));
> +}
> +
> +static inline void ssi_outl_or(u32 data, void __iomem *base, u32 offset)
> +{
> +	u32 tmp = ssi_inl(base, offset);
> +	ssi_outl((tmp | data), base, offset);
> +}
> +
> +static inline void ssi_outl_and(u32 data, void __iomem *base, u32 offset)
> +{
> +	u32 tmp = ssi_inl(base, offset);
> +	ssi_outl((tmp & data), base, offset);
> +}
> +
> +static inline u16 ssi_inw(void __iomem *base, u32 offset)
> +{
> +	return inw(OMAP2_IO_ADDRESS(base + offset));
> +}
> +
> +static inline void ssi_outw(u16 data, void __iomem *base, u32 offset)
> +{
> +	outw(data, OMAP2_IO_ADDRESS(base + offset));
> +}
> +
> +static inline void ssi_outw_or(u16 data, void __iomem *base, u32 offset)
> +{
> +	u16 tmp = ssi_inw(base, offset);
> +	ssi_outw((tmp | data), base, offset);
> +}
> +
> +static inline void ssi_outw_and(u16 data, void __iomem *base, u32 offset)
> +{
> +	u16 tmp = ssi_inw(base, offset);
> +	ssi_outw((tmp & data), base, offset);
> +}
> +#endif
> diff --git a/drivers/misc/ssi/ssi_driver_bus.c b/drivers/misc/ssi/ssi_driver_bus.c
> new file mode 100644
> index 0000000..6a07ee0
> --- /dev/null
> +++ b/drivers/misc/ssi/ssi_driver_bus.c
> @@ -0,0 +1,192 @@
> +/*
> + * ssi_driver_bus.c
> + *
> + * Implements SSI bus, device and driver interface.
> + *
> + * Copyright (C) 2007-2008 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/device.h>
> +#include "ssi_driver.h"
> +
> +/* LDM. defintions for the ssi bus, ssi device, and ssi_device driver */
> +struct bus_type ssi_bus_type;
> +
> +static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
> +								char *buf)
> +{
> +	return snprintf(buf, BUS_ID_SIZE + 1, "%s%s\n", SSI_PREFIX,
> +								dev->bus_id);
> +}
> +
> +static struct device_attribute ssi_dev_attrs[] = {
> +	__ATTR_RO(modalias),
> +	__ATTR_NULL,
> +};
> +
> +static int ssi_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
> +{
> +	add_uevent_var(env, "MODALIAS=%s%s", SSI_PREFIX, dev->bus_id);
> +	return 0;
> +}
> +
> +/* NOTE: Function called in interrupt context */
> +static int ssi_e_handler(struct device_driver *drv, void *p_event)
> +{
> +	struct ssi_port_event *event = (struct ssi_port_event *)p_event;
> +	struct ssi_device_driver *ssi_drv =  to_ssi_device_driver(drv);
> +	struct ssi_port *p = event->ssi_port;
> +
> +	BUG_ON(p_event == NULL);
> +
> +	if ((ssi_drv->port_event) &&
> +		(test_bit(event->event, &ssi_drv->event_mask)) &&
> +		((p->ssi_controller->id == -1) ||
> +		(test_bit(p->ssi_controller->id, &ssi_drv->ctrl_mask))) &&
> +		(ssi_drv->ch_mask[p->port_number - 1] != 0)) {
> +
> +		(*ssi_drv->port_event)(p->ssi_controller->id, p->port_number,
> +					event->event, event->priv);
> +	}
> +
> +	return 0;
> +}
> +
> +int ssi_port_event_handler(struct ssi_port *p, unsigned int event, void *arg)
> +{
> +	int err = 0;
> +	struct ssi_port_event p_ev = {
> +		.ssi_port = p,
> +		.event = event,
> +		.priv = arg
> +	};
> +
> +	BUG_ON(p == NULL);
> +
> +	err = bus_for_each_drv(&ssi_bus_type, NULL, &p_ev, ssi_e_handler);
> +
> +	return err;
> +}
> +
> +static int ssi_bus_match(struct device *device, struct device_driver *driver)
> +{
> +	struct ssi_device *dev = to_ssi_device(device);
> +	struct ssi_device_driver *drv = to_ssi_device_driver(driver);
> +
> +	if (!test_bit(dev->n_ctrl, &drv->ctrl_mask))
> +		return 0;
> +
> +	if (!test_bit(dev->n_ch, &drv->ch_mask[dev->n_p]))
> +		return 0;
> +
> +	return 1;
> +}
> +
> +int ssi_bus_unreg_dev(struct device *device, void *p)
> +{
> +	device->release(device);
> +	device_unregister(device);
> +
> +	return 0;
> +}
> +
> +int __init ssi_bus_init(void)
> +{
> +
> +	return bus_register(&ssi_bus_type);
> +
> +}
> +
> +void ssi_bus_exit(void)
> +{
> +	bus_for_each_dev(&ssi_bus_type, NULL, NULL, ssi_bus_unreg_dev);
> +	bus_unregister(&ssi_bus_type);
> +}
> +
> +static int ssi_driver_probe(struct device *dev)
> +{
> +	struct ssi_device_driver *drv = to_ssi_device_driver(dev->driver);
> +
> +	return 	drv->probe(to_ssi_device(dev));
	      ^ trailing whitespace

> +}
> +
> +static int ssi_driver_remove(struct device *dev)
> +{
> +	struct ssi_device_driver *drv = to_ssi_device_driver(dev->driver);
> +
> +	return drv->remove(to_ssi_device(dev));
> +}
> +
> +static int ssi_driver_suspend(struct device *dev, pm_message_t mesg)
> +{
> +	struct ssi_device_driver *drv = to_ssi_device_driver(dev->driver);
> +
> +	return drv->suspend(to_ssi_device(dev), mesg);
> +}
> +
> +static int ssi_driver_resume(struct device *dev)
> +{
> +	struct ssi_device_driver *drv = to_ssi_device_driver(dev->driver);
> +
> +	return drv->resume(to_ssi_device(dev));
> +}
> +
> +struct bus_type ssi_bus_type = {
> +	.name = "ssi",
> +	.dev_attrs = ssi_dev_attrs,
> +	.match = ssi_bus_match,
> +	.uevent = ssi_bus_uevent,

tabify these

	.name		= "ssi",
	.dev_attrs	= ssi_dev_attrs,
	.match		= ssi_bus_match,
	.uevent		= ssi_bus_uevent,

> +};
> +
> +/**
> + * register_ssi_driver - Register SSI device driver
> + * @driver - reference to the SSI device driver.
> + */
> +int register_ssi_driver(struct ssi_device_driver *driver)
> +{
> +	int ret = 0;
> +
> +	BUG_ON(driver == NULL);

don't bug, just return. BUG will oops the kernel. Returning would be
more appropriate since what needs fix is the driver trying to register
not the bus driver.

> +
> +	driver->driver.bus = &ssi_bus_type;
> +	if (driver->probe)
> +		driver->driver.probe = ssi_driver_probe;
> +	if (driver->remove)
> +		driver->driver.remove = ssi_driver_remove;
> +	if (driver->suspend)
> +		driver->driver.suspend = ssi_driver_suspend;
> +	if (driver->resume)
> +		driver->driver.resume = ssi_driver_resume;
> +
> +	ret = driver_register(&driver->driver);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(register_ssi_driver);
> +
> +/**
> + * unregister_ssi_driver - Unregister SSI device driver
> + * @driver - reference to the SSI device driver.
> + */
> +void unregister_ssi_driver(struct ssi_device_driver *driver)
> +{
> +	BUG_ON(driver == NULL);

ditto.

> +
> +	driver_unregister(&driver->driver);
> +}
> +EXPORT_SYMBOL(unregister_ssi_driver);
> diff --git a/drivers/misc/ssi/ssi_driver_dma.c b/drivers/misc/ssi/ssi_driver_dma.c
> new file mode 100644
> index 0000000..2524a12
> --- /dev/null
> +++ b/drivers/misc/ssi/ssi_driver_dma.c
> @@ -0,0 +1,406 @@
> +/*
> + * ssi_driver_dma.c
> + *
> + * Implements SSI low level interface driver functionality with DMA support.
> + *
> + * Copyright (C) 2007-2008 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/dma-mapping.h>
> +#include "ssi_driver.h"
> +
> +#define SSI_SYNC_WRITE	0
> +#define SSI_SYNC_READ  	1
			^^ trailing whitespaces
> +
> +static unsigned char sync_table[2][2][8] = {
> +	{
> +	 {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
> +	 {0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00}
> +	 },
> +	{

	}, {   would look better

> +	 {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17},
> +	 {0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}
> +	 }
> +};
> +
> +static unsigned int get_sync_type(unsigned int sync)
> +{
> +	return (sync & 0x10) ? SSI_SYNC_READ : SSI_SYNC_WRITE;
> +}
> +
> +static unsigned int get_sync_port(unsigned int sync)
> +{
> +	if (((sync >= 0x01) && (sync <= 0x08)) ||
> +					((sync >= 0x10) && (sync <= 0x17)))
> +		return 1;
> +	else if (((sync >= 0x09) && (sync <= 0x0F)) ||
> +					((sync >= 0x18) && (sync <= 0x1E)))
> +		return 2;
> +	else
> +		return 3;
> +}
> +
> +static unsigned int get_sync_channel(unsigned int sync)
> +{
> +	if ((sync == 0x00) || (sync == 0x1F))
> +		return 8;
> +
> +	if (sync & 0x10)
> +		return (sync & 0x0F) % 8;
> +	else
> +		return (sync - 1) % 8;
> +}
> +
> +static unsigned int get_sync(unsigned int port,
> +			     unsigned int channel, unsigned int type)
> +{
> +	if ((port == 0) || (port > SSI_MAX_PORTS) ||
> +			(channel >= SSI_PORT_MAX_CH) || (type > SSI_SYNC_READ))
> +		return 0x00;
> +
> +	return sync_table[type][port - 1][channel];
> +}
> +
> +static void rst_ch_read(struct ssi_dev *ssi_ctrl,
> +				unsigned int n_p, unsigned int n_ch)
> +{
> +	struct ssi_channel *ch =
> +		&ssi_ctrl->ssi_port[n_p - 1].ssi_channel[n_ch];
> +
> +	ch->read_data.addr = NULL;
> +	ch->read_data.size = 0;
> +	ch->read_data.lch = -1;
> +}
> +
> +static void rst_ch_write(struct ssi_dev *ssi_ctrl,
> +				unsigned int n_p, unsigned int n_ch)
> +{
> +	struct ssi_channel *ch =
> +		&ssi_ctrl->ssi_port[n_p - 1].ssi_channel[n_ch];
> +
> +	ch->write_data.addr = NULL;
> +	ch->write_data.size = 0;
> +	ch->write_data.lch = -1;
> +}
> +
> +/*
> + * get_free_lch - Get a free GDD(DMA)logical channel
> + * @ssi_ctrl- SSI controller of the GDD.
> + *
> + * Needs to be called holding the gdd controller lock
> + */
> +static unsigned int get_free_lch(struct ssi_dev *ssi_ctrl)
> +{
> +	unsigned int enable_reg;
> +	unsigned int lch = 0;
> +
> +	enable_reg = ssi_inl(ssi_ctrl->base, SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
> +	while ((lch < SSI_NUM_LCH) && (enable_reg & SSI_GDD_LCH(lch)))
> +		lch++;
> +
> +	return lch;
> +}
> +
> +/*
> + * ssi_driver_write_dma - Program GDD [DMA] to write data from memory to
> + * the ssi channel buffer.
> + * @ssi_channel - pointer to the ssi_channel to write data to.
> + * @data - 32-bit word pointer to the data.
> + * @size - Number of 32bit words to be transfered.
> + *
> + * ssi_controller lock must be hold before calling this function.
> + */
> +int ssi_driver_write_dma(struct ssi_channel *ssi_channel, u32 *data,
> +			 unsigned int size)
> +{
> +	struct ssi_dev *ssi_ctrl = ssi_channel->ssi_port->ssi_controller;
> +	void __iomem *base = ssi_ctrl->base;
> +	unsigned int port = ssi_channel->ssi_port->port_number;
> +	unsigned int channel = ssi_channel->channel_number;
> +	unsigned int sync;
> +	int lch;
> +	dma_addr_t dma_data;
> +	u32 s_addr;
> +	u16 tmp;
> +
> +	if ((size < 1) || (data == NULL))
> +		return -EINVAL;
> +
> +	clk_enable(ssi_ctrl->ssi_clk);
> +
> +	lch = get_free_lch(ssi_ctrl);
> +	if (lch >= SSI_NUM_LCH) {
> +		dev_err(&ssi_ctrl->pdev->dev, "No free GDD logical "
> +								"channels.\n");
> +		clk_disable(ssi_ctrl->ssi_clk);
> +		return -EBUSY;	/* No free GDD logical channels. */
> +	}
> +	/* NOTE: Gettting a free gdd logical channel and
> +	 * reserve it must be done atomicaly. */
> +	ssi_channel->write_data.lch = lch;
> +
> +	sync = get_sync(port, channel, SSI_SYNC_WRITE);
> +	dma_data = dma_map_single(NULL, data, size * 4, DMA_TO_DEVICE);
> +	dma_sync_single(NULL, dma_data, size * 4, DMA_TO_DEVICE);
> +
> +	tmp = SSI_SRC_SINGLE_ACCESS0 |
> +		SSI_SRC_MEMORY_PORT |
> +		SSI_DST_SINGLE_ACCESS0 |
> +		SSI_DST_PERIPHERAL_PORT |
> +		SSI_DATA_TYPE_S32;
> +	ssi_outw(tmp, base, SSI_GDD_CSDP_REG(lch));
> +	tmp = SSI_SRC_AMODE_POSTINC | SSI_DST_AMODE_CONST | sync;
> +	ssi_outw(tmp, base, SSI_GDD_CCR_REG(lch));
> +	ssi_outw((SSI_BLOCK_IE | SSI_TOUT_IE), base, SSI_GDD_CICR_REG(lch));
> +	s_addr = (u32)base + SSI_SST_BUFFER_CH_REG(port, channel);
> +	ssi_outl(s_addr, base, SSI_GDD_CDSA_REG(lch));
> +	ssi_outl(dma_data, base, SSI_GDD_CSSA_REG(lch));
> +	ssi_outw(size, base, SSI_GDD_CEN_REG(lch));
> +	ssi_outl_or(SSI_GDD_LCH(lch), base, SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
> +	ssi_outw_or(SSI_CCR_ENABLE, base, SSI_GDD_CCR_REG(lch));
> +
> +	return 0;
> +}
> +
> +/*
> + * ssi_driver_read_dma - Program GDD [DMA] to write data to memory from
> + * the ssi channel buffer.
> + * @ssi_channel - pointer to the ssi_channel to read data from.
> + * @data - 32-bit word pointer where to store the incoming data.
> + * @size - Number of 32bit words to be transfered to the buffer.
> + *
> + * ssi_controller lock must be hold before calling this function.
> + */
> +int ssi_driver_read_dma(struct ssi_channel *ssi_channel, u32 *data,
> +			unsigned int count)
> +{
> +	struct ssi_dev *ssi_ctrl = ssi_channel->ssi_port->ssi_controller;
> +	void __iomem *base = ssi_ctrl->base;
> +	unsigned int port = ssi_channel->ssi_port->port_number;
> +	unsigned int channel = ssi_channel->channel_number;
> +	unsigned int sync;
> +	unsigned int lch;
> +	dma_addr_t dma_data;
> +	u32 d_addr;
> +	u16 tmp;
> +
> +	clk_enable(ssi_ctrl->ssi_clk);
> +	lch = get_free_lch(ssi_ctrl);
> +	if (lch >= SSI_NUM_LCH) {
> +		dev_err(&ssi_ctrl->pdev->dev, "No free GDD logical "
> +								"channels.\n");
> +		clk_disable(ssi_ctrl->ssi_clk);
> +		return -EBUSY;	/* No free GDD logical channels. */
> +	}
> +	/*
> +	 * NOTE: Gettting a free gdd logical channel and
> +	 * reserve it must be done atomicaly.
> +	 */
> +	ssi_channel->read_data.lch = lch;
> +
> +	sync = get_sync(port, channel, SSI_SYNC_READ);
> +
> +	dma_data = dma_map_single(NULL, data, count * 4, DMA_FROM_DEVICE);
> +
> +	tmp = SSI_DST_SINGLE_ACCESS0 |
> +		SSI_DST_MEMORY_PORT |
> +		SSI_SRC_SINGLE_ACCESS0 |
> +		SSI_SRC_PERIPHERAL_PORT |
> +		SSI_DATA_TYPE_S32;
> +	ssi_outw(tmp, base, SSI_GDD_CSDP_REG(lch));
> +	tmp = SSI_DST_AMODE_POSTINC | SSI_SRC_AMODE_CONST | sync;
> +	ssi_outw(tmp, base, SSI_GDD_CCR_REG(lch));
> +	ssi_outw((SSI_BLOCK_IE | SSI_TOUT_IE), base, SSI_GDD_CICR_REG(lch));
> +	d_addr = (u32)base + SSI_SSR_BUFFER_CH_REG(port, channel);
> +	ssi_outl(d_addr, base, SSI_GDD_CSSA_REG(lch));
> +	ssi_outl((u32)dma_data, base, SSI_GDD_CDSA_REG(lch));
> +	ssi_outw(count, base, SSI_GDD_CEN_REG(lch));
> +
> +	ssi_outl_or(SSI_GDD_LCH(lch), base, SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
> +	ssi_outw_or(SSI_CCR_ENABLE, base, SSI_GDD_CCR_REG(lch));
> +
> +	return 0;
> +}
> +
> +void ssi_driver_cancel_write_dma(struct ssi_channel *ssi_ch)
> +{
> +	int lch = ssi_ch->write_data.lch;
> +	unsigned int port = ssi_ch->ssi_port->port_number;
> +	unsigned int channel = ssi_ch->channel_number;
> +	struct ssi_dev *ssi_ctrl = ssi_ch->ssi_port->ssi_controller;
> +	u32 ccr;
> +
> +	if (lch < 0)
> +		return;
> +
> +	clk_enable(ssi_ctrl->ssi_clk);
> +	ccr = ssi_inw(ssi_ctrl->base, SSI_GDD_CCR_REG(lch));
> +	if (!(ccr & SSI_CCR_ENABLE)) {
> +		dev_dbg(&ssi_ch->dev->device, LOG_NAME "Write cancel on not "
> +		"enabled logical channel %d CCR REG 0x%08X\n", lch, ccr);
> +		clk_disable(ssi_ctrl->ssi_clk);
> +		return;
> +	}
> +
> +	ssi_outw_and(~SSI_CCR_ENABLE, ssi_ctrl->base, SSI_GDD_CCR_REG(lch));
> +	ssi_outl_and(~SSI_GDD_LCH(lch), ssi_ctrl->base,
> +						SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
> +	ssi_outl(SSI_GDD_LCH(lch), ssi_ctrl->base,
> +						SSI_SYS_GDD_MPU_IRQ_STATUS_REG);
> +
> +	ssi_outl_and(~NOTFULL(channel), ssi_ctrl->base,
> +						SSI_SST_BUFSTATE_REG(port));
> +
> +
> +	ssi_ch->write_data.addr = NULL;
> +	ssi_ch->write_data.size = 0;
> +	ssi_ch->write_data.lch = -1;
> +	clk_disable(ssi_ctrl->ssi_clk);
> +	clk_disable(ssi_ctrl->ssi_clk);
> +}
> +
> +void ssi_driver_cancel_read_dma(struct ssi_channel *ssi_ch)
> +{
> +	int lch = ssi_ch->read_data.lch;
> +	struct ssi_dev *ssi_ctrl = ssi_ch->ssi_port->ssi_controller;
> +	unsigned int port = ssi_ch->ssi_port->port_number;
> +	unsigned int channel = ssi_ch->channel_number;
> +	u32 reg;
> +
> +	if (lch < 0)
> +		return;
> +
> +	clk_enable(ssi_ctrl->ssi_clk);
> +	reg = ssi_inw(ssi_ctrl->base, SSI_GDD_CCR_REG(lch));
> +	if (!(reg & SSI_CCR_ENABLE)) {
> +		dev_dbg(&ssi_ch->dev->device, LOG_NAME "Read cancel on not "
> +		"enable logical channel %d CCR REG 0x%08X\n", lch, reg);
> +		clk_disable(ssi_ctrl->ssi_clk);
> +		return;
> +	}
> +
> +	ssi_outw_and(~SSI_CCR_ENABLE, ssi_ctrl->base, SSI_GDD_CCR_REG(lch));
> +	ssi_outl_and(~SSI_GDD_LCH(lch), ssi_ctrl->base,
> +						SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
> +	ssi_outl(SSI_GDD_LCH(lch), ssi_ctrl->base,
> +						SSI_SYS_GDD_MPU_IRQ_STATUS_REG);
> +
> +	ssi_outl_and(~NOTEMPTY(channel), ssi_ctrl->base,
> +						SSI_SSR_BUFSTATE_REG(port));
> +
> +	ssi_ch->read_data.addr = NULL;
> +	ssi_ch->read_data.size = 0;
> +	ssi_ch->read_data.lch = -1;
> +	clk_disable(ssi_ctrl->ssi_clk);
> +	clk_disable(ssi_ctrl->ssi_clk);
> +}
> +
> +static void dma_read_cb(struct ssi_dev *ctrl, unsigned int port,
> +							unsigned int channel)
> +{
> +	struct ssi_channel *ch = &ctrl->ssi_port[port - 1].ssi_channel[channel];
> +
> +	ch->ops.read_done(ch->dev);
> +}
> +
> +static void dma_write_cb(struct ssi_dev *ctrl, unsigned int port,
> +							unsigned int channel)
> +{
> +	struct ssi_channel *ch = &ctrl->ssi_port[port - 1].ssi_channel[channel];
> +
> +	ch->ops.write_done(ch->dev);
> +}
> +
> +static void do_gdd_lch(struct ssi_dev *ssi_ctrl, unsigned int gdd_lch)
> +{
> +	void __iomem *base = ssi_ctrl->base;
> +	unsigned int port;
> +	unsigned int channel;
> +	u32 sync;
> +	u32 gdd_csr;
> +	dma_addr_t dma_h;
> +	size_t size;
> +
> +	sync = ssi_inw(base, SSI_GDD_CCR_REG(gdd_lch)) & SSI_CCR_SYNC_MASK;
> +	port = get_sync_port(sync);
> +	channel = get_sync_channel(sync);
> +
> +	spin_lock(&ssi_ctrl->lock);
> +
> +	ssi_outl_and(~SSI_GDD_LCH(gdd_lch), base,
> +						SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
> +	gdd_csr = ssi_inw(base, SSI_GDD_CSR_REG(gdd_lch));
> +
> +	if (!(gdd_csr & SSI_CSR_TOUR)) {
> +		if (get_sync_type(sync) == SSI_SYNC_READ) {
> +			dma_h = ssi_inl(base, SSI_GDD_CDSA_REG(gdd_lch));
> +			size = ssi_inw(base, SSI_GDD_CEN_REG(gdd_lch)) * 4;
> +			dma_sync_single(NULL, dma_h, size, DMA_FROM_DEVICE);
> +			rst_ch_read(ssi_ctrl, port, channel);
> +			spin_unlock(&ssi_ctrl->lock);
> +			dma_read_cb(ssi_ctrl, port, channel);
> +		} else {
> +			rst_ch_write(ssi_ctrl, port, channel);
> +			spin_unlock(&ssi_ctrl->lock);
> +			dma_write_cb(ssi_ctrl, port, channel);
> +		}
> +	} else {
> +		dev_err(&ssi_ctrl->pdev->dev, "Error  on GDD transfer "
> +				"on gdd channel %d port %d channel %d\n",
> +						gdd_lch, port, channel);
> +		spin_unlock(&ssi_ctrl->lock);
> +		ssi_port_event_handler(&ssi_ctrl->ssi_port[port - 1],
> +							SSI_EVENT_ERROR, NULL);
> +	}
> +}
> +
> +void do_ssi_gdd_tasklet(unsigned long device)
> +{
> +	struct ssi_dev *ssi_ctrl = (struct ssi_dev *)device;
> +	void __iomem *base = ssi_ctrl->base;
> +	unsigned int gdd_lch = 0;
> +	u32 status_reg = 0;
> +	u32 lch_served = 0;
> +
> +	clk_enable(ssi_ctrl->ssi_clk);
> +
> +	status_reg = ssi_inl(base, SSI_SYS_GDD_MPU_IRQ_STATUS_REG);
> +
> +	for (gdd_lch = 0; gdd_lch < SSI_NUM_LCH; gdd_lch++) {
> +		if (status_reg & SSI_GDD_LCH(gdd_lch)) {
> +			do_gdd_lch(ssi_ctrl, gdd_lch);
> +			lch_served |= SSI_GDD_LCH(gdd_lch);
> +			clk_disable(ssi_ctrl->ssi_clk);
> +		}
> +	}
> +
> +	ssi_outl(lch_served, base, SSI_SYS_GDD_MPU_IRQ_STATUS_REG);
> +	clk_disable(ssi_ctrl->ssi_clk);
> +
> +	enable_irq(ssi_ctrl->gdd_irq);
> +}
> +
> +irqreturn_t ssi_gdd_mpu_handler(int irq, void *ssi_controller)
> +{
> +	struct ssi_dev *ssi_ctrl = (struct ssi_dev *)ssi_controller;
> +
> +	tasklet_hi_schedule(&ssi_ctrl->ssi_gdd_tasklet);
> +	disable_irq_nosync(ssi_ctrl->gdd_irq);
> +
> +	return IRQ_HANDLED;
> +}
> diff --git a/drivers/misc/ssi/ssi_driver_if.c b/drivers/misc/ssi/ssi_driver_if.c
> new file mode 100644
> index 0000000..385467e
> --- /dev/null
> +++ b/drivers/misc/ssi/ssi_driver_if.c
> @@ -0,0 +1,335 @@
> +/*
> + * ssi_driver_if.c
> + *
> + * Implements SSI hardware driver interfaces for the upper layers.
> + *
> + * Copyright (C) 2007-2008 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 "ssi_driver.h"
> +
> +/**
> + * ssi_open - open a ssi device channel.
> + * @dev - Reference to the ssi device channel to be openned.
> + *
> + * Returns 0 on success, -EINVAL on bad parameters, -EBUSY if is already opened.
> + */
> +int ssi_open(struct ssi_device *dev)
> +{
> +	struct ssi_channel *ch;
> +	struct ssi_port *port;
> +	struct ssi_dev *ssi_ctrl;
> +
> +	if (!dev || !dev->ch) {
> +		pr_err(LOG_NAME "Wrong SSI device %p\n", dev);
> +		return -EINVAL;
> +	}
> +
> +	ch = dev->ch;
> +	if (!ch->ops.read_done || !ch->ops.write_done) {
> +		dev_err(&dev->device, "Trying to open with no callbacks "
> +								"registered\n");
> +		return -EINVAL;
> +	}
> +	port = ch->ssi_port;
> +	ssi_ctrl = port->ssi_controller;
> +	spin_lock_bh(&ssi_ctrl->lock);
> +	if (ch->flags & SSI_CH_OPEN) {
> +		dev_err(&dev->device, "Port %d Channel %d already OPENED\n",
> +							dev->n_p, dev->n_ch);
> +		spin_unlock_bh(&ssi_ctrl->lock);
> +		return -EBUSY;
> +	}
> +	clk_enable(ssi_ctrl->ssi_clk);
> +	ch->flags |= SSI_CH_OPEN;
> +	ssi_outl_or(SSI_ERROROCCURED | SSI_BREAKDETECTED, ssi_ctrl->base,
> +		SSI_SYS_MPU_ENABLE_REG(port->port_number, port->n_irq));
> +	clk_disable(ssi_ctrl->ssi_clk);
> +	spin_unlock_bh(&ssi_ctrl->lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(ssi_open);
> +
> +/**
> + * ssi_write - write data into the ssi device channel
> + * @dev - reference to the ssi device channel  to write into.
> + * @data - pointer to a 32-bit word data to be written.
> + * @count - number of 32-bit word to be written.
> + *
> + * Return 0 on sucess, a negative value on failure.
> + * A success values only indicates that the request has been accepted.
> + * Transfer is only completed when the write_done callback is called.
> + *
> + */
> +int ssi_write(struct ssi_device *dev, u32 *data, unsigned int count)
> +{
> +	struct ssi_channel *ch;
> +	int err;
> +
> +	if (unlikely(!dev || !dev->ch || !data || (count <= 0))) {
> +		dev_err(&dev->device, "Wrong paramenters "
> +			"ssi_device %p data %p count %d", dev, data, count);
> +		return -EINVAL;
> +	}
> +	if (unlikely(!(dev->ch->flags & SSI_CH_OPEN))) {
> +		dev_err(&dev->device, "SSI device NOT open\n");
> +		return -EINVAL;
> +	}
> +
> +	ch = dev->ch;
> +	spin_lock_bh(&ch->ssi_port->ssi_controller->lock);
> +	ch->write_data.addr = data;
> +	ch->write_data.size = count;
> +
> +	if (count == 1)
> +		err = ssi_driver_write_interrupt(ch, data);
> +	else
> +		err = ssi_driver_write_dma(ch, data, count);
> +
> +	if (unlikely(err < 0)) {
> +		ch->write_data.addr = NULL;
> +		ch->write_data.size = 0;
> +	}
> +	spin_unlock_bh(&ch->ssi_port->ssi_controller->lock);
> +
> +	return err;
> +
> +}
> +EXPORT_SYMBOL(ssi_write);
> +
> +/**
> + * ssi_read - read data from the ssi device channel
> + * @dev - ssi device channel reference to read data from.
> + * @data - pointer to a 32-bit word data to store the data.
> + * @count - number of 32-bit word to be stored.
> + *
> + * Return 0 on sucess, a negative value on failure.
> + * A success values only indicates that the request has been accepted.
> + * Data is only available in the buffer when the read_done callback is called.
> + *
> + */
> +int ssi_read(struct ssi_device *dev, u32 *data, unsigned int count)
> +{
> +	struct ssi_channel *ch;
> +	int err;
> +
> +	if (unlikely(!dev || !dev->ch || !data || (count <= 0))) {
> +		dev_err(&dev->device, "Wrong paramenters "
> +			"ssi_device %p data %p count %d", dev, data, count);
> +		return -EINVAL;
> +	}
> +	if (unlikely(!(dev->ch->flags & SSI_CH_OPEN))) {
> +		dev_err(&dev->device, "SSI device NOT open\n");
> +		return -EINVAL;
> +	}
> +
> +	ch = dev->ch;
> +	spin_lock_bh(&ch->ssi_port->ssi_controller->lock);
> +	ch->read_data.addr = data;
> +	ch->read_data.size = count;
> +
> +	if (count == 1)
> +		err = ssi_driver_read_interrupt(ch, data);
> +	else
> +		err = ssi_driver_read_dma(ch, data, count);
> +
> +	if (unlikely(err < 0)) {
> +		ch->read_data.addr = NULL;
> +		ch->read_data.size = 0;
> +	}
> +	spin_unlock_bh(&ch->ssi_port->ssi_controller->lock);
> +
> +	return err;
> +}
> +EXPORT_SYMBOL(ssi_read);
> +
> +void __ssi_write_cancel(struct ssi_channel *ch)
> +{
> +	if (ch->write_data.size == 1)
> +		ssi_driver_cancel_write_interrupt(ch);
> +	else if (ch->write_data.size > 1)
> +		ssi_driver_cancel_write_dma(ch);
> +
> +}
> +/**
> + * ssi_write_cancel - Cancel pending write request.
> + * @dev - ssi device channel where to cancel the pending write.
> + *
> + * write_done() callback will not be called after sucess of this function.
> + */
> +void ssi_write_cancel(struct ssi_device *dev)
> +{
> +	if (unlikely(!dev || !dev->ch)) {
> +		pr_err(LOG_NAME "Wrong SSI device %p\n", dev);
> +		return;
> +	}
> +	if (unlikely(!(dev->ch->flags & SSI_CH_OPEN))) {
> +		dev_err(&dev->device, "SSI device NOT open\n");
> +		return;
> +	}
> +
> +	spin_lock_bh(&dev->ch->ssi_port->ssi_controller->lock);
> +	__ssi_write_cancel(dev->ch);
> +	spin_unlock_bh(&dev->ch->ssi_port->ssi_controller->lock);
> +}
> +EXPORT_SYMBOL(ssi_write_cancel);
> +
> +void __ssi_read_cancel(struct ssi_channel *ch)
> +{
> +	if (ch->read_data.size == 1)
> +		ssi_driver_cancel_read_interrupt(ch);
> +	else if (ch->read_data.size > 1)
> +		ssi_driver_cancel_read_dma(ch);
> +}
> +
> +/**
> + * ssi_read_cancel - Cancel pending read request.
> + * @dev - ssi device channel where to cancel the pending read.
> + *
> + * read_done() callback will not be called after sucess of this function.
> + */
> +void ssi_read_cancel(struct ssi_device *dev)
> +{
> +	if (unlikely(!dev || !dev->ch)) {
> +		pr_err(LOG_NAME "Wrong SSI device %p\n", dev);
> +		return;
> +	}
> +
> +	if (unlikely(!(dev->ch->flags & SSI_CH_OPEN))) {
> +		dev_err(&dev->device, "SSI device NOT open\n");
> +		return;
> +	}
> +
> +	spin_lock_bh(&dev->ch->ssi_port->ssi_controller->lock);
> +	__ssi_read_cancel(dev->ch);
> +	spin_unlock_bh(&dev->ch->ssi_port->ssi_controller->lock);
> +
> +}
> +EXPORT_SYMBOL(ssi_read_cancel);
> +
> +/**
> + * ssi_ioctl - SSI I/O control
> + * @dev - ssi device channel reference to apply the I/O control
> + * 						(or port associated to it)
> + * @command - SSI I/O control command
> + * @arg - parameter associated to the control command. NULL, if no parameter.
> + *
> + * Return 0 on sucess, a negative value on failure.
> + *
> + */
> +int ssi_ioctl(struct ssi_device *dev, unsigned int command, void *arg)
> +{
> +	struct ssi_channel *ch;
> +	struct ssi_dev *ssi_ctrl;
> +	void __iomem *base;
> +	unsigned int port, channel;
> +	u32 wake;
> +	int err = 0;
> +
> +	if (unlikely((!dev) ||
> +		(!dev->ch) ||
> +		(!dev->ch->ssi_port) ||
> +		(!dev->ch->ssi_port->ssi_controller)) ||
> +		(!(dev->ch->flags & SSI_CH_OPEN))) {
> +		pr_err(LOG_NAME "SSI IOCTL Invalid parameter\n");
> +		return -EINVAL;
> +	}
> +
> +
> +	ch = dev->ch;
> +	ssi_ctrl = ch->ssi_port->ssi_controller;
> +	port = ch->ssi_port->port_number;
> +	channel = ch->channel_number;
> +	base = ssi_ctrl->base;
> +	clk_enable(ssi_ctrl->ssi_clk);
> +
> +	switch (command) {
> +	case SSI_IOCTL_WAKE_UP:
> +		/* We only claim once the wake line per channel */
> +		wake = ssi_inl(base, SSI_SYS_WAKE_REG(port));
> +		if (!(wake & SSI_WAKE(channel))) {
> +			clk_enable(ssi_ctrl->ssi_clk);
> +			ssi_outl(SSI_WAKE(channel), base,
> +					SSI_SYS_SET_WAKE_REG(port));
> +		}
> +		break;
> +	case SSI_IOCTL_WAKE_DOWN:
> +		wake = ssi_inl(base, SSI_SYS_WAKE_REG(port));
> +		if ((wake & SSI_WAKE(channel))) {
> +			ssi_outl(SSI_WAKE(channel), base,
> +						SSI_SYS_CLEAR_WAKE_REG(port));
> +			clk_disable(ssi_ctrl->ssi_clk);
> +		}
> +		break;
> +	case SSI_IOCTL_SEND_BREAK:
> +		ssi_outl(1, base, SSI_SST_BREAK_REG(port));
> +		break;
> +	case SSI_IOCTL_WAKE:
> +		if (arg == NULL)
> +			err = -EINVAL;
> +		else
> +			*(u32 *)arg = ssi_inl(base, SSI_SYS_WAKE_REG(port));
> +		break;
> +	default:
> +		err = -ENOIOCTLCMD;
> +		break;
> +	}
> +
> +	clk_disable(ssi_ctrl->ssi_clk);
> +
> +	return err;
> +}
> +EXPORT_SYMBOL(ssi_ioctl);
> +
> +/**
> + * ssi_close - close given ssi device channel
> + * @dev - reference to ssi device channel.
> + */
> +void ssi_close(struct ssi_device *dev)
> +{
> +	if (!dev || !dev->ch) {
> +		pr_err(LOG_NAME "Trying to close wrong SSI device %p\n", dev);
> +		return;
> +	}
> +
> +	spin_lock_bh(&dev->ch->ssi_port->ssi_controller->lock);
> +	if (dev->ch->flags & SSI_CH_OPEN) {
> +		dev->ch->flags &= ~SSI_CH_OPEN;
> +		__ssi_write_cancel(dev->ch);
> +		__ssi_read_cancel(dev->ch);
> +	}
> +	spin_unlock_bh(&dev->ch->ssi_port->ssi_controller->lock);
> +
> +}
> +EXPORT_SYMBOL(ssi_close);
> +
> +/**
> + * ssi_dev_set_cb - register read_done() and write_done() callbacks.
> + * @dev - reference to ssi device channel where callbacks are associated.
> + * @r_cb - callback to signal read transfer completed.
> + * @w_cb - callback to signal write transfer completed.
> + */
> +void ssi_dev_set_cb(struct ssi_device *dev, void (*r_cb)(struct ssi_device *dev)
> +					, void (*w_cb)(struct ssi_device *dev))
> +{
> +	dev->ch->ops.read_done = r_cb;
> +	dev->ch->ops.write_done = w_cb;
> +}
> +EXPORT_SYMBOL(ssi_dev_set_cb);
> diff --git a/drivers/misc/ssi/ssi_driver_int.c b/drivers/misc/ssi/ssi_driver_int.c
> new file mode 100644
> index 0000000..6491e48
> --- /dev/null
> +++ b/drivers/misc/ssi/ssi_driver_int.c
> @@ -0,0 +1,232 @@
> +/*
> + * ssi_driver_int.c
> + *
> + * Implements SSI interrupt functionality.
> + *
> + * Copyright (C) 2007-2008 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 "ssi_driver.h"
> +
> +static void reset_ch_read(struct ssi_channel *ch)
> +{
> +	ch->read_data.addr = NULL;
> +	ch->read_data.size = 0;
> +	ch->read_data.lch = -1;
> +}
> +
> +static void reset_ch_write(struct ssi_channel *ch)
> +{
> +	ch->write_data.addr = NULL;
> +	ch->write_data.size = 0;
> +	ch->write_data.lch = -1;
> +}
> +
> +int ssi_driver_write_interrupt(struct ssi_channel *ch, u32 *data)
> +{
> +	struct ssi_port *p = ch->ssi_port;
> +	unsigned int port = p->port_number;
> +	unsigned int channel = ch->channel_number;
> +
> +	clk_enable(p->ssi_controller->ssi_clk);
> +	ssi_outl_or(SSI_SST_DATAACCEPT(channel), p->ssi_controller->base,
> +					SSI_SYS_MPU_ENABLE_REG(port, p->n_irq));
> +
> +
> +	return 0;
> +}
> +
> +int ssi_driver_read_interrupt(struct ssi_channel *ch, u32 *data)
> +{
> +	struct ssi_port *p = ch->ssi_port;
> +	unsigned int port = p->port_number;
> +	unsigned int channel = ch->channel_number;
> +
> +	clk_enable(p->ssi_controller->ssi_clk);
> +
> +	ssi_outl_or(SSI_SSR_DATAAVAILABLE(channel), p->ssi_controller->base,
> +					SSI_SYS_MPU_ENABLE_REG(port, p->n_irq));
> +
> +	clk_disable(p->ssi_controller->ssi_clk);
> +
> +	return 0;
> +}
> +
> +void ssi_driver_cancel_write_interrupt(struct ssi_channel *ch)
> +{
> +	struct ssi_port *p = ch->ssi_port;
> +	unsigned int port = p->port_number;
> +	unsigned int channel = ch->channel_number;
> +	void __iomem *base = p->ssi_controller->base;
> +	u32 enable;
> +
> +	clk_enable(p->ssi_controller->ssi_clk);
> +
> +	enable = ssi_inl(base, SSI_SYS_MPU_ENABLE_REG(port, p->n_irq));
> +	if (!(enable & SSI_SST_DATAACCEPT(channel))) {
> +		dev_dbg(&ch->dev->device, LOG_NAME "Write cancel on not "
> +		"enabled channel %d ENABLE REG 0x%08X", channel, enable);
> +		clk_disable(p->ssi_controller->ssi_clk);
> +		return;
> +	}
> +	ssi_outl_and(~SSI_SST_DATAACCEPT(channel), base,
> +				SSI_SYS_MPU_ENABLE_REG(port, p->n_irq));
> +	ssi_outl_and(~NOTFULL(channel), base, SSI_SST_BUFSTATE_REG(port));
> +	reset_ch_write(ch);
> +
> +	clk_disable(p->ssi_controller->ssi_clk);
> +	clk_disable(p->ssi_controller->ssi_clk);
> +
> +}
> +
> +void ssi_driver_cancel_read_interrupt(struct ssi_channel *ch)
> +{
> +	struct ssi_port *p = ch->ssi_port;
> +	unsigned int port = p->port_number;
> +	unsigned int channel = ch->channel_number;
> +	void __iomem *base = p->ssi_controller->base;
> +
> +	clk_enable(p->ssi_controller->ssi_clk);
> +
> +	ssi_outl_and(~SSI_SSR_DATAAVAILABLE(channel), base,
> +					SSI_SYS_MPU_ENABLE_REG(port, p->n_irq));
> +	ssi_outl_and(~NOTEMPTY(channel), base, SSI_SSR_BUFSTATE_REG(port));
> +	reset_ch_read(ch);
> +
> +	clk_disable(p->ssi_controller->ssi_clk);
> +}
> +
> +static void do_channel_tx(struct ssi_channel *ch)
> +{
> +	struct ssi_dev *ssi_ctrl = ch->ssi_port->ssi_controller;
> +	void __iomem *base = ssi_ctrl->base;
> +	unsigned int n_ch;
> +	unsigned int n_p;
> +	unsigned int irq;
> +
> +	n_ch = ch->channel_number;
> +	n_p = ch->ssi_port->port_number;
> +	irq = ch->ssi_port->n_irq;
> +
> +	spin_lock(&ssi_ctrl->lock);
> +
> +	if (ch->write_data.addr == NULL) {
> +		ssi_outl_and(~SSI_SST_DATAACCEPT(n_ch), base,
> +					SSI_SYS_MPU_ENABLE_REG(n_p, irq));
> +		reset_ch_write(ch);
> +		spin_unlock(&ssi_ctrl->lock);
> +		clk_disable(ssi_ctrl->ssi_clk);
> +		(*ch->ops.write_done)(ch->dev);
> +	} else {
> +		ssi_outl(*(ch->write_data.addr), base,
> +					SSI_SST_BUFFER_CH_REG(n_p, n_ch));
> +		ch->write_data.addr = NULL;
> +		spin_unlock(&ssi_ctrl->lock);
> +	}
> +}
> +
> +static void do_channel_rx(struct ssi_channel *ch)
> +{
> +	struct ssi_dev *ssi_ctrl = ch->ssi_port->ssi_controller;
> +	void __iomem *base = ch->ssi_port->ssi_controller->base;
> +	unsigned int n_ch;
> +	unsigned int n_p;
> +	unsigned int irq;
> +
> +	n_ch = ch->channel_number;
> +	n_p = ch->ssi_port->port_number;
> +	irq = ch->ssi_port->n_irq;
> +
> +	spin_lock(&ssi_ctrl->lock);
> +
> +	*(ch->read_data.addr) = ssi_inl(base, SSI_SSR_BUFFER_CH_REG(n_p, n_ch));
> +
> +	ssi_outl_and(~SSI_SSR_DATAAVAILABLE(n_ch), base,
> +					SSI_SYS_MPU_ENABLE_REG(n_p, irq));
> +	reset_ch_read(ch);
> +
> +	spin_unlock(&ssi_ctrl->lock);
> +
> +	(*ch->ops.read_done)(ch->dev);
> +}
> +
> +void do_ssi_tasklet(unsigned long ssi_port)
> +{
> +	struct ssi_port *pport = (struct ssi_port *)ssi_port;
> +	struct ssi_dev *ssi_ctrl = pport->ssi_controller;
> +	void __iomem *base = ssi_ctrl->base;
> +	unsigned int port = pport->port_number;
> +	unsigned int channel = 0;
> +	unsigned int irq = pport->n_irq;
> +	u32 status_reg;
> +	u32 enable_reg;
> +	u32 ssr_err_reg;
> +	u32 channels_served;
> +
> +	clk_enable(ssi_ctrl->ssi_clk);
> +
> +	channels_served = 0;
> +	status_reg = ssi_inl(base, SSI_SYS_MPU_STATUS_REG(port, irq));
> +	enable_reg = ssi_inl(base, SSI_SYS_MPU_ENABLE_REG(port, irq));
> +
> +	for (channel = 0; channel < pport->max_ch; channel++) {
> +		if ((status_reg & SSI_SST_DATAACCEPT(channel)) &&
> +		    (enable_reg & SSI_SST_DATAACCEPT(channel))) {
> +			do_channel_tx(&pport->ssi_channel[channel]);
> +			channels_served |= SSI_SST_DATAACCEPT(channel);
> +		}
> +
> +		if ((status_reg & SSI_SSR_DATAAVAILABLE(channel)) &&
> +		    (enable_reg & SSI_SSR_DATAAVAILABLE(channel))) {
> +			do_channel_rx(&pport->ssi_channel[channel]);
> +			channels_served |= SSI_SSR_DATAAVAILABLE(channel);
> +		}
> +	}
> +
> +	if ((status_reg & SSI_BREAKDETECTED) &&
> +	    (enable_reg & SSI_BREAKDETECTED)) {
> +		dev_info(&ssi_ctrl->pdev->dev,
> +					"Hardware BREAK on port %d\n", port);
> +		ssi_outl(0, base, SSI_SSR_BREAK_REG(port));
> +		ssi_port_event_handler(pport, SSI_EVENT_BREAK_DETECTED, NULL);
> +	}
> +
> +	if (status_reg & SSI_ERROROCCURED) {
> +		ssr_err_reg = ssi_inl(base, SSI_SSR_ERROR_REG(port));
> +		dev_err(&ssi_ctrl->pdev->dev, "SSI ERROR Port %d: 0x%02x\n",
> +							port, ssr_err_reg);
> +		ssi_outl(ssr_err_reg, base, SSI_SSR_ERRORACK_REG(port));
> +		ssi_port_event_handler(pport, SSI_EVENT_ERROR, NULL);
> +	}
> +
> +	ssi_outl((channels_served | SSI_ERROROCCURED | SSI_BREAKDETECTED), base,
> +					SSI_SYS_MPU_STATUS_REG(port, irq));
> +
> +	clk_disable(ssi_ctrl->ssi_clk);
> +	enable_irq(pport->irq);
> +}
> +
> +irqreturn_t ssi_mpu_handler(int irq, void *ssi_port)
> +{
> +	struct ssi_port *p = (struct ssi_port *)ssi_port;

unnecessary cast, remove.

> +
> +	tasklet_hi_schedule(&p->ssi_tasklet);
> +	disable_irq_nosync(p->irq);
> +
> +	return IRQ_HANDLED;
> +}

this patch is really huge, I'm not finished with it. Still a lot to
review.

-- 
balbi

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

* Re: [RFC][PATCH 4/5] OMAP SSI integration into misc drivers
  2008-10-03 11:52       ` [RFC][PATCH 4/5] OMAP SSI integration into misc drivers Carlos Chinea
  2008-10-03 11:52         ` [RFC][PATCH 5/5] OMAP SSI API documentation Carlos Chinea
@ 2008-10-07  0:08         ` Felipe Balbi
  1 sibling, 0 replies; 14+ messages in thread
From: Felipe Balbi @ 2008-10-07  0:08 UTC (permalink / raw)
  To: Carlos Chinea; +Cc: linux-kernel, linux-omap

On Fri, Oct 03, 2008 at 02:52:29PM +0300, Carlos Chinea wrote:
> 
> Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
> ---
>  drivers/misc/Kconfig  |    2 ++
>  drivers/misc/Makefile |    1 +
>  2 files changed, 3 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index f0202ee..b09dc68 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -488,4 +488,6 @@ config SGI_GRU_DEBUG
>  	This option enables addition debugging code for the SGI GRU driver. If
>  	you are unsure, say N.
>  
> +source "drivers/misc/ssi/Kconfig"
> +
>  endif # MISC_DEVICES
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index b6167e7..39e153d 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -31,3 +31,4 @@ obj-$(CONFIG_KGDB_TESTS)	+= kgdbts.o
>  obj-$(CONFIG_SGI_XP)		+= sgi-xp/
>  obj-$(CONFIG_SGI_GRU)		+= sgi-gru/
>  obj-$(CONFIG_HP_ILO)		+= hpilo.o
> +obj-$(CONFIG_OMAP_SSI)		+= ssi/

I believe the whole ssi should sit under drivers/ssi

-- 
balbi

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

* Re: [RFC][PATCH 2/5] OMAP SSI driver interface
  2008-10-06 23:29     ` [RFC][PATCH 2/5] OMAP SSI driver interface Felipe Balbi
@ 2008-10-07  1:03       ` David Brownell
  2008-10-07 11:56         ` Woodruff, Richard
  0 siblings, 1 reply; 14+ messages in thread
From: David Brownell @ 2008-10-07  1:03 UTC (permalink / raw)
  To: me; +Cc: Carlos Chinea, linux-kernel, linux-omap

On Monday 06 October 2008, Felipe Balbi wrote:
> looks much better and much easier to type: #include <linux/ssi.h>

Does this work on non-OMAP kernels?  If not, <mach/ssi.h> or
similar would seem more sensible ...

Agreed about there being way too many headers, too.

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

* RE: [RFC][PATCH 2/5] OMAP SSI driver interface
  2008-10-07  1:03       ` David Brownell
@ 2008-10-07 11:56         ` Woodruff, Richard
  0 siblings, 0 replies; 14+ messages in thread
From: Woodruff, Richard @ 2008-10-07 11:56 UTC (permalink / raw)
  To: David Brownell, me; +Cc: Carlos Chinea, linux-kernel, linux-omap


> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-
> owner@vger.kernel.org] On Behalf Of David Brownell
> On Monday 06 October 2008, Felipe Balbi wrote:
> > looks much better and much easier to type: #include <linux/ssi.h>
>
> Does this work on non-OMAP kernels?  If not, <mach/ssi.h> or
> similar would seem more sensible ...

Seems like some cousin might be multi vendor via MIPI HSI. http://www.mipi.org/wgoverview.shtml


Regards,
Richard W.


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

* Re: [RFC][PATCH 3/5] OMAP SSI driver code
  2008-10-07  0:03       ` [RFC][PATCH 3/5] OMAP SSI driver code Felipe Balbi
@ 2008-10-07 22:01         ` Felipe Balbi
  0 siblings, 0 replies; 14+ messages in thread
From: Felipe Balbi @ 2008-10-07 22:01 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Carlos Chinea, linux-kernel, linux-omap

continuing with this patch


On Tue, Oct 07, 2008 at 03:03:09AM +0300, Felipe Balbi wrote:
> On Fri, Oct 03, 2008 at 02:52:28PM +0300, Carlos Chinea wrote:
> 
> Description. This seems to repeat in all your patches, take a look at
> linux kernel patch format: http://linux.yyz.us/patch-format.html
> 
> > Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
> > ---
> >  drivers/misc/ssi/Kconfig          |   11 +
> >  drivers/misc/ssi/Makefile         |    7 +
> >  drivers/misc/ssi/ssi_driver.c     |  513 +++++++++++++++++++++++++++++++++++++
> >  drivers/misc/ssi/ssi_driver.h     |  211 +++++++++++++++
> >  drivers/misc/ssi/ssi_driver_bus.c |  192 ++++++++++++++
> >  drivers/misc/ssi/ssi_driver_dma.c |  406 +++++++++++++++++++++++++++++
> >  drivers/misc/ssi/ssi_driver_if.c  |  335 ++++++++++++++++++++++++
> >  drivers/misc/ssi/ssi_driver_int.c |  232 +++++++++++++++++
> >  8 files changed, 1907 insertions(+), 0 deletions(-)
> >  create mode 100644 drivers/misc/ssi/Kconfig
> >  create mode 100644 drivers/misc/ssi/Makefile
> >  create mode 100644 drivers/misc/ssi/ssi_driver.c
> >  create mode 100644 drivers/misc/ssi/ssi_driver.h
> >  create mode 100644 drivers/misc/ssi/ssi_driver_bus.c
> >  create mode 100644 drivers/misc/ssi/ssi_driver_dma.c
> >  create mode 100644 drivers/misc/ssi/ssi_driver_if.c
> >  create mode 100644 drivers/misc/ssi/ssi_driver_int.c
> > 
> > diff --git a/drivers/misc/ssi/Kconfig b/drivers/misc/ssi/Kconfig
> > new file mode 100644
> > index 0000000..5e0842c
> > --- /dev/null
> > +++ b/drivers/misc/ssi/Kconfig
> > @@ -0,0 +1,11 @@
> > +#
> > +# OMAP SSI HW kernel configuration
> > +#
> > +config OMAP_SSI
> > +	tristate "OMAP SSI hardware driver"
> > +	depends on ARCH_OMAP
> > +	default n
> > +	---help---
> > +	  If you say Y here, you will enable the OMAP SSI hardware driver.
> > +
> > +	  If unsure, say N.
> > diff --git a/drivers/misc/ssi/Makefile b/drivers/misc/ssi/Makefile
> > new file mode 100644
> > index 0000000..2b74e02
> > --- /dev/null
> > +++ b/drivers/misc/ssi/Makefile
> > @@ -0,0 +1,7 @@
> > +#
> > +# Makefile for SSI drivers
> > +#
> > +omap_ssi-objs := 	ssi_driver.o ssi_driver_dma.o ssi_driver_int.o \
> 		   ^ trailing whitespace
> 
> > +			ssi_driver_if.o ssi_driver_bus.o
> > +
> > +obj-$(CONFIG_OMAP_SSI)	+= omap_ssi.o
> > diff --git a/drivers/misc/ssi/ssi_driver.c b/drivers/misc/ssi/ssi_driver.c
> > new file mode 100644
> > index 0000000..292e868
> > --- /dev/null
> > +++ b/drivers/misc/ssi/ssi_driver.c
> > @@ -0,0 +1,513 @@
> > +/*
> > + * ssi_driver.c
> > + *
> > + * Implements SSI module interface, initialization, and PM related functions.
> > + *
> > + * Copyright (C) 2007-2008 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/err.h>
> > +#include "ssi_driver.h"
> > +
> > +static void ssi_dev_release(struct device *dev)
> > +{
> > +}
> > +
> > +static void __init ssi_undo_reg_dev(struct platform_device *pd,
> > +								int p, int ch)
> 
> Take a few tabs out of the second line, it'll look better.
> 
> > +{
> > +	struct ssi_platform_data *p_data =
> 
> 	normally we call it pdata, but it's your call in this case
> 
> > +		(struct ssi_platform_data *) pd->dev.platform_data;
> 
> 		unnecessary cast. Remove
> 
> > +	struct ssi_port *ssi_p;
> > +	int port;
> > +	int channel;
> > +
> > +	for (port = p; port >= 0; port--) {
> > +		ssi_p = &p_data->ssi_ctrl->ssi_port[port];
> > +		for (channel = ch; channel >= 0 ; channel--)
> > +			device_unregister(&ssi_p->ssi_channel[channel].dev
> > +								->device);
> 
> This function should be unnecessary. pdata usage is wrong.
> 
> > +	}
> > +}
> > +
> > +static int __init reg_ssi_dev_ch(struct platform_device *pd,
> > +						unsigned int p, unsigned int ch)
> 
> the following should be probe().
> 
> > +{
> > +	struct ssi_device *dev;
> > +	struct ssi_platform_data *p_data =
> > +		(struct ssi_platform_data *) pd->dev.platform_data;
> 
> unnecessary cast, remove.
> 
> > +
> > +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> > +	if (!dev)
> > +		return -ENOMEM;
> > +	dev->n_ctrl = pd->id;
> > +	dev->n_p = p;
> > +	dev->n_ch = ch;
> > +	dev->ch = &p_data->ssi_ctrl->ssi_port[p].ssi_channel[ch];
> > +	p_data->ssi_ctrl->ssi_port[p].ssi_channel[ch].dev = dev;
> 
> pdata usage is wrong. It should be used to initialize a few fields in
> the device structure and then vanish.
> 
> > +	dev->device.bus = &ssi_bus_type;
> > +	dev->device.parent = &pd->dev;
> > +	dev->device.release = ssi_dev_release;
> > +	if (dev->n_ctrl < 0)
> > +		snprintf(dev->device.bus_id, sizeof(dev->device.bus_id),
> > +			"omap_ssi-p%u.c%u", p, ch);
> > +	else
> > +		snprintf(dev->device.bus_id, sizeof(dev->device.bus_id),
> > +			"omap_ssi%d-p%u.c%u", dev->n_ctrl, p, ch);
> > +
> > +	return device_register(&dev->device);
> > +}
> > +
> > +static int __init register_ssi_devices(struct platform_device *pd)
> > +{
> > +	struct ssi_platform_data *p_data =
> > +		(struct ssi_platform_data *) pd->dev.platform_data;
> 
> unnecessary cast. Remove
> 
> > +	unsigned int n_ch = 0;
> > +	int port;
> > +	int ch = 0;
> > +	int err = 0;
> > +
> > +	for (port = 0; ((port < p_data->num_ports) && (err >= 0)); port++) {
> > +		n_ch = max(p_data->ports[port].tx_ch,
> > +						p_data->ports[port].rx_ch);
> > +		for (ch = 0; ((ch < n_ch) && (err >= 0)); ch++)
> > +			err = reg_ssi_dev_ch(pd, port, ch);
> > +	}
> > +
> > +	if (err < 0) {
> > +		port--;
> > +		ch--;
> > +		dev_err(&pd->dev, "Error registering ssi device channel "
> > +					"p%d-c%d : %d\n", port, ch, err);
> > +		if ((port == 0) && (ch == 0))
> > +			return err;
> > +		else if ((port > 0) && (ch == 0))
> > +			ch = n_ch;
> > +		ssi_undo_reg_dev(pd, port, ch);
> > +	}
> > +	return err;
> > +}
> > +
> > +static int __init ssi_softreset(struct ssi_dev *ssi_ctrl)
> > +{
> > +	int ind = 0;
> > +	void __iomem *base = ssi_ctrl->base;
> > +	u32 status;
> > +
> > +	ssi_outl_or(SSI_SOFTRESET, base, SSI_SYS_SYSCONFIG_REG);
> > +
> > +	status = ssi_inl(base, SSI_SYS_SYSSTATUS_REG);
> > +	while ((!(status & SSI_RESETDONE)) && (ind < SSI_RESETDONE_RETRIES)) {
> > +		set_current_state(TASK_UNINTERRUPTIBLE);
> > +		schedule_timeout(msecs_to_jiffies(SSI_RESETDONE_TIMEOUT));
> > +		status = ssi_inl(base, SSI_SYS_SYSSTATUS_REG);
> > +		ind++;
> > +	}
> > +
> > +	if (ind >= SSI_RESETDONE_RETRIES)
> > +		return -EIO;
> > +
> > +	/* Reseting GDD */
> > +	ssi_outl_or(SSI_SWRESET, base, SSI_GDD_GRST_REG);
> > +
> > +	return 0;
> > +}
> > +
> > +static void __init set_ssi_ports_default(
> > +						struct platform_device *pd)
> > +{
> > +	struct ssi_port_pd *cfg = NULL;
> > +	struct ssi_platform_data *p_data =
> > +		(struct ssi_platform_data *) pd->dev.platform_data;
> > +	unsigned int port = 0;
> > +	void __iomem *base = p_data->ssi_ctrl->base;
> 
> is this really a virtual address?? then can you use
> __raw_{read,write}[blw]() and drop ssi-specific read/write functions?
> 
> btw, the base address shouldn't come via pdata, it should come via
> struct resource and get ioremap:ed in the driver. Recently we fixed all
> the physical/virtual address mess and if we accept this it'll lead to
> similar issues. Please fix.
> 
> > +
> > +	for (port = 1; port <= p_data->num_ports; port++) {
> > +		cfg = &p_data->ports[port - 1];
> > +		ssi_outl(cfg->tx_mode, base, SSI_SST_MODE_REG(port));
> > +		ssi_outl(cfg->tx_frame_size, base, SSI_SST_FRAMESIZE_REG(port));
> > +		ssi_outl(cfg->divisor, base, SSI_SST_DIVISOR_REG(port));
> > +		ssi_outl(cfg->tx_ch, base, SSI_SST_CHANNELS_REG(port));
> > +		ssi_outl(cfg->arb_mode, base, SSI_SST_ARBMODE_REG(port));
> > +
> > +		ssi_outl(cfg->rx_mode, base, SSI_SSR_MODE_REG(port));
> > +		ssi_outl(cfg->rx_frame_size, base, SSI_SSR_FRAMESIZE_REG(port));
> > +		ssi_outl(cfg->rx_ch, base, SSI_SSR_CHANNELS_REG(port));
> > +		ssi_outl(cfg->timeout, base, SSI_SSR_TIMEOUT_REG(port));
> > +	}
> > +}
> > +
> > +static int __init ssi_port_channels_init(
> > +			struct platform_device *pd, unsigned int lport)
> > +{
> > +	struct ssi_platform_data *p_data =
> > +		(struct ssi_platform_data *) pd->dev.platform_data;
> > +	struct ssi_channel *ch;
> > +	struct ssi_port *port;
> > +	unsigned int n_ch;
> > +	unsigned int ch_i;
> > +
> > +	n_ch = max(p_data->ports[lport].tx_ch, p_data->ports[lport].rx_ch);
> > +	port =  &p_data->ssi_ctrl->ssi_port[lport];
> > +	for (ch_i = 0; ch_i < n_ch; ch_i++) {
> > +		ch = &port->ssi_channel[ch_i];
> > +		ch->channel_number = ch_i;
> > +		ch->flags = 0;
> > +		ch->ssi_port = port;
> > +		ch->read_data.addr = NULL;
> > +		ch->read_data.size = 0;
> > +		ch->read_data.lch = -1;
> > +		ch->write_data.addr = NULL;
> > +		ch->write_data.size = 0;
> > +		ch->write_data.lch = -1;
> > +		spin_lock_init(&ch->ssi_ch_lock);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int __init ssi_ports_init(struct platform_device *pd)
> > +{
> > +	struct ssi_platform_data *p_data =
> > +		(struct ssi_platform_data *) pd->dev.platform_data;
> 
> unnecessary cast. Remove
> 
> > +	struct ssi_port *ssi_p = NULL;
> > +	unsigned int port = 0;
> > +	unsigned int err = 0;
> > +	unsigned int n_ports;
> > +
> > +	n_ports = min(p_data->num_ports, (u8)SSI_MAX_PORTS);
> > +
> > +	for (port = 0; ((port < n_ports) && (err >= 0)); port++) {
> > +		ssi_p = &p_data->ssi_ctrl->ssi_port[port];
> > +		ssi_p->port_number = port + 1;
> > +		ssi_p->ssi_controller = p_data->ssi_ctrl;
> > +		ssi_p->max_ch = max(p_data->ports[port].tx_ch,
> > +						p_data->ports[port].rx_ch);
> > +		ssi_p->max_ch = min(ssi_p->max_ch, (u8)SSI_PORT_MAX_CH);
> > +		ssi_p->n_irq = p_data->ports[port].n_irq;
> > +		ssi_p->irq = 0;
> > +		spin_lock_init(&ssi_p->lock);
> > +		err = ssi_port_channels_init(pd, port);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int __init ssi_controller_init(struct platform_device *pd)
> > +{
> > +	struct ssi_platform_data *p_data =
> > +		(struct ssi_platform_data *) pd->dev.platform_data;
> > +	struct ssi_dev *ssi_ctrl = p_data->ssi_ctrl;
> > +	int err;
> > +
> > +	ssi_ctrl->id = pd->id;
> > +	ssi_ctrl->max_p = p_data->num_ports;
> 
> in the header file you have a define for the max_ports and here you use
> platform_data to initialize it. Quite weird. Where are you using that
> define ?
> 
> > +	ssi_ctrl->pdev = pd;
> 
> do not save the entire pdev pointer, save only the dev pointer use
> platform_set_drvdata(pdev, ssi_ctrl);
> 
> > +	spin_lock_init(&ssi_ctrl->lock);
> > +	ssi_ctrl->ssi_clk = clk_get(&pd->dev, p_data->clk_name);
> 
> please don't.
> 
> > +	if (IS_ERR(ssi_ctrl->ssi_clk)) {
> > +		dev_err(&pd->dev, "Unable to get SSI clocks");
> > +		return PTR_ERR(ssi_ctrl->ssi_clk);
> > +	}
> > +
> > +	err = ssi_ports_init(pd);
> > +	if (err < 0) {
> > +		dev_err(&pd->dev, "Error on ssi_controller initialization");
> > +		clk_put(ssi_ctrl->ssi_clk);
> > +		return -EBUSY;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void ssi_controller_exit(struct platform_device *pd)
> > +{
> > +	struct ssi_platform_data *p_data =
> > +		(struct ssi_platform_data *) pd->dev.platform_data;
> 
> unnecessary cast, remove.
> 
> > +	struct ssi_dev *ssi_ctrl = p_data->ssi_ctrl;
> > +
> > +	p_data->ssi_ctrl = NULL;
> > +	ssi_ctrl->pdev = NULL;
> > +	clk_put(ssi_ctrl->ssi_clk);
> > +
> 
> unnecessary extra white line, remove.
> 
> > +}
> > +
> > +static int __init request_ssi_irqs(struct platform_device *pd)
> > +{
> > +	struct ssi_platform_data *p_data = pd->dev.platform_data;
> > +	struct ssi_dev *ssi_ctrl = p_data->ssi_ctrl;
> > +	struct ssi_port *ssi_p;
> > +	struct resource *mpu_irq;
> 
> unnecessary if it's only to get the irq.
> 
> > +	int i;
> > +	int j;
> > +	int err = 0;
> > +
> > +	for (i = 0; ((i < p_data->num_ports) && (!err)); i++) {
> > +		mpu_irq = platform_get_resource(pd, IORESOURCE_IRQ, i*2);
> 
> no
> 
> > +		if (!mpu_irq) {
> > +			dev_err(&pd->dev, "SSI misses info for MPU IRQ"
> > +							" on port %d", i + 1);
> > +			err = -ENODEV;
> 
> use a goto to create a nice error path, then you remove this else below.
> 
> > +		} else {
> > +			ssi_p = &ssi_ctrl->ssi_port[i];
> > +			ssi_p->n_irq = 0; /* We only use one irq line */
> > +			ssi_p->irq = mpu_irq->start;
> 
> 			ssi_p->irq = platform_get_irq(pdev, 0);
> 			also gets rid of the unnecessary mpu_irq.
> 
> > +			tasklet_init(&ssi_p->ssi_tasklet,
> > +					do_ssi_tasklet,	(unsigned long)ssi_p);
> > +			err = request_irq(mpu_irq->start, ssi_mpu_handler,
> > +				IRQF_DISABLED, mpu_irq->name, ssi_p);
> > +			if (err < 0) {
> > +				dev_err(&pd->dev, "FAILED request IRQ (%d)",
> > +							 mpu_irq->start);
> > +				err = -EBUSY;
> > +			}
> > +		}
> > +	}
> > +
> > +	if (err < 0) {
> > +		/* Let's free the reserved resources if there are any errors */
> > +		for (j = 0; (j > (i-1)); j++) {
> > +			printk(KERN_DEBUG LOG_NAME "Free resources on port %d",
> > +									j+1);
> > +			ssi_p = &ssi_ctrl->ssi_port[i];
> > +			tasklet_disable(&ssi_p->ssi_tasklet);
> > +			free_irq(ssi_p->irq, ssi_p);
> > +		}
> > +	}
> > +
> > +	return err;
> > +}
> > +
> > +static int __init request_ssi_gdd_irq(struct platform_device *pd)
> > +{
> > +	struct ssi_platform_data *p_data = pd->dev.platform_data;
> > +	struct ssi_dev *ssi_ctrl = p_data->ssi_ctrl;
> > +	struct resource *gdd_irq;
> 
> no
> 
> > +	int err = 0;
> > +
> > +	gdd_irq = platform_get_resource(pd, IORESOURCE_IRQ, 4);
> 
> no
> 
> > +	if (!gdd_irq) {
> > +		dev_err(&pd->dev, "SSI device has no info about GDD IRQ");
> > +		return -ENODEV;
> > +	}
> > +
> > +	ssi_ctrl->gdd_irq = gdd_irq->start;
> 
> hell no. Use platform_get_irq() like I said before.
> 
> > +	err = request_irq(gdd_irq->start, ssi_gdd_mpu_handler,
> > +		IRQF_DISABLED, gdd_irq->name, ssi_ctrl);
> > +	if (err < 0) {
> > +		dev_err(&pd->dev, "FAILED to request IRQ NUMBER (%d)",
> > +							gdd_irq->start);
> > +		return -EBUSY;
> > +	}
> > +	tasklet_init(&ssi_ctrl->ssi_gdd_tasklet, do_ssi_gdd_tasklet,
> > +			(unsigned long)ssi_ctrl);
> > +
> > +	return err;
> > +}
> > +
> > +static void free_ssi_irqs(struct ssi_dev *ssi_ctrl)
> > +{
> > +	struct ssi_port *ssi_p;
> > +	int i;
> > +
> > +	for (i = 0; (i < ssi_ctrl->max_p); i++) {
> > +		ssi_p = &ssi_ctrl->ssi_port[i];
> > +		tasklet_disable(&ssi_p->ssi_tasklet);
> > +		free_irq(ssi_p->irq, ssi_p);
> > +	}
> > +}
> > +
> > +static void free_ssi_gdd_irq(struct ssi_dev *ssi_ctrl)
> > +{
> > +	tasklet_disable(&ssi_ctrl->ssi_gdd_tasklet);
> > +	free_irq(ssi_ctrl->gdd_irq, ssi_ctrl);
> > +}
> > +
> > +static int __init ssi_probe(struct platform_device *pd)
> > +{
> > +	struct resource *mem, *ioarea;
> > +	struct ssi_dev *ssi_ctrl = NULL;
> > +	struct ssi_platform_data *p_data = NULL;
> > +	u32 revision = 0;
> > +	int err = 0;
> > +
> > +
> > +	if ((pd == NULL) || (pd->dev.platform_data == NULL)) {
> > +		pr_err(LOG_NAME "No device/platform_data found on "
> > +								"ssi device\n");
> > +		return -ENODEV;
> > +	}
> 
> aff... pd will never be NULL if this driver is probing. I'd rather:
> 
> struct ssi_platform_data *pdata = pd->dev.platform_data;
> 
> if (!pdata) {
> 	error messages goes here
> }
> 
> > +
> > +	ssi_ctrl = kzalloc(sizeof(*ssi_ctrl), GFP_KERNEL);
> > +	if (ssi_ctrl == NULL) {
> > +		dev_err(&pd->dev, "Could not allocate memory for"
> > +					" struct ssi_dev\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	p_data = (struct ssi_platform_data *) pd->dev.platform_data;
> 
> no cast needed.
> 
> > +	p_data->ssi_ctrl = ssi_ctrl;
> 
> hell no. platform_set_drvdata(pd, ssi_ctrl);
> 
> > +
> > +	mem = platform_get_resource(pd, IORESOURCE_MEM, 0);
> > +	if (!mem) {
> > +		dev_err(&pd->dev, "SSI device does not have "
> > +					"SSI IO memory region information\n");
> > +		err = -ENODEV;
> > +		goto rback5;
> > +	}
> > +
> > +	err = ssi_controller_init(pd);
> > +	if (err < 0) {
> > +		dev_err(&pd->dev, "Could not initialize ssi controller:"
> > +						" %d\n", err);
> > +		goto rback5;
> > +	}
> > +
> > +	ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
> > +		 pd->dev.bus_id);
> > +	if (!ioarea) {
> > +		dev_err(&pd->dev, "Unable to request SSI IO memory "
> > +								"region\n");
> > +		err = -EBUSY;
> > +		goto rollback4;
> > +	}
> > +
> > +	ssi_ctrl->base = (void __iomem *)mem->start;
> 
> afff... this is terrible. Use ioremap();
> 
> > +
> > +	clk_enable(ssi_ctrl->ssi_clk);
> > +
> > +	err = request_ssi_irqs(pd);
> > +	if (err < 0)
> > +		goto rollback1;
> > +
> > +	err = request_ssi_gdd_irq(pd);
> > +	if (err < 0)
> > +		goto rollback2;
> > +
> > +	err = ssi_softreset(ssi_ctrl);
> > +	if (err < 0)
> > +		goto rollback3;
> > +
> > +	/* Set default PM settings */
> > +	ssi_outl((SSI_AUTOIDLE | SSI_SIDLEMODE_SMART | SSI_MIDLEMODE_SMART),
> > +			ssi_ctrl->base,  SSI_SYS_SYSCONFIG_REG);
> > +	ssi_outl(SSI_CLK_AUTOGATING_ON, ssi_ctrl->base, SSI_GDD_GCR_REG);
> > +
> > +	set_ssi_ports_default(pd);
> > +
> > +	/* Gather info from registers for the driver.(REVISION) */
> > +	revision = ssi_inl(ssi_ctrl->base, SSI_SYS_REVISION_REG);
> > +	dev_info(&pd->dev, "SSI Hardware REVISION %d.%d\n",
> > +		 (revision & SSI_REV_MAJOR) >> 4, (revision & SSI_REV_MINOR));
> > +
> > +	err = register_ssi_devices(pd);
> > +	if (err < 0)
> > +		goto rollback3;
> > +
> > +	clk_disable(ssi_ctrl->ssi_clk);
> > +
> > +	return err;
> > +
> > +rollback3:
> > +	free_ssi_gdd_irq(ssi_ctrl);
> > +rollback2:
> > +	free_ssi_irqs(ssi_ctrl);
> > +rollback1:
> 	add iounmap() here
> > +	release_mem_region(mem->start, mem->end - mem->start + 1);
> > +	clk_disable(ssi_ctrl->ssi_clk);
> > +rollback4:
> > +	ssi_controller_exit(pd);
> > +rback5:
> > +	kfree(ssi_ctrl);
> > +	return err;
> > +}
> > +
> > +static void __exit close_all_ch(struct ssi_dev *ssi_ctrl)
> > +{
> > +	struct ssi_port *ssi_p;
> > +	unsigned int port;
> > +	unsigned int ch;
> > +
> > +	for (port = 0; port < ssi_ctrl->max_p; port++) {
> > +		ssi_p = &ssi_ctrl->ssi_port[port];
> > +		for (ch = 0; ch < ssi_p->max_ch; ch++)
> > +			ssi_close(ssi_p->ssi_channel[ch].dev);
> > +	}
> > +}
> > +
> > +static int __exit ssi_remove(struct platform_device *pd)
> > +{
> > +	struct resource *mem;
> > +	struct ssi_platform_data *p_data =
> > +		(struct ssi_platform_data *) pd->dev.platform_data;
> > +	struct ssi_dev *ssi_ctrl = p_data->ssi_ctrl;
> 
> This should ssi_ctrl = platform_get_drvdata(pd);
> 
> > +
> > +	close_all_ch(ssi_ctrl);
> > +	free_ssi_gdd_irq(ssi_ctrl);
> > +	free_ssi_irqs(ssi_ctrl);
> > +	mem = platform_get_resource(pd, IORESOURCE_MEM, 0);
> > +	if (mem)
> > +		release_mem_region(mem->start, mem->end - mem->start + 1);
> 
> when you add proper ioremap(), add iounmap here.
> 
> > +	ssi_controller_exit(pd);
> > +	kfree(ssi_ctrl);
> > +
> > +	return 0;
> > +}
> > +
> > +static struct platform_driver ssi_pdriver = {
> > +	.probe = ssi_probe,
> > +	.remove = __exit_p(ssi_remove),
> > +	.driver = {
> > +		.name = "omap_ssi",
> > +		.owner = THIS_MODULE,
> > +	}
> > +};
> > +
> > +static int __init ssi_driver_init(void)
> > +{
> > +	int err = 0;
> > +
> > +	pr_info("SSI DRIVER Version " SSI_DRIVER_VERSION "\n");
> > +
> > +	ssi_bus_init();
> > +	err = platform_driver_probe(&ssi_pdriver, ssi_probe);
> > +	if (err < 0) {
> > +		pr_err(LOG_NAME "Platform DRIVER register FAILED: %d\n", err);
> > +		ssi_bus_exit();
> > +		return err;
> > +	}
> > +
> > +	return 0;
> 
> return platform_driver_register(&ssi_pdriver);
> should be enough.
> 
> > +}
> > +
> > +static void __exit ssi_driver_exit(void)
> > +{
> > +	ssi_bus_exit();
> > +	platform_driver_unregister(&ssi_pdriver);
> > +
> > +	pr_info("SSI DRIVER removed\n");
> > +}
> > +
> > +module_init(ssi_driver_init);
> > +module_exit(ssi_driver_exit);
> > +
> > +MODULE_ALIAS("platform:omap_ssi");
> > +MODULE_AUTHOR(SSI_DRIVER_AUTHOR);
> > +MODULE_DESCRIPTION(SSI_DRIVER_DESC);
> > +MODULE_LICENSE("GPL");
> > diff --git a/drivers/misc/ssi/ssi_driver.h b/drivers/misc/ssi/ssi_driver.h
> > new file mode 100644
> > index 0000000..3c6d849
> > --- /dev/null
> > +++ b/drivers/misc/ssi/ssi_driver.h
> > @@ -0,0 +1,211 @@
> > +/*
> > + * ssi_driver.h
> > + *
> > + * Header file for the SSI driver low level interface.
> > + *
> > + * Copyright (C) 2007-2008 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 __SSI_DRIVER_H__
> > +#define __SSI_DRIVER_H__
> > +
> > +#include <linux/module.h>
> > +#include <linux/init.h>
> > +#include <linux/clk.h>
> > +#include <linux/ioport.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irq.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/io.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/gpio.h>
> > +
> > +#include <mach/ssi/ssi_reg_common.h>
> > +#include <mach/ssi/ssi_sys_reg.h>
> > +#include <mach/ssi/ssi_ssr_reg.h>
> > +#include <mach/ssi/ssi_sst_reg.h>
> > +#include <mach/ssi/ssi_gdd_reg.h>
> > +
> > +#include <linux/ssi_driver_if.h>
> > +
> > +#define	SSI_DRIVER_VERSION	"1.1-rc2"
> > +#define SSI_DRIVER_AUTHOR	"Carlos Chinea / Nokia"
> > +#define SSI_DRIVER_DESC		"Synchronous Serial Interface Driver"
> > +#define SSI_DRIVER_LINCESE	"GPL"
> > +#define SSI_DRIVER_NAME		"ssi_driver"
> > +
> > +#define SSI_DEVICE_NAME		"ssi_device"
> 
> these should be in the C-file. Nobody needs to access them
> 
> > +
> > +/* 10 ms */
> > +#define SSI_RESETDONE_TIMEOUT	10
> > +/* Let's retry 20 times.=>20x10 ms=200 ms */
> > +#define SSI_RESETDONE_RETRIES	20
> > +
> > +/* Channel states */
> > +#define	SSI_CH_OPEN		0x01
> > +
> > +/*
> > + * The number of channels to use by the driver in the ports, or the highest
> > + * port channel number (+1) used. (MAX:8)
> > + */
> > +#define SSI_PORT_MAX_CH		4
> > +/* Number of logical channel in GDD */
> > +#define SSI_NUM_LCH		8
> > +
> > +#define LOG_NAME		"OMAP SSI: "
> > +#define SSI_PREFIX		"ssi:"
> > +
> > +/*
> > + * Callbacks use by the SSI upper layer (SSI protocol) to receive data
> > + * from the port channels.
> > + */
> > +struct ssi_channel_ops {
> > +	void (*write_done) (struct ssi_device *dev);
> > +	void (*read_done) (struct ssi_device *dev);
> > +};
> > +
> > +struct ssi_data {
> > +	/* Pointer to the data to be sent/received */
> > +	u32 *addr;
> > +	/*
> > +	 * Number of words to be sent or space reserved for data to be
> > +	 * received.
> > +	 */
> > +	unsigned int size;
> > +	/* Holds GDD logical channed number */
> > +	int lch;
> > +};
> > +
> > +struct ssi_channel {
> > +	struct ssi_channel_ops ops;
> > +	struct ssi_data read_data;
> > +	struct ssi_data write_data;
> > +	struct ssi_port *ssi_port;
> > +	u8 flags;
> > +	u8 channel_number;
> > +	spinlock_t ssi_ch_lock;
> > +	struct ssi_device *dev;
> > +	void *priv;
> > +};
> > +
> > +/* Holds information related to SSI port */
> > +struct ssi_port {
> > +	struct ssi_channel ssi_channel[SSI_PORT_MAX_CH];
> > +	struct ssi_dev *ssi_controller;
> > +	u8 port_number;
> > +	u8 max_ch;
> > +	u8 n_irq; /* IRQ0 or IRQ1 */
> > +	int irq	    /* Actual IRQ number */;
> > +	spinlock_t lock;
> > +	struct tasklet_struct ssi_tasklet;
> > +};
> > +
> > +/*
> > + * Struct definition to hold information about the clocks, ssi controller
> > + * and the ssi ports.
> > + */
> > +struct ssi_dev {
> > +	/* Holds reference to PORT 1 (and PORT2 if defined) */
> > +	struct ssi_port ssi_port[SSI_MAX_PORTS];
> > +	int id;
> > +	u8 flags;
> > +	u8 max_p;
> > +	struct clk *ssi_clk;
> > +	void __iomem *base;
> > +	spinlock_t lock;
> > +	int gdd_irq;
> > +	struct tasklet_struct ssi_gdd_tasklet;
> > +	struct platform_device *pdev;
> > +};
> > +
> > +/* SSI Bus */
> > +struct ssi_port_event {
> > +	struct ssi_port *ssi_port;
> > +	unsigned int event;
> > +	void *priv;
> > +};
> > +extern struct bus_type ssi_bus_type;
> > +
> > +int ssi_port_event_handler(struct ssi_port *p, unsigned int event, void *arg);
> > +int ssi_bus_init(void);
> > +void ssi_bus_exit(void);
> > +/* End SSI Bus */
> > +
> > +int ssi_driver_read_interrupt(struct ssi_channel *ssi_channel, u32 *data);
> > +int ssi_driver_write_interrupt(struct ssi_channel *ssi_channel, u32 *data);
> > +int ssi_driver_read_dma(struct ssi_channel *ssi_channel, u32 *data,
> > +			unsigned int count);
> > +int ssi_driver_write_dma(struct ssi_channel *ssi_channel, u32 *data,
> > +			unsigned int count);
> > +
> > +void ssi_driver_cancel_write_interrupt(struct ssi_channel *ch);
> > +void ssi_driver_cancel_read_interrupt(struct ssi_channel *ch);
> > +void ssi_driver_cancel_write_dma(struct ssi_channel *ch);
> > +void ssi_driver_cancel_read_dma(struct ssi_channel *ch);
> > +
> > +irqreturn_t ssi_mpu_handler(int irq, void *ssi_port);
> > +irqreturn_t ssi_gdd_mpu_handler(int irq, void *ssi_controller);
> > +
> > +void do_ssi_tasklet(unsigned long ssi_port);
> > +void do_ssi_gdd_tasklet(unsigned long device);
> > +
> > +static inline u32 ssi_inl(void __iomem *base, u32 offset)
> > +{
> > +	return inl(OMAP2_IO_ADDRESS(base + offset));
> > +}
> > +
> > +static inline void ssi_outl(u32 data, void __iomem *base, u32 offset)
> > +{
> > +	outl(data, OMAP2_IO_ADDRESS(base + offset));
> > +}
> > +
> > +static inline void ssi_outl_or(u32 data, void __iomem *base, u32 offset)
> > +{
> > +	u32 tmp = ssi_inl(base, offset);
> > +	ssi_outl((tmp | data), base, offset);
> > +}
> > +
> > +static inline void ssi_outl_and(u32 data, void __iomem *base, u32 offset)
> > +{
> > +	u32 tmp = ssi_inl(base, offset);
> > +	ssi_outl((tmp & data), base, offset);
> > +}
> > +
> > +static inline u16 ssi_inw(void __iomem *base, u32 offset)
> > +{
> > +	return inw(OMAP2_IO_ADDRESS(base + offset));
> > +}
> > +
> > +static inline void ssi_outw(u16 data, void __iomem *base, u32 offset)
> > +{
> > +	outw(data, OMAP2_IO_ADDRESS(base + offset));
> > +}
> > +
> > +static inline void ssi_outw_or(u16 data, void __iomem *base, u32 offset)
> > +{
> > +	u16 tmp = ssi_inw(base, offset);
> > +	ssi_outw((tmp | data), base, offset);
> > +}
> > +
> > +static inline void ssi_outw_and(u16 data, void __iomem *base, u32 offset)
> > +{
> > +	u16 tmp = ssi_inw(base, offset);
> > +	ssi_outw((tmp & data), base, offset);
> > +}
> > +#endif
> > diff --git a/drivers/misc/ssi/ssi_driver_bus.c b/drivers/misc/ssi/ssi_driver_bus.c
> > new file mode 100644
> > index 0000000..6a07ee0
> > --- /dev/null
> > +++ b/drivers/misc/ssi/ssi_driver_bus.c
> > @@ -0,0 +1,192 @@
> > +/*
> > + * ssi_driver_bus.c
> > + *
> > + * Implements SSI bus, device and driver interface.
> > + *
> > + * Copyright (C) 2007-2008 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/device.h>
> > +#include "ssi_driver.h"
> > +
> > +/* LDM. defintions for the ssi bus, ssi device, and ssi_device driver */
> > +struct bus_type ssi_bus_type;
> > +
> > +static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
> > +								char *buf)
> > +{
> > +	return snprintf(buf, BUS_ID_SIZE + 1, "%s%s\n", SSI_PREFIX,
> > +								dev->bus_id);
> > +}
> > +
> > +static struct device_attribute ssi_dev_attrs[] = {
> > +	__ATTR_RO(modalias),
> > +	__ATTR_NULL,
> > +};
> > +
> > +static int ssi_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
> > +{
> > +	add_uevent_var(env, "MODALIAS=%s%s", SSI_PREFIX, dev->bus_id);
> > +	return 0;
> > +}
> > +
> > +/* NOTE: Function called in interrupt context */
> > +static int ssi_e_handler(struct device_driver *drv, void *p_event)
> > +{
> > +	struct ssi_port_event *event = (struct ssi_port_event *)p_event;
> > +	struct ssi_device_driver *ssi_drv =  to_ssi_device_driver(drv);
> > +	struct ssi_port *p = event->ssi_port;
> > +
> > +	BUG_ON(p_event == NULL);
> > +
> > +	if ((ssi_drv->port_event) &&
> > +		(test_bit(event->event, &ssi_drv->event_mask)) &&
> > +		((p->ssi_controller->id == -1) ||
> > +		(test_bit(p->ssi_controller->id, &ssi_drv->ctrl_mask))) &&
> > +		(ssi_drv->ch_mask[p->port_number - 1] != 0)) {
> > +
> > +		(*ssi_drv->port_event)(p->ssi_controller->id, p->port_number,
> > +					event->event, event->priv);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +int ssi_port_event_handler(struct ssi_port *p, unsigned int event, void *arg)
> > +{
> > +	int err = 0;
> > +	struct ssi_port_event p_ev = {
> > +		.ssi_port = p,
> > +		.event = event,
> > +		.priv = arg
> > +	};
> > +
> > +	BUG_ON(p == NULL);
> > +
> > +	err = bus_for_each_drv(&ssi_bus_type, NULL, &p_ev, ssi_e_handler);
> > +
> > +	return err;
> > +}
> > +
> > +static int ssi_bus_match(struct device *device, struct device_driver *driver)
> > +{
> > +	struct ssi_device *dev = to_ssi_device(device);
> > +	struct ssi_device_driver *drv = to_ssi_device_driver(driver);
> > +
> > +	if (!test_bit(dev->n_ctrl, &drv->ctrl_mask))
> > +		return 0;
> > +
> > +	if (!test_bit(dev->n_ch, &drv->ch_mask[dev->n_p]))
> > +		return 0;
> > +
> > +	return 1;
> > +}
> > +
> > +int ssi_bus_unreg_dev(struct device *device, void *p)
> > +{
> > +	device->release(device);
> > +	device_unregister(device);
> > +
> > +	return 0;
> > +}
> > +
> > +int __init ssi_bus_init(void)
> > +{
> > +

remove this extra line

> > +	return bus_register(&ssi_bus_type);
> > +

remove this extra line

> > +}
> > +
> > +void ssi_bus_exit(void)
> > +{
> > +	bus_for_each_dev(&ssi_bus_type, NULL, NULL, ssi_bus_unreg_dev);
> > +	bus_unregister(&ssi_bus_type);
> > +}
> > +
> > +static int ssi_driver_probe(struct device *dev)
> > +{
> > +	struct ssi_device_driver *drv = to_ssi_device_driver(dev->driver);
	int ret;

	if (!drv->probe) /* note if you add MODULE_DEVICE_TABLE test here !drv->id_table */
		return -ENODEV;

I think would be nice to save a reference of driver inside ssi_device,
so you would:

	to_ssi_device(dev)->driver = drv;
	dev_dbg(dev, "probe\n");

	ret = driver->probe(to_ssi_device(dev));

	if (ret)
		to_ssi_device(dev)->driver = NULL

	return ret;

> > +}
> > +
> > +static int ssi_driver_remove(struct device *dev)
> > +{
> > +	struct ssi_device_driver *drv = to_ssi_device_driver(dev->driver);
	int ret;

	if (!dev->driver)
		return 0;

	drv = to_ssi_device_driver(dev->driver);
	if (drv->remove) {
		dev_dbg(dev, "remove\n");
		ret = drv->remove(to_ssi_device(dev));
	} else {
		dev->driver = NULL;
		ret = 0;
	}

	if (ret == 0)
		to_sse_device(dev)->driver = NULL;

	return ret;
> > +}
> > +
> > +static int ssi_driver_suspend(struct device *dev, pm_message_t mesg)
> > +{
> > +	struct ssi_device_driver *drv = to_ssi_device_driver(dev->driver);
> > +
> > +	return drv->suspend(to_ssi_device(dev), mesg);
> > +}
> > +
> > +static int ssi_driver_resume(struct device *dev)
> > +{
> > +	struct ssi_device_driver *drv = to_ssi_device_driver(dev->driver);
> > +
> > +	return drv->resume(to_ssi_device(dev));

you need to be careful here. Try something like:

	if (!dev->driver)
		return 0;

	drv = to_ssi_device_driver(dev->driver);

	if (!drv->suspend)
		return 0;

	return driver->suspend(to_ssi_device(dev), msg);

ditto to suspend

> > +}
> > +
> > +struct bus_type ssi_bus_type = {
> > +	.name = "ssi",
> > +	.dev_attrs = ssi_dev_attrs,
> > +	.match = ssi_bus_match,
> > +	.uevent = ssi_bus_uevent,
> 
> tabify these
> 
> 	.name		= "ssi",
> 	.dev_attrs	= ssi_dev_attrs,
> 	.match		= ssi_bus_match,
> 	.uevent		= ssi_bus_uevent,

add suspend, resume, probe and remove to ssi_bus_type and remove what
you have in register_ssi_driver.

> > +int register_ssi_driver(struct ssi_device_driver *driver)
> > +{
> > +	int ret = 0;
> > +
> > +	BUG_ON(driver == NULL);
> 
> don't bug, just return. BUG will oops the kernel. Returning would be
> more appropriate since what needs fix is the driver trying to register
> not the bus driver.
> 
> > +
> > +	driver->driver.bus = &ssi_bus_type;

--- remove from here

> > +	if (driver->probe)
> > +		driver->driver.probe = ssi_driver_probe;
> > +	if (driver->remove)
> > +		driver->driver.remove = ssi_driver_remove;
> > +	if (driver->suspend)
> > +		driver->driver.suspend = ssi_driver_suspend;
> > +	if (driver->resume)
> > +		driver->driver.resume = ssi_driver_resume;
> > +

--- to here.

> > +	ret = driver_register(&driver->driver);

would be nice to get a success message if ret == 0:

if (ret == 0)
	pr_debug("ssi: driver %s registered\n", driver->driver.name);

> > +
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL(register_ssi_driver);
> > +
> > +/**
> > + * unregister_ssi_driver - Unregister SSI device driver
> > + * @driver - reference to the SSI device driver.
> > + */
> > +void unregister_ssi_driver(struct ssi_device_driver *driver)
> > +{
> > +	BUG_ON(driver == NULL);
> 
> ditto.
> 
> > +
> > +	driver_unregister(&driver->driver);

same here:

	pr_debug("ssi: driver %s unregistered\n", driver->driver.name);

> > +}
> > +EXPORT_SYMBOL(unregister_ssi_driver);
> > diff --git a/drivers/misc/ssi/ssi_driver_dma.c b/drivers/misc/ssi/ssi_driver_dma.c
> > new file mode 100644
> > index 0000000..2524a12
> > --- /dev/null
> > +++ b/drivers/misc/ssi/ssi_driver_dma.c
> > @@ -0,0 +1,406 @@
> > +/*
> > + * ssi_driver_dma.c
> > + *
> > + * Implements SSI low level interface driver functionality with DMA support.
> > + *
> > + * Copyright (C) 2007-2008 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/dma-mapping.h>
> > +#include "ssi_driver.h"
> > +
> > +#define SSI_SYNC_WRITE	0
> > +#define SSI_SYNC_READ  	1
> 			^^ trailing whitespaces
> > +

the following variables and functions, etc please prepend them with ssi_
to avoid possible namespace conflicts.

also, fix the comment style to be kerneldoc style:

/**
 * function name - small description
 *
 * @parameter1: small description
 * @parameter2: small description
 * @parameterN: small description
 *
 * Here you put a long description of the function
 * and what it really does. Just don't extend too much
 * and don't translate C-code into english, just give
 * information on what's the main purpose for that function
 * and anything you think is really valid to mention.
 *
 * Returns 0 on success or negative errno
 */

of course the Returns blabla will have to change according to what the
function really returns: channel number, port number, sync type, etc.

> > +static unsigned char sync_table[2][2][8] = {
> > +	{
> > +	 {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
> > +	 {0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00}
> > +	 },
> > +	{
> 
> 	}, {   would look better
> 
> > +	 {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17},
> > +	 {0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}
> > +	 }
> > +};
> > +
> > +static unsigned int get_sync_type(unsigned int sync)
> > +{
> > +	return (sync & 0x10) ? SSI_SYNC_READ : SSI_SYNC_WRITE;
> > +}
> > +
> > +static unsigned int get_sync_port(unsigned int sync)
> > +{
> > +	if (((sync >= 0x01) && (sync <= 0x08)) ||
> > +					((sync >= 0x10) && (sync <= 0x17)))
> > +		return 1;
> > +	else if (((sync >= 0x09) && (sync <= 0x0F)) ||
> > +					((sync >= 0x18) && (sync <= 0x1E)))
> > +		return 2;
> > +	else
> > +		return 3;

what's the main idea here? you pass sync and it's return the port number ??
this really looks odd. I wanna understand this better and let's try to
find a better solution, shall we ? :-)

> > +}
> > +
> > +static unsigned int get_sync_channel(unsigned int sync)
> > +{
> > +	if ((sync == 0x00) || (sync == 0x1F))
> > +		return 8;
> > +
> > +	if (sync & 0x10)
> > +		return (sync & 0x0F) % 8;
> > +	else
> > +		return (sync - 1) % 8;

ditto

> > +}
> > +
> > +static unsigned int get_sync(unsigned int port,
> > +			     unsigned int channel, unsigned int type)
> > +{
> > +	if ((port == 0) || (port > SSI_MAX_PORTS) ||
> > +			(channel >= SSI_PORT_MAX_CH) || (type > SSI_SYNC_READ))
> > +		return 0x00;
> > +
> > +	return sync_table[type][port - 1][channel];

ditto

> > +}
> > +
> > +static void rst_ch_read(struct ssi_dev *ssi_ctrl,
> > +				unsigned int n_p, unsigned int n_ch)
> > +{
> > +	struct ssi_channel *ch =
> > +		&ssi_ctrl->ssi_port[n_p - 1].ssi_channel[n_ch];
> > +
> > +	ch->read_data.addr = NULL;
> > +	ch->read_data.size = 0;
> > +	ch->read_data.lch = -1;
> > +}
> > +
> > +static void rst_ch_write(struct ssi_dev *ssi_ctrl,
> > +				unsigned int n_p, unsigned int n_ch)
> > +{
> > +	struct ssi_channel *ch =
> > +		&ssi_ctrl->ssi_port[n_p - 1].ssi_channel[n_ch];
> > +
> > +	ch->write_data.addr = NULL;
> > +	ch->write_data.size = 0;
> > +	ch->write_data.lch = -1;
> > +}
> > +
> > +/*
> > + * get_free_lch - Get a free GDD(DMA)logical channel
> > + * @ssi_ctrl- SSI controller of the GDD.
> > + *
> > + * Needs to be called holding the gdd controller lock
> > + */
> > +static unsigned int get_free_lch(struct ssi_dev *ssi_ctrl)
> > +{
> > +	unsigned int enable_reg;
> > +	unsigned int lch = 0;
> > +
> > +	enable_reg = ssi_inl(ssi_ctrl->base, SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
> > +	while ((lch < SSI_NUM_LCH) && (enable_reg & SSI_GDD_LCH(lch)))
> > +		lch++;
> > +
> > +	return lch;
> > +}
> > +
> > +/*
> > + * ssi_driver_write_dma - Program GDD [DMA] to write data from memory to
> > + * the ssi channel buffer.
> > + * @ssi_channel - pointer to the ssi_channel to write data to.
> > + * @data - 32-bit word pointer to the data.
> > + * @size - Number of 32bit words to be transfered.
> > + *
> > + * ssi_controller lock must be hold before calling this function.

s/hold/held

> > + */
> > +int ssi_driver_write_dma(struct ssi_channel *ssi_channel, u32 *data,
> > +			 unsigned int size)
> > +{
> > +	struct ssi_dev *ssi_ctrl = ssi_channel->ssi_port->ssi_controller;
> > +	void __iomem *base = ssi_ctrl->base;
> > +	unsigned int port = ssi_channel->ssi_port->port_number;
> > +	unsigned int channel = ssi_channel->channel_number;
> > +	unsigned int sync;
> > +	int lch;
> > +	dma_addr_t dma_data;
> > +	u32 s_addr;
> > +	u16 tmp;
> > +
> > +	if ((size < 1) || (data == NULL))
> > +		return -EINVAL;
> > +
> > +	clk_enable(ssi_ctrl->ssi_clk);
> > +
> > +	lch = get_free_lch(ssi_ctrl);
> > +	if (lch >= SSI_NUM_LCH) {
> > +		dev_err(&ssi_ctrl->pdev->dev, "No free GDD logical "
> > +								"channels.\n");
> > +		clk_disable(ssi_ctrl->ssi_clk);
> > +		return -EBUSY;	/* No free GDD logical channels. */
> > +	}
> > +	/* NOTE: Gettting a free gdd logical channel and
> > +	 * reserve it must be done atomicaly. */
> > +	ssi_channel->write_data.lch = lch;
> > +
> > +	sync = get_sync(port, channel, SSI_SYNC_WRITE);
> > +	dma_data = dma_map_single(NULL, data, size * 4, DMA_TO_DEVICE);
> > +	dma_sync_single(NULL, dma_data, size * 4, DMA_TO_DEVICE);
> > +
> > +	tmp = SSI_SRC_SINGLE_ACCESS0 |
> > +		SSI_SRC_MEMORY_PORT |
> > +		SSI_DST_SINGLE_ACCESS0 |
> > +		SSI_DST_PERIPHERAL_PORT |
> > +		SSI_DATA_TYPE_S32;
> > +	ssi_outw(tmp, base, SSI_GDD_CSDP_REG(lch));
> > +	tmp = SSI_SRC_AMODE_POSTINC | SSI_DST_AMODE_CONST | sync;
> > +	ssi_outw(tmp, base, SSI_GDD_CCR_REG(lch));
> > +	ssi_outw((SSI_BLOCK_IE | SSI_TOUT_IE), base, SSI_GDD_CICR_REG(lch));
> > +	s_addr = (u32)base + SSI_SST_BUFFER_CH_REG(port, channel);
> > +	ssi_outl(s_addr, base, SSI_GDD_CDSA_REG(lch));
> > +	ssi_outl(dma_data, base, SSI_GDD_CSSA_REG(lch));
> > +	ssi_outw(size, base, SSI_GDD_CEN_REG(lch));
> > +	ssi_outl_or(SSI_GDD_LCH(lch), base, SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
> > +	ssi_outw_or(SSI_CCR_ENABLE, base, SSI_GDD_CCR_REG(lch));

a bit of spacing up here will increase readability.

> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * ssi_driver_read_dma - Program GDD [DMA] to write data to memory from
> > + * the ssi channel buffer.
> > + * @ssi_channel - pointer to the ssi_channel to read data from.
> > + * @data - 32-bit word pointer where to store the incoming data.
> > + * @size - Number of 32bit words to be transfered to the buffer.
> > + *
> > + * ssi_controller lock must be hold before calling this function.
> > + */
> > +int ssi_driver_read_dma(struct ssi_channel *ssi_channel, u32 *data,
> > +			unsigned int count)
> > +{
> > +	struct ssi_dev *ssi_ctrl = ssi_channel->ssi_port->ssi_controller;
> > +	void __iomem *base = ssi_ctrl->base;
> > +	unsigned int port = ssi_channel->ssi_port->port_number;
> > +	unsigned int channel = ssi_channel->channel_number;
> > +	unsigned int sync;
> > +	unsigned int lch;
> > +	dma_addr_t dma_data;
> > +	u32 d_addr;
> > +	u16 tmp;
> > +
> > +	clk_enable(ssi_ctrl->ssi_clk);
> > +	lch = get_free_lch(ssi_ctrl);
> > +	if (lch >= SSI_NUM_LCH) {
> > +		dev_err(&ssi_ctrl->pdev->dev, "No free GDD logical "
> > +								"channels.\n");
> > +		clk_disable(ssi_ctrl->ssi_clk);
> > +		return -EBUSY;	/* No free GDD logical channels. */
> > +	}
> > +	/*
> > +	 * NOTE: Gettting a free gdd logical channel and
> > +	 * reserve it must be done atomicaly.
> > +	 */
> > +	ssi_channel->read_data.lch = lch;
> > +
> > +	sync = get_sync(port, channel, SSI_SYNC_READ);
> > +
> > +	dma_data = dma_map_single(NULL, data, count * 4, DMA_FROM_DEVICE);
> > +
> > +	tmp = SSI_DST_SINGLE_ACCESS0 |
> > +		SSI_DST_MEMORY_PORT |
> > +		SSI_SRC_SINGLE_ACCESS0 |
> > +		SSI_SRC_PERIPHERAL_PORT |
> > +		SSI_DATA_TYPE_S32;
> > +	ssi_outw(tmp, base, SSI_GDD_CSDP_REG(lch));
> > +	tmp = SSI_DST_AMODE_POSTINC | SSI_SRC_AMODE_CONST | sync;
> > +	ssi_outw(tmp, base, SSI_GDD_CCR_REG(lch));
> > +	ssi_outw((SSI_BLOCK_IE | SSI_TOUT_IE), base, SSI_GDD_CICR_REG(lch));
> > +	d_addr = (u32)base + SSI_SSR_BUFFER_CH_REG(port, channel);

don't keep casting the addresses, find a better solution. If you need to
keep casting them around between void __iomem * and some unsigned, your
code needs attention. Ditto to all other occurrencies of this.

> > +	ssi_outl(d_addr, base, SSI_GDD_CSSA_REG(lch));
> > +	ssi_outl((u32)dma_data, base, SSI_GDD_CDSA_REG(lch));
> > +	ssi_outw(count, base, SSI_GDD_CEN_REG(lch));

ditto.

> > +
> > +	ssi_outl_or(SSI_GDD_LCH(lch), base, SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
> > +	ssi_outw_or(SSI_CCR_ENABLE, base, SSI_GDD_CCR_REG(lch));
> > +
> > +	return 0;
> > +}
> > +
> > +void ssi_driver_cancel_write_dma(struct ssi_channel *ssi_ch)
> > +{
> > +	int lch = ssi_ch->write_data.lch;
> > +	unsigned int port = ssi_ch->ssi_port->port_number;
> > +	unsigned int channel = ssi_ch->channel_number;
> > +	struct ssi_dev *ssi_ctrl = ssi_ch->ssi_port->ssi_controller;
> > +	u32 ccr;
> > +
> > +	if (lch < 0)
> > +		return;
> > +
> > +	clk_enable(ssi_ctrl->ssi_clk);
> > +	ccr = ssi_inw(ssi_ctrl->base, SSI_GDD_CCR_REG(lch));
> > +	if (!(ccr & SSI_CCR_ENABLE)) {
> > +		dev_dbg(&ssi_ch->dev->device, LOG_NAME "Write cancel on not "
> > +		"enabled logical channel %d CCR REG 0x%08X\n", lch, ccr);
> > +		clk_disable(ssi_ctrl->ssi_clk);
> > +		return;
> > +	}
> > +
> > +	ssi_outw_and(~SSI_CCR_ENABLE, ssi_ctrl->base, SSI_GDD_CCR_REG(lch));
> > +	ssi_outl_and(~SSI_GDD_LCH(lch), ssi_ctrl->base,
> > +						SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
> > +	ssi_outl(SSI_GDD_LCH(lch), ssi_ctrl->base,
> > +						SSI_SYS_GDD_MPU_IRQ_STATUS_REG);
> > +
> > +	ssi_outl_and(~NOTFULL(channel), ssi_ctrl->base,
> > +						SSI_SST_BUFSTATE_REG(port));
> > +
> > +

any reason why one blank line isn't enough ?

> > +	ssi_ch->write_data.addr = NULL;
> > +	ssi_ch->write_data.size = 0;
> > +	ssi_ch->write_data.lch = -1;
> > +	clk_disable(ssi_ctrl->ssi_clk);
> > +	clk_disable(ssi_ctrl->ssi_clk);

clk_disable is duplicated.

> > +}
> > +
> > +void ssi_driver_cancel_read_dma(struct ssi_channel *ssi_ch)
> > +{
> > +	int lch = ssi_ch->read_data.lch;
> > +	struct ssi_dev *ssi_ctrl = ssi_ch->ssi_port->ssi_controller;
> > +	unsigned int port = ssi_ch->ssi_port->port_number;
> > +	unsigned int channel = ssi_ch->channel_number;
> > +	u32 reg;
> > +
> > +	if (lch < 0)
> > +		return;
> > +
> > +	clk_enable(ssi_ctrl->ssi_clk);
> > +	reg = ssi_inw(ssi_ctrl->base, SSI_GDD_CCR_REG(lch));
> > +	if (!(reg & SSI_CCR_ENABLE)) {
> > +		dev_dbg(&ssi_ch->dev->device, LOG_NAME "Read cancel on not "
> > +		"enable logical channel %d CCR REG 0x%08X\n", lch, reg);
> > +		clk_disable(ssi_ctrl->ssi_clk);
> > +		return;
> > +	}
> > +
> > +	ssi_outw_and(~SSI_CCR_ENABLE, ssi_ctrl->base, SSI_GDD_CCR_REG(lch));
> > +	ssi_outl_and(~SSI_GDD_LCH(lch), ssi_ctrl->base,
> > +						SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
> > +	ssi_outl(SSI_GDD_LCH(lch), ssi_ctrl->base,
> > +						SSI_SYS_GDD_MPU_IRQ_STATUS_REG);
> > +
> > +	ssi_outl_and(~NOTEMPTY(channel), ssi_ctrl->base,
> > +						SSI_SSR_BUFSTATE_REG(port));
> > +
> > +	ssi_ch->read_data.addr = NULL;
> > +	ssi_ch->read_data.size = 0;
> > +	ssi_ch->read_data.lch = -1;
> > +	clk_disable(ssi_ctrl->ssi_clk);
> > +	clk_disable(ssi_ctrl->ssi_clk);

ditto.

> > +}
> > +
> > +static void dma_read_cb(struct ssi_dev *ctrl, unsigned int port,
> > +							unsigned int channel)

take a few tabs away from the second line.

> > +{
> > +	struct ssi_channel *ch = &ctrl->ssi_port[port - 1].ssi_channel[channel];
> > +
> > +	ch->ops.read_done(ch->dev);
> > +}
> > +
> > +static void dma_write_cb(struct ssi_dev *ctrl, unsigned int port,
> > +							unsigned int channel)

take a few tabs away from the second line.

> > +{
> > +	struct ssi_channel *ch = &ctrl->ssi_port[port - 1].ssi_channel[channel];
> > +
> > +	ch->ops.write_done(ch->dev);
> > +}
> > +
> > +static void do_gdd_lch(struct ssi_dev *ssi_ctrl, unsigned int gdd_lch)
> > +{
> > +	void __iomem *base = ssi_ctrl->base;
> > +	unsigned int port;
> > +	unsigned int channel;
> > +	u32 sync;
> > +	u32 gdd_csr;
> > +	dma_addr_t dma_h;
> > +	size_t size;
> > +
> > +	sync = ssi_inw(base, SSI_GDD_CCR_REG(gdd_lch)) & SSI_CCR_SYNC_MASK;
> > +	port = get_sync_port(sync);
> > +	channel = get_sync_channel(sync);
> > +
> > +	spin_lock(&ssi_ctrl->lock);
> > +
> > +	ssi_outl_and(~SSI_GDD_LCH(gdd_lch), base,
> > +						SSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
> > +	gdd_csr = ssi_inw(base, SSI_GDD_CSR_REG(gdd_lch));
> > +
> > +	if (!(gdd_csr & SSI_CSR_TOUR)) {
> > +		if (get_sync_type(sync) == SSI_SYNC_READ) {
> > +			dma_h = ssi_inl(base, SSI_GDD_CDSA_REG(gdd_lch));
> > +			size = ssi_inw(base, SSI_GDD_CEN_REG(gdd_lch)) * 4;
> > +			dma_sync_single(NULL, dma_h, size, DMA_FROM_DEVICE);
> > +			rst_ch_read(ssi_ctrl, port, channel);
> > +			spin_unlock(&ssi_ctrl->lock);
> > +			dma_read_cb(ssi_ctrl, port, channel);
> > +		} else {
> > +			rst_ch_write(ssi_ctrl, port, channel);
> > +			spin_unlock(&ssi_ctrl->lock);
> > +			dma_write_cb(ssi_ctrl, port, channel);
> > +		}
> > +	} else {
> > +		dev_err(&ssi_ctrl->pdev->dev, "Error  on GDD transfer "
> > +				"on gdd channel %d port %d channel %d\n",
> > +						gdd_lch, port, channel);
> > +		spin_unlock(&ssi_ctrl->lock);
> > +		ssi_port_event_handler(&ssi_ctrl->ssi_port[port - 1],
> > +							SSI_EVENT_ERROR, NULL);
> > +	}
> > +}
> > +
> > +void do_ssi_gdd_tasklet(unsigned long device)
> > +{
> > +	struct ssi_dev *ssi_ctrl = (struct ssi_dev *)device;
> > +	void __iomem *base = ssi_ctrl->base;
> > +	unsigned int gdd_lch = 0;
> > +	u32 status_reg = 0;
> > +	u32 lch_served = 0;
> > +
> > +	clk_enable(ssi_ctrl->ssi_clk);
> > +
> > +	status_reg = ssi_inl(base, SSI_SYS_GDD_MPU_IRQ_STATUS_REG);
> > +
> > +	for (gdd_lch = 0; gdd_lch < SSI_NUM_LCH; gdd_lch++) {
> > +		if (status_reg & SSI_GDD_LCH(gdd_lch)) {
> > +			do_gdd_lch(ssi_ctrl, gdd_lch);
> > +			lch_served |= SSI_GDD_LCH(gdd_lch);
> > +			clk_disable(ssi_ctrl->ssi_clk);

clk_disable() will be called as many times as this loop executes and the
if condition is met. Fix it.

> > +		}
> > +	}
> > +
> > +	ssi_outl(lch_served, base, SSI_SYS_GDD_MPU_IRQ_STATUS_REG);
> > +	clk_disable(ssi_ctrl->ssi_clk);
> > +
> > +	enable_irq(ssi_ctrl->gdd_irq);
> > +}
> > +
> > +irqreturn_t ssi_gdd_mpu_handler(int irq, void *ssi_controller)
> > +{
> > +	struct ssi_dev *ssi_ctrl = (struct ssi_dev *)ssi_controller;
> > +
> > +	tasklet_hi_schedule(&ssi_ctrl->ssi_gdd_tasklet);
> > +	disable_irq_nosync(ssi_ctrl->gdd_irq);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > diff --git a/drivers/misc/ssi/ssi_driver_if.c b/drivers/misc/ssi/ssi_driver_if.c
> > new file mode 100644
> > index 0000000..385467e
> > --- /dev/null
> > +++ b/drivers/misc/ssi/ssi_driver_if.c
> > @@ -0,0 +1,335 @@
> > +/*
> > + * ssi_driver_if.c
> > + *
> > + * Implements SSI hardware driver interfaces for the upper layers.
> > + *
> > + * Copyright (C) 2007-2008 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 "ssi_driver.h"
> > +
> > +/**
> > + * ssi_open - open a ssi device channel.
> > + * @dev - Reference to the ssi device channel to be openned.
> > + *
> > + * Returns 0 on success, -EINVAL on bad parameters, -EBUSY if is already opened.
> > + */
> > +int ssi_open(struct ssi_device *dev)
> > +{
> > +	struct ssi_channel *ch;
> > +	struct ssi_port *port;
> > +	struct ssi_dev *ssi_ctrl;
> > +
> > +	if (!dev || !dev->ch) {
> > +		pr_err(LOG_NAME "Wrong SSI device %p\n", dev);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ch = dev->ch;
> > +	if (!ch->ops.read_done || !ch->ops.write_done) {
> > +		dev_err(&dev->device, "Trying to open with no callbacks "
> > +								"registered\n");
> > +		return -EINVAL;
> > +	}
> > +	port = ch->ssi_port;
> > +	ssi_ctrl = port->ssi_controller;
> > +	spin_lock_bh(&ssi_ctrl->lock);
> > +	if (ch->flags & SSI_CH_OPEN) {
> > +		dev_err(&dev->device, "Port %d Channel %d already OPENED\n",
> > +							dev->n_p, dev->n_ch);
> > +		spin_unlock_bh(&ssi_ctrl->lock);
> > +		return -EBUSY;
> > +	}
> > +	clk_enable(ssi_ctrl->ssi_clk);
> > +	ch->flags |= SSI_CH_OPEN;
> > +	ssi_outl_or(SSI_ERROROCCURED | SSI_BREAKDETECTED, ssi_ctrl->base,
> > +		SSI_SYS_MPU_ENABLE_REG(port->port_number, port->n_irq));
> > +	clk_disable(ssi_ctrl->ssi_clk);
> > +	spin_unlock_bh(&ssi_ctrl->lock);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL(ssi_open);
> > +
> > +/**
> > + * ssi_write - write data into the ssi device channel
> > + * @dev - reference to the ssi device channel  to write into.
> > + * @data - pointer to a 32-bit word data to be written.
> > + * @count - number of 32-bit word to be written.
> > + *
> > + * Return 0 on sucess, a negative value on failure.
> > + * A success values only indicates that the request has been accepted.

plural or singular ??

> > + * Transfer is only completed when the write_done callback is called.
> > + *
> > + */
> > +int ssi_write(struct ssi_device *dev, u32 *data, unsigned int count)
> > +{
> > +	struct ssi_channel *ch;
> > +	int err;
> > +
> > +	if (unlikely(!dev || !dev->ch || !data || (count <= 0))) {

if someone doesn't meet this conditions they deserve to oops I'd say.
It would avoid badly written drivers.

> > +		dev_err(&dev->device, "Wrong paramenters "
> > +			"ssi_device %p data %p count %d", dev, data, count);
> > +		return -EINVAL;
> > +	}
> > +	if (unlikely(!(dev->ch->flags & SSI_CH_OPEN))) {
> > +		dev_err(&dev->device, "SSI device NOT open\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	ch = dev->ch;
> > +	spin_lock_bh(&ch->ssi_port->ssi_controller->lock);
> > +	ch->write_data.addr = data;
> > +	ch->write_data.size = count;

how about matching the names ??

> > +
> > +	if (count == 1)
> > +		err = ssi_driver_write_interrupt(ch, data);
> > +	else
> > +		err = ssi_driver_write_dma(ch, data, count);
> > +
> > +	if (unlikely(err < 0)) {
> > +		ch->write_data.addr = NULL;
> > +		ch->write_data.size = 0;
> > +	}
> > +	spin_unlock_bh(&ch->ssi_port->ssi_controller->lock);
> > +
> > +	return err;
> > +
> > +}
> > +EXPORT_SYMBOL(ssi_write);
> > +
> > +/**
> > + * ssi_read - read data from the ssi device channel
> > + * @dev - ssi device channel reference to read data from.
> > + * @data - pointer to a 32-bit word data to store the data.
> > + * @count - number of 32-bit word to be stored.
> > + *
> > + * Return 0 on sucess, a negative value on failure.
> > + * A success values only indicates that the request has been accepted.
> > + * Data is only available in the buffer when the read_done callback is called.
> > + *
> > + */
> > +int ssi_read(struct ssi_device *dev, u32 *data, unsigned int count)
> > +{
> > +	struct ssi_channel *ch;
> > +	int err;
> > +
> > +	if (unlikely(!dev || !dev->ch || !data || (count <= 0))) {

ditto.

> > +		dev_err(&dev->device, "Wrong paramenters "
> > +			"ssi_device %p data %p count %d", dev, data, count);
> > +		return -EINVAL;
> > +	}
> > +	if (unlikely(!(dev->ch->flags & SSI_CH_OPEN))) {
> > +		dev_err(&dev->device, "SSI device NOT open\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	ch = dev->ch;
> > +	spin_lock_bh(&ch->ssi_port->ssi_controller->lock);
> > +	ch->read_data.addr = data;
> > +	ch->read_data.size = count;

how about matching the names ??

> > +
> > +	if (count == 1)
> > +		err = ssi_driver_read_interrupt(ch, data);
> > +	else
> > +		err = ssi_driver_read_dma(ch, data, count);
> > +
> > +	if (unlikely(err < 0)) {
> > +		ch->read_data.addr = NULL;
> > +		ch->read_data.size = 0;
> > +	}
> > +	spin_unlock_bh(&ch->ssi_port->ssi_controller->lock);
> > +
> > +	return err;
> > +}
> > +EXPORT_SYMBOL(ssi_read);
> > +
> > +void __ssi_write_cancel(struct ssi_channel *ch)
> > +{
> > +	if (ch->write_data.size == 1)
> > +		ssi_driver_cancel_write_interrupt(ch);
> > +	else if (ch->write_data.size > 1)
> > +		ssi_driver_cancel_write_dma(ch);
> > +
> > +}
> > +/**
> > + * ssi_write_cancel - Cancel pending write request.
> > + * @dev - ssi device channel where to cancel the pending write.
> > + *
> > + * write_done() callback will not be called after sucess of this function.
> > + */
> > +void ssi_write_cancel(struct ssi_device *dev)
> > +{
> > +	if (unlikely(!dev || !dev->ch)) {
> > +		pr_err(LOG_NAME "Wrong SSI device %p\n", dev);
> > +		return;
> > +	}
> > +	if (unlikely(!(dev->ch->flags & SSI_CH_OPEN))) {
> > +		dev_err(&dev->device, "SSI device NOT open\n");
> > +		return;
> > +	}
> > +
> > +	spin_lock_bh(&dev->ch->ssi_port->ssi_controller->lock);
> > +	__ssi_write_cancel(dev->ch);
> > +	spin_unlock_bh(&dev->ch->ssi_port->ssi_controller->lock);
> > +}
> > +EXPORT_SYMBOL(ssi_write_cancel);
> > +
> > +void __ssi_read_cancel(struct ssi_channel *ch)
> > +{
> > +	if (ch->read_data.size == 1)
> > +		ssi_driver_cancel_read_interrupt(ch);
> > +	else if (ch->read_data.size > 1)
> > +		ssi_driver_cancel_read_dma(ch);
> > +}
> > +
> > +/**
> > + * ssi_read_cancel - Cancel pending read request.
> > + * @dev - ssi device channel where to cancel the pending read.
> > + *
> > + * read_done() callback will not be called after sucess of this function.
> > + */
> > +void ssi_read_cancel(struct ssi_device *dev)
> > +{
> > +	if (unlikely(!dev || !dev->ch)) {
> > +		pr_err(LOG_NAME "Wrong SSI device %p\n", dev);
> > +		return;
> > +	}
> > +
> > +	if (unlikely(!(dev->ch->flags & SSI_CH_OPEN))) {
> > +		dev_err(&dev->device, "SSI device NOT open\n");
> > +		return;
> > +	}
> > +
> > +	spin_lock_bh(&dev->ch->ssi_port->ssi_controller->lock);
> > +	__ssi_read_cancel(dev->ch);
> > +	spin_unlock_bh(&dev->ch->ssi_port->ssi_controller->lock);
> > +
> > +}
> > +EXPORT_SYMBOL(ssi_read_cancel);
> > +
> > +/**
> > + * ssi_ioctl - SSI I/O control
> > + * @dev - ssi device channel reference to apply the I/O control
> > + * 						(or port associated to it)
       ^ trailing whitespace and you can remove a few tabs from the
second line

> > + * @command - SSI I/O control command
> > + * @arg - parameter associated to the control command. NULL, if no parameter.
> > + *
> > + * Return 0 on sucess, a negative value on failure.
> > + *
> > + */
> > +int ssi_ioctl(struct ssi_device *dev, unsigned int command, void *arg)
> > +{
> > +	struct ssi_channel *ch;
> > +	struct ssi_dev *ssi_ctrl;
> > +	void __iomem *base;
> > +	unsigned int port, channel;
> > +	u32 wake;
> > +	int err = 0;
> > +
> > +	if (unlikely((!dev) ||
> > +		(!dev->ch) ||
> > +		(!dev->ch->ssi_port) ||
> > +		(!dev->ch->ssi_port->ssi_controller)) ||
> > +		(!(dev->ch->flags & SSI_CH_OPEN))) {
> > +		pr_err(LOG_NAME "SSI IOCTL Invalid parameter\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +

remove one blank line.

> > +	ch = dev->ch;
> > +	ssi_ctrl = ch->ssi_port->ssi_controller;
> > +	port = ch->ssi_port->port_number;
> > +	channel = ch->channel_number;
> > +	base = ssi_ctrl->base;
> > +	clk_enable(ssi_ctrl->ssi_clk);
> > +
> > +	switch (command) {
> > +	case SSI_IOCTL_WAKE_UP:
> > +		/* We only claim once the wake line per channel */
> > +		wake = ssi_inl(base, SSI_SYS_WAKE_REG(port));
> > +		if (!(wake & SSI_WAKE(channel))) {
> > +			clk_enable(ssi_ctrl->ssi_clk);
> > +			ssi_outl(SSI_WAKE(channel), base,
> > +					SSI_SYS_SET_WAKE_REG(port));
> > +		}
> > +		break;
> > +	case SSI_IOCTL_WAKE_DOWN:
> > +		wake = ssi_inl(base, SSI_SYS_WAKE_REG(port));
> > +		if ((wake & SSI_WAKE(channel))) {
> > +			ssi_outl(SSI_WAKE(channel), base,
> > +						SSI_SYS_CLEAR_WAKE_REG(port));
> > +			clk_disable(ssi_ctrl->ssi_clk);
> > +		}
> > +		break;
> > +	case SSI_IOCTL_SEND_BREAK:
> > +		ssi_outl(1, base, SSI_SST_BREAK_REG(port));
> > +		break;
> > +	case SSI_IOCTL_WAKE:
> > +		if (arg == NULL)
> > +			err = -EINVAL;
> > +		else
> > +			*(u32 *)arg = ssi_inl(base, SSI_SYS_WAKE_REG(port));
> > +		break;
> > +	default:
> > +		err = -ENOIOCTLCMD;
> > +		break;
> > +	}
> > +
> > +	clk_disable(ssi_ctrl->ssi_clk);
> > +
> > +	return err;
> > +}
> > +EXPORT_SYMBOL(ssi_ioctl);
> > +
> > +/**
> > + * ssi_close - close given ssi device channel
> > + * @dev - reference to ssi device channel.
> > + */
> > +void ssi_close(struct ssi_device *dev)
> > +{
> > +	if (!dev || !dev->ch) {
> > +		pr_err(LOG_NAME "Trying to close wrong SSI device %p\n", dev);
> > +		return;
> > +	}
> > +
> > +	spin_lock_bh(&dev->ch->ssi_port->ssi_controller->lock);
> > +	if (dev->ch->flags & SSI_CH_OPEN) {
> > +		dev->ch->flags &= ~SSI_CH_OPEN;
> > +		__ssi_write_cancel(dev->ch);
> > +		__ssi_read_cancel(dev->ch);
> > +	}
> > +	spin_unlock_bh(&dev->ch->ssi_port->ssi_controller->lock);
> > +
> > +}
> > +EXPORT_SYMBOL(ssi_close);
> > +
> > +/**
> > + * ssi_dev_set_cb - register read_done() and write_done() callbacks.
> > + * @dev - reference to ssi device channel where callbacks are associated.
> > + * @r_cb - callback to signal read transfer completed.
> > + * @w_cb - callback to signal write transfer completed.

I'd call them read and write. Or maybe have only one 'complete' and it
handles read and write inside itself, would have to think a bit more
about this.

> > + */
> > +void ssi_dev_set_cb(struct ssi_device *dev, void (*r_cb)(struct ssi_device *dev)
> > +					, void (*w_cb)(struct ssi_device *dev))
> > +{
> > +	dev->ch->ops.read_done = r_cb;
> > +	dev->ch->ops.write_done = w_cb;
> > +}
> > +EXPORT_SYMBOL(ssi_dev_set_cb);
> > diff --git a/drivers/misc/ssi/ssi_driver_int.c b/drivers/misc/ssi/ssi_driver_int.c
> > new file mode 100644
> > index 0000000..6491e48
> > --- /dev/null
> > +++ b/drivers/misc/ssi/ssi_driver_int.c
> > @@ -0,0 +1,232 @@
> > +/*
> > + * ssi_driver_int.c
> > + *
> > + * Implements SSI interrupt functionality.
> > + *
> > + * Copyright (C) 2007-2008 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 "ssi_driver.h"
> > +
> > +static void reset_ch_read(struct ssi_channel *ch)
> > +{
> > +	ch->read_data.addr = NULL;
> > +	ch->read_data.size = 0;
> > +	ch->read_data.lch = -1;
> > +}
> > +
> > +static void reset_ch_write(struct ssi_channel *ch)
> > +{
> > +	ch->write_data.addr = NULL;
> > +	ch->write_data.size = 0;
> > +	ch->write_data.lch = -1;
> > +}
> > +
> > +int ssi_driver_write_interrupt(struct ssi_channel *ch, u32 *data)
> > +{
> > +	struct ssi_port *p = ch->ssi_port;
> > +	unsigned int port = p->port_number;
> > +	unsigned int channel = ch->channel_number;
> > +
> > +	clk_enable(p->ssi_controller->ssi_clk);
> > +	ssi_outl_or(SSI_SST_DATAACCEPT(channel), p->ssi_controller->base,
> > +					SSI_SYS_MPU_ENABLE_REG(port, p->n_irq));
> > +
> > +

one blank line is enough.

> > +	return 0;
> > +}
> > +
> > +int ssi_driver_read_interrupt(struct ssi_channel *ch, u32 *data)
> > +{
> > +	struct ssi_port *p = ch->ssi_port;
> > +	unsigned int port = p->port_number;
> > +	unsigned int channel = ch->channel_number;
> > +
> > +	clk_enable(p->ssi_controller->ssi_clk);
> > +
> > +	ssi_outl_or(SSI_SSR_DATAAVAILABLE(channel), p->ssi_controller->base,
> > +					SSI_SYS_MPU_ENABLE_REG(port, p->n_irq));
> > +
> > +	clk_disable(p->ssi_controller->ssi_clk);
> > +
> > +	return 0;
> > +}
> > +
> > +void ssi_driver_cancel_write_interrupt(struct ssi_channel *ch)
> > +{
> > +	struct ssi_port *p = ch->ssi_port;
> > +	unsigned int port = p->port_number;
> > +	unsigned int channel = ch->channel_number;
> > +	void __iomem *base = p->ssi_controller->base;
> > +	u32 enable;
> > +
> > +	clk_enable(p->ssi_controller->ssi_clk);
> > +
> > +	enable = ssi_inl(base, SSI_SYS_MPU_ENABLE_REG(port, p->n_irq));
> > +	if (!(enable & SSI_SST_DATAACCEPT(channel))) {
> > +		dev_dbg(&ch->dev->device, LOG_NAME "Write cancel on not "
> > +		"enabled channel %d ENABLE REG 0x%08X", channel, enable);
> > +		clk_disable(p->ssi_controller->ssi_clk);
> > +		return;
> > +	}
> > +	ssi_outl_and(~SSI_SST_DATAACCEPT(channel), base,
> > +				SSI_SYS_MPU_ENABLE_REG(port, p->n_irq));
> > +	ssi_outl_and(~NOTFULL(channel), base, SSI_SST_BUFSTATE_REG(port));
> > +	reset_ch_write(ch);
> > +
> > +	clk_disable(p->ssi_controller->ssi_clk);
> > +	clk_disable(p->ssi_controller->ssi_clk);
> > +

remove this extra line

> > +}
> > +
> > +void ssi_driver_cancel_read_interrupt(struct ssi_channel *ch)
> > +{
> > +	struct ssi_port *p = ch->ssi_port;
> > +	unsigned int port = p->port_number;
> > +	unsigned int channel = ch->channel_number;
> > +	void __iomem *base = p->ssi_controller->base;
> > +
> > +	clk_enable(p->ssi_controller->ssi_clk);
> > +
> > +	ssi_outl_and(~SSI_SSR_DATAAVAILABLE(channel), base,
> > +					SSI_SYS_MPU_ENABLE_REG(port, p->n_irq));
> > +	ssi_outl_and(~NOTEMPTY(channel), base, SSI_SSR_BUFSTATE_REG(port));
> > +	reset_ch_read(ch);
> > +
> > +	clk_disable(p->ssi_controller->ssi_clk);
> > +}
> > +
> > +static void do_channel_tx(struct ssi_channel *ch)
> > +{
> > +	struct ssi_dev *ssi_ctrl = ch->ssi_port->ssi_controller;
> > +	void __iomem *base = ssi_ctrl->base;
> > +	unsigned int n_ch;
> > +	unsigned int n_p;
> > +	unsigned int irq;
> > +
> > +	n_ch = ch->channel_number;
> > +	n_p = ch->ssi_port->port_number;
> > +	irq = ch->ssi_port->n_irq;
> > +
> > +	spin_lock(&ssi_ctrl->lock);
> > +
> > +	if (ch->write_data.addr == NULL) {
> > +		ssi_outl_and(~SSI_SST_DATAACCEPT(n_ch), base,
> > +					SSI_SYS_MPU_ENABLE_REG(n_p, irq));
> > +		reset_ch_write(ch);
> > +		spin_unlock(&ssi_ctrl->lock);
> > +		clk_disable(ssi_ctrl->ssi_clk);
> > +		(*ch->ops.write_done)(ch->dev);
> > +	} else {
> > +		ssi_outl(*(ch->write_data.addr), base,
> > +					SSI_SST_BUFFER_CH_REG(n_p, n_ch));
> > +		ch->write_data.addr = NULL;
> > +		spin_unlock(&ssi_ctrl->lock);
> > +	}
> > +}
> > +
> > +static void do_channel_rx(struct ssi_channel *ch)
> > +{
> > +	struct ssi_dev *ssi_ctrl = ch->ssi_port->ssi_controller;
> > +	void __iomem *base = ch->ssi_port->ssi_controller->base;
> > +	unsigned int n_ch;
> > +	unsigned int n_p;
> > +	unsigned int irq;
> > +
> > +	n_ch = ch->channel_number;
> > +	n_p = ch->ssi_port->port_number;
> > +	irq = ch->ssi_port->n_irq;
> > +
> > +	spin_lock(&ssi_ctrl->lock);
> > +
> > +	*(ch->read_data.addr) = ssi_inl(base, SSI_SSR_BUFFER_CH_REG(n_p, n_ch));
> > +
> > +	ssi_outl_and(~SSI_SSR_DATAAVAILABLE(n_ch), base,
> > +					SSI_SYS_MPU_ENABLE_REG(n_p, irq));
> > +	reset_ch_read(ch);
> > +
> > +	spin_unlock(&ssi_ctrl->lock);
> > +
> > +	(*ch->ops.read_done)(ch->dev);
> > +}
> > +
> > +void do_ssi_tasklet(unsigned long ssi_port)
> > +{
> > +	struct ssi_port *pport = (struct ssi_port *)ssi_port;
> > +	struct ssi_dev *ssi_ctrl = pport->ssi_controller;
> > +	void __iomem *base = ssi_ctrl->base;
> > +	unsigned int port = pport->port_number;
> > +	unsigned int channel = 0;
> > +	unsigned int irq = pport->n_irq;
> > +	u32 status_reg;
> > +	u32 enable_reg;
> > +	u32 ssr_err_reg;
> > +	u32 channels_served;
> > +
> > +	clk_enable(ssi_ctrl->ssi_clk);
> > +
> > +	channels_served = 0;
> > +	status_reg = ssi_inl(base, SSI_SYS_MPU_STATUS_REG(port, irq));
> > +	enable_reg = ssi_inl(base, SSI_SYS_MPU_ENABLE_REG(port, irq));
> > +
> > +	for (channel = 0; channel < pport->max_ch; channel++) {
> > +		if ((status_reg & SSI_SST_DATAACCEPT(channel)) &&
> > +		    (enable_reg & SSI_SST_DATAACCEPT(channel))) {
> > +			do_channel_tx(&pport->ssi_channel[channel]);
> > +			channels_served |= SSI_SST_DATAACCEPT(channel);
> > +		}
> > +
> > +		if ((status_reg & SSI_SSR_DATAAVAILABLE(channel)) &&
> > +		    (enable_reg & SSI_SSR_DATAAVAILABLE(channel))) {
> > +			do_channel_rx(&pport->ssi_channel[channel]);
> > +			channels_served |= SSI_SSR_DATAAVAILABLE(channel);
> > +		}
> > +	}
> > +
> > +	if ((status_reg & SSI_BREAKDETECTED) &&
> > +	    (enable_reg & SSI_BREAKDETECTED)) {
> > +		dev_info(&ssi_ctrl->pdev->dev,
> > +					"Hardware BREAK on port %d\n", port);
> > +		ssi_outl(0, base, SSI_SSR_BREAK_REG(port));
> > +		ssi_port_event_handler(pport, SSI_EVENT_BREAK_DETECTED, NULL);
> > +	}
> > +
> > +	if (status_reg & SSI_ERROROCCURED) {
> > +		ssr_err_reg = ssi_inl(base, SSI_SSR_ERROR_REG(port));
> > +		dev_err(&ssi_ctrl->pdev->dev, "SSI ERROR Port %d: 0x%02x\n",
> > +							port, ssr_err_reg);
> > +		ssi_outl(ssr_err_reg, base, SSI_SSR_ERRORACK_REG(port));
> > +		ssi_port_event_handler(pport, SSI_EVENT_ERROR, NULL);
> > +	}
> > +
> > +	ssi_outl((channels_served | SSI_ERROROCCURED | SSI_BREAKDETECTED), base,
> > +					SSI_SYS_MPU_STATUS_REG(port, irq));
> > +
> > +	clk_disable(ssi_ctrl->ssi_clk);
> > +	enable_irq(pport->irq);
> > +}

I might have more comments on this later. But I'll wait until you come
with the new version and these comments fixed to look at it again.

-- 
balbi

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

* Re: [RFC][PATCH 5/5] OMAP SSI API documentation
  2008-10-03 11:52         ` [RFC][PATCH 5/5] OMAP SSI API documentation Carlos Chinea
@ 2008-10-09 16:47           ` Felipe Balbi
  0 siblings, 0 replies; 14+ messages in thread
From: Felipe Balbi @ 2008-10-09 16:47 UTC (permalink / raw)
  To: Carlos Chinea; +Cc: linux-kernel, linux-omap

On Fri, Oct 03, 2008 at 02:52:30PM +0300, Carlos Chinea wrote:

there are issues in the documentation as well.

> Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
> ---
>  Documentation/arm/OMAP/ssi/board-ssi.c.example |  216 ++++++++++++++++++++++
>  Documentation/arm/OMAP/ssi/ssi                 |  232 ++++++++++++++++++++++++
>  2 files changed, 448 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/arm/OMAP/ssi/board-ssi.c.example
>  create mode 100644 Documentation/arm/OMAP/ssi/ssi
> 
> diff --git a/Documentation/arm/OMAP/ssi/board-ssi.c.example b/Documentation/arm/OMAP/ssi/board-ssi.c.example
> new file mode 100644
> index 0000000..a346628
> --- /dev/null
> +++ b/Documentation/arm/OMAP/ssi/board-ssi.c.example
> @@ -0,0 +1,216 @@
> +/*
> + * board-ssi.c.example
> + *
> + * Copyright (C) 2007-2008 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
> + */
> +
> +#ifdef CONFIG_OMAP_SSI

don't ifdef here. This file should only be built if SSI is selected.

> +
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/ssi_driver_if.h>
> +#include <mach/ssi/ssi_sys_reg.h>
> +#include <mach/ssi/ssi_ssr_reg.h>
> +#include <mach/ssi/ssi_sst_reg.h>

you see how many includes you need to make it work ?

It should be #include <linux/ssi.h> and that's all. This driver can be
generic enough to build and work on non-omap platforms, can't it ?

> +
> +#include "clock.h"
> +
> +struct ssi_internal_clk {
> +	struct clk clk;
> +	struct clk **childs;
> +	int n_childs;
> +	struct platform_device *pdev;

why do you need to let the internal clock know so much about the user ?
I think struct device * should be enough here.

> +};
> +
> +static struct ssi_internal_clk ssi_clock;
> +
> +static void ssi_pdev_release(struct device *dev)
> +{
> +}
> +
> +static struct ssi_port_pd ssi_ports[] = {
> +	[0] = {
> +		.tx_mode = SSI_MODE_FRAME,
> +		.tx_frame_size = SSI_FRAMESIZE_DEFAULT,
> +		.divisor = SSI_DIVISOR_DEFAULT,
> +		.tx_ch = SSI_CHANNELS_DEFAULT,
> +		.arb_mode = SSI_ARBMODE_ROUNDROBIN,
> +		.rx_mode = SSI_MODE_FRAME,
> +		.rx_frame_size = SSI_FRAMESIZE_DEFAULT,
> +		.rx_ch = SSI_CHANNELS_DEFAULT,
> +		.timeout = SSI_TIMEOUT_DEFAULT,
> +		.n_irq = 0,
> +		},

tabify these.

> +};
> +
> +static struct ssi_platform_data ssi_p_d = {
> +	.clk_name = "ssi_clk",
> +	.num_ports = ARRAY_SIZE(ssi_ports),
> +	.ports = ssi_ports,

tabify

> +};
> +
> +static struct resource ssi_resources[] = {
> +	[0] = {
> +		.start = SSI_IOMEM_BASE_ADDR,
> +		.end = SSI_IOMEM_BASE_ADDR + SSI_IOMEM_SIZE,
> +		.name = SSI_IOMEM_NAME,
> +		.flags = IORESOURCE_MEM,
> +		},

tabify and the closing bracket should be aligned with [0]

> +	[1] = 	{
	     ^  trailing whitespace

> +		.start = SSI_P1_MPU_IRQ0,
> +		.end = SSI_P1_MPU_IRQ0,
> +		.name = SSI_P1_MPU_IRQ0_NAME,
> +		.flags = IORESOURCE_IRQ,
> +		},

tabify
tabify and the closing bracket should be aligned with [1]

> +	[2] = 	{
	     ^  trailing whitespace

> +		.start = SSI_P1_MPU_IRQ1,
> +		.end = SSI_P1_MPU_IRQ1,
> +		.name = SSI_P1_MPU_IRQ1_NAME,
> +		.flags = IORESOURCE_IRQ,
> +		},

tabify
tabify and the closing bracket should be aligned with [2]

> +	[3] = 	{
	     ^  trailing whitespace

> +		.start = SSI_P2_MPU_IRQ0,
> +		.end = SSI_P2_MPU_IRQ0,
> +		.name = SSI_P2_MPU_IRQ0_NAME,
> +		.flags = IORESOURCE_IRQ,
> +		},

tabify
tabify and the closing bracket should be aligned with [3]

> +	[4] = 	{
	     ^  trailing whitespace

> +		.start = SSI_P2_MPU_IRQ1,
> +		.end = SSI_P2_MPU_IRQ1,
> +		.name = SSI_P2_MPU_IRQ1_NAME,
> +		.flags = IORESOURCE_IRQ,
> +		},

tabify
tabify and the closing bracket should be aligned with [4]

> +	[5] = 	{
	     ^  trailing whitespace

> +		.start = SSI_GDD_MPU_IRQ,
> +		.end = SSI_GDD_MPU_IRQ,
> +		.name = SSI_GDD_MPU_IRQ_NAME,
> +		.flags = IORESOURCE_IRQ,
> +		},

tabify and the closing bracket should be aligned with [5]

> +};
> +
> +static struct platform_device ssi_pdev = {
> +	.name = "omap_ssi",
> +	.id = -1,
> +	.num_resources = ARRAY_SIZE(ssi_resources),
> +	.resource = ssi_resources,
> +	.dev = 	{
	      ^  trailing whitespace

> +		.release = ssi_pdev_release,
> +		.platform_data = &ssi_p_d,
> +		},

this bracket should be aligned with .dev

> +};
> +
> +static void set_ssi_mode(struct platform_device *pdev, u32 mode)
> +{
> +	void __iomem *base = (void __iomem *)pdev->resource[0].start;

this is wrong, looks like mode sould be passed down to the driver and
the driver would set_ssi_mode().

> +	int port;
> +	int num_ports = ((struct ssi_platform_data *)

the cast is unnecessary and you're abusing platform_data, I'd say.

> +					(pdev->dev.platform_data))->num_ports;
> +
> +	for (port = 0; port < num_ports; port++) {
> +		outl(mode, OMAP2_IO_ADDRESS(base + SSI_SST_MODE_REG(port)));
> +		outl(mode, OMAP2_IO_ADDRESS(base + SSI_SSR_MODE_REG(port)));
> +	}
> +}
> +
> +static int ssi_clk_init(struct ssi_internal_clk *ssi_clk)
> +{
> +	const char *clk_names[] = { "ssi_ick", "ssi_ssr_fck" };
> +	int i;
> +	int j;
> +
> +	ssi_clk->n_childs = ARRAY_SIZE(clk_names);
> +	ssi_clk->childs = kzalloc(ssi_clk->n_childs * sizeof(*ssi_clk->childs),
> +								GFP_KERNEL);
> +	if (!ssi_clk->childs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ssi_clk->n_childs; i++) {
> +		ssi_clk->childs[i] = clk_get(NULL, clk_names[i]);
> +		if (IS_ERR(ssi_clk->childs[i])) {
> +			pr_err("Unable to get SSI clock: %s", clk_names[i]);
> +			for (j = i - 1; j >= 0; j--)
> +				clk_put(ssi_clk->childs[j]);
> +			return -ENODEV;
> +		}
> +	}

this looks like it should be done by the driver and not board-specific.

> +
> +	return 0;
> +}
> +
> +static int ssi_clk_enable(struct clk *clk)
> +{
> +	struct ssi_internal_clk *ssi_clk =
> +				container_of(clk, struct ssi_internal_clk, clk);
> +	int err = 0;
> +	int i;
> +	int j;
> +
> +	for (i = 0; ((i < ssi_clk->n_childs) && (err >= 0)); i++)
> +		err = omap2_clk_enable(ssi_clk->childs[i]);
> +
> +	if (unlikely(err < 0)) {
> +		pr_err("Error on SSI clk %d\n", i);
> +		for (j = i - 1; j >= 0; j--)
> +			omap2_clk_disable(ssi_clk->childs[j]);

if you get and error, return already, will avoid the else below.

> +	} else {
> +		if (ssi_clk->clk.usecount == 1)
> +			set_ssi_mode(ssi_clk->pdev, SSI_MODE_FRAME);
> +	}
> +
> +	return err;
> +}
> +
> +static void ssi_clk_disable(struct clk *clk)
> +{
> +	struct ssi_internal_clk *ssi_clk =
> +				container_of(clk, struct ssi_internal_clk, clk);
> +
> +	int i;
> +
> +	if (ssi_clk->clk.usecount == 0)
> +		set_ssi_mode(ssi_clk->pdev, SSI_MODE_SLEEP);
> +
> +	for (i = 0; i < ssi_clk->n_childs; i++)
> +		omap2_clk_disable(ssi_clk->childs[i]);
> +}
> +
> +static struct ssi_internal_clk ssi_clock = {
> +	.clk = {
> +		.name = "ssi_clk",
> +		.id = -1,
> +		.enable = ssi_clk_enable,
> +		.disable = ssi_clk_disable,
> +	},
> +	.pdev = &ssi_pdev,
> +};

it doesn't look like you do anything useful with this ssi_internal_clk
structure. I'd say you can move the clk definition to <mach/clock.h> if
Tony, Paul and Kevin are ok with it.

> +
> +void __init ssi_init(void)
> +{
> +	int err;
> +
> +	ssi_clk_init(&ssi_clock);
> +	clk_register(&ssi_clock.clk);
> +
> +	err = platform_device_register(&ssi_pdev);
> +	if (err < 0)
> +		pr_err("Unable to register SSI platform device: %d\n", err);

if the clk definition can be moved to <mach/clock.h> then you can
simply:

return platform_device_register(&ssi_pdev);

> +}
> +#endif
> diff --git a/Documentation/arm/OMAP/ssi/ssi b/Documentation/arm/OMAP/ssi/ssi
> new file mode 100644
> index 0000000..990ae48
> --- /dev/null
> +++ b/Documentation/arm/OMAP/ssi/ssi

let's use an extension to this file: ssi.txt

> @@ -0,0 +1,232 @@
> +OMAP SSI API's How To
> +=====================
> +
> +The Synchronous Serial Interface (SSI) is a high speed communication interface
> +that is used for connecting OMAP to a cellular modem engine.
> +
> +The SSI interface supports full duplex communication over multiple channels and
> +is capable of reaching speeds up to 110 Mbit/s
> +
> +I OMAP SSI driver API overview
> +-----------------------------
> +
> +A) SSI Bus, SSI channels and protocol drivers overview. 
							  ^ trailing whitespace

> +
> +The OMAP SSI driver is intended to be used inside the kernel by protocol drivers.
> +
> +The OMAP SSI abstracts the concept of SSI channels by creating an SSI bus an

s/an/and (at the end)

> +attaching SSI channel devices to it.(see Figure 1)
				       ^ add a space

> +
> +Protocol drivers will then claim one or more SSI channels, after registering with the OMAP SSI driver.
> +
> +	+---------------------+		+----------------+
> +	+  SSI channel device +		+  SSI protocol  +
> +	+  (omap_ssi.pX-cY)   +	<-------+  driver        +
> +	+---------------------+		+----------------+
> +		|				|
> +(/sys/bus/ssi/devices/omap_ssi.pX-cy)	(/sys/bus/ssi/drivers/ssi_protocol)
> +		|				|
> ++---------------------------------------------------------------+
> ++			SSI bus					+	
> ++---------------------------------------------------------------+	
> +	
> +			Figure 1.
> +
> +(NOTE: omap_ssi.pX-cY represents the SSI channel Y on port X from the omap_ssi
> +device)

you could go simpler and call it:

/sys/bus/ssi/devices/X-Y: (X == port, Y == channel);

> +
> +B) Data transfers
> +
> +The OMAP SSI driver exports an asynchronous interface for sending and receiving
> +data over the SSI channels. Protocol drivers will register a set of read and write
> +completion callbacks for each SSI channel they use.
> +
> +Protocol drivers call ssi_write/ssi_read functions to signal the OMAP SSI driver
> +that is willing to write/read data to/from a channel. Transfers are completed only
> +when the OMAP SSI driver calls the completion callback.
> +
> +An SSI channel can simultaneously have both a read and a write request
> +pending, however, requests cannot be queued.
> +
> +It is safe to call ssi_write/ssi_read functions inside the callbacks functions.
> +In fact, a protocol driver should normally re-issue the read request from within
> +the read callback, in order to not miss any incoming messages.
> +
> +C) Error handling
> +
> +SSI is a multi channel interface but the channels share the same physical wires.
> +Therefore, any transmission error potentially affects all the protocol drivers
> +that sit on top of the SSI driver. Whenever an error occurs, it is broadcasted to
> +all protocol drivers.
> +
> +Errors are signaled to the protocol drivers through the port_event callback.
> +Protocol drivers can avoid receiving those notifications by not setting the
> +SSI_EVENT_ERROR in the event_mask field.(see struct ssi_device_driver)
> +
> +Completion callbacks functions are only called when a transfer has succeed.
> +
> +II OMAP SSI API's
> +-----------------
> +
> +A) Include
> +
> +#include<linux/ssi_driver_if.h>
> +
> +B) int register_ssi_driver(struct ssi_device_driver *driver);
> +
> +Description: Register an SSI protocol driver
> +
> +Parameter: A protocol driver declaration (see struct ssi_device_driver)
> +
> +B) void unregister_ssi_driver(struct ssi_device_driver *driver);
> +
> +Description: Unregister an SSI protocol driver
> +
> +Parameter: A protocol driver declaration (see struct ssi_device_driver)
> +
> +C) int ssi_open(struct ssi_device *dev);
> +
> +Description: Open an SSI device channel
> +
> +Parameter: The SSI channel
> +
> +D) int ssi_write(struct ssi_device *dev, u32 *data, unsigned int count);
> +
> +Description: Send data through an SSI channel. The transfer is only completed
> +when the write_complete callback is called
> +
> +Parameters:
> +	- dev: SSI channel
> +	- data: pointer to the data to send
> +	- count: number of 32-bit words to be sent
> +
> +E) void ssi_write_cancel(struct ssi_device *dev);
> +
> +Description: Cancel current pending write operation
> +
> +Parameters: SSI channel
> +	
> +F) int ssi_read(struct ssi_device *dev, u32 *data, unsigned int w_count);
> +
> +Description: Receive data through an SSI channel. The transfer is only completed
> +when the read_complete callback is called
> +
> +Parameters:
> +	- dev: SSI channel
> +	- data: pointer where to store the data
> +	- count: number of 32-bit words to be read
> +
> +
> +G) void ssi_read_cancel(struct ssi_device *dev);
> +
> +Description: Cancel current pending read operation
> +
> +Parameters: SSI channel
> +
> +H) int ssi_ioctl(struct ssi_device *dev, unsigned int command, void *arg);
> +
> +Description: Apply some control command to the port associated to the given
> +SSI channel
> +
> +Parameters:
> +	- dev: SSI channel
> +	- command: command to execute
> +	- arg: parameter for the control command
> +
> +Commands:
> +	- SSI_IOCTL_WAKE_UP: 
> +		Description: Set SSI wakeup line for the channel
> +		Parameters: None
> +	- SSI_IOCTL_WAKE_DOWN:
> +		Description: Unset SSI wakeup line for the channel
> +		Parameters: None
> +	- SSI_IOCTL_SEND_BREAK:
> +		Description: Send a HW BREAK frame in FRAME mode
> +		Parameters: None
> +	- SSI_IOCTL_WAKE:
> +		Description: Get wakeup line status
> +		Parameters: Pointer to a u32 variable to return result
> +		(Result: 0 means wakeline DOWN, other result means wakeline UP)
> +
> +I)void ssi_close(struct ssi_device *dev);
> +
> +Description: Close an SSI channel
> +
> +Parameters: The SSI channel to close
> +
> +J) void ssi_dev_set_cb(	struct ssi_device *dev,
> +			void (*r_cb)(struct ssi_device *dev),
> +			void (*w_cb)(struct ssi_device *dev));
> +
> +Description: Set the read and write callbacks for the SSI channel. This
> +function is usually called in the probe function of the SSI protocol driver to
> +set completion callbacks for the asynchronous read and write transfer
> +
> +Parameters:
> +	- dev: SSI channel
> +	- r_cb: Pointer to a callback function to signal that a read transfer is
> +		completed
> +	- w_cb: Pointer to a callback function to signal that a write transfer
> +		is completed
> +
> +H) struct ssi_device_driver
> +
> +Description: Protocol drivers pass this struct to the register_ssi_driver function
> +in order to register with the OMAP SSI driver. Among other things it tells the
> +OMAP SSI driver which channels the protocol driver wants to allocate for its use
> +
> +Declaration:
> +struct ssi_device_driver {
> +	unsigned long		ctrl_mask;
> +	unsigned long		ch_mask[SSI_MAX_PORTS];
> +	unsigned long		event_mask;
> +	void 			(*port_event) (int c_id, unsigned int port,
> +						unsigned int event, void *arg);
> +	int			(*probe)(struct ssi_device *dev);
> +	int			(*remove)(struct ssi_device *dev);
> +	int			(*suspend)(struct ssi_device *dev,
> +						pm_message_t mesg);
> +	int			(*resume)(struct ssi_device *dev);
> +	struct device_driver 	driver;
> +};
> +
> +Fields description:
> +	ctrl_mask: SSI block ids to use
> +	ch_mask[SSI_MAX_PORTS]: SSI channels to use
> +	event_mask: SSI events to be notified
> +	port_event: Function callback for notifying SSI events
> +		   (i.e.: error transfer)
> +		Parameters:
> +			c_id: SSI Block id which generate the event
> +			port: Port number which generate the event
> +			event: Event code
> +	probe: Probe function
> +		Parameters: SSI channel
> +	remove: Remove function
> +		Parameters: SSI channel
> +
> +Example:
> +
> +static struct ssi_device_driver ssi_protocol_driver = {
> +	.ctrl_mask = ANY_SSI_CONTROLLER,
> +	.ch_mask[0] = CHANNEL(0) | CHANNEL(1),
> +	.event_mask = SSI_EVENT_ERROR_MASK,
> +	.port_event = port_event_callback,
> +	.probe = ssi_proto_probe,
> +	.remove = __devexit_p(ssi_proto_remove),
> +	.driver = {
> +			.name = "ssi_protocol",
> +	},
> +};
> +
> +
> +III OMAP SSI platform_device
> +----------------------------
> +
> +You can find a example of how to define an SSI platform device in:
> +
> +Documentation/arm/OMAP/ssi/board-ssi.c.example
> +
> +=================================================
> +Contact: Carlos Chinea <carlos.chinea@nokia.com>
> +Copyright (C) 2008 Nokia Corporation.
> -- 
> 1.5.3.6
> 
> --
> 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

-- 
balbi

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

end of thread, other threads:[~2008-10-09 16:47 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-10-03 11:50 [RFC][PATCH 0/5] OMAP Synchronous Serial Interface (SSI) driver Carlos Chinea
2008-10-03 11:52 ` [RFC][PATCH 1/5] OMAP SSI hardware interface definitions Carlos Chinea
2008-10-03 11:52   ` [RFC][PATCH 2/5] OMAP SSI driver interface Carlos Chinea
2008-10-03 11:52     ` [RFC][PATCH 3/5] OMAP SSI driver code Carlos Chinea
2008-10-03 11:52       ` [RFC][PATCH 4/5] OMAP SSI integration into misc drivers Carlos Chinea
2008-10-03 11:52         ` [RFC][PATCH 5/5] OMAP SSI API documentation Carlos Chinea
2008-10-09 16:47           ` Felipe Balbi
2008-10-07  0:08         ` [RFC][PATCH 4/5] OMAP SSI integration into misc drivers Felipe Balbi
2008-10-07  0:03       ` [RFC][PATCH 3/5] OMAP SSI driver code Felipe Balbi
2008-10-07 22:01         ` Felipe Balbi
2008-10-06 23:29     ` [RFC][PATCH 2/5] OMAP SSI driver interface Felipe Balbi
2008-10-07  1:03       ` David Brownell
2008-10-07 11:56         ` Woodruff, Richard
2008-10-06 23:16   ` [RFC][PATCH 1/5] OMAP SSI hardware interface definitions Felipe Balbi

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