* [PATCH 0/5] RFC: OMAP Synchronous Serial Interface (SSI) driver
@ 2008-09-16 14:30 Carlos Chinea
2008-10-03 10:25 ` [PATCH 0/5] RFC: OMAP Synchronous Serial Interface (SSI) driver (take II) Carlos Chinea
0 siblings, 1 reply; 9+ messages in thread
From: Carlos Chinea @ 2008-09-16 14:30 UTC (permalink / raw)
To: 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-rc6.
Any comments will be appreciated.
Br,
Carlos
Documentation/arm/OMAP/ssi/board-ssi.c.example | 216 +++++++
Documentation/arm/OMAP/ssi/ssi | 233 +++++++
arch/arm/plat-omap/include/mach/ssi/ssi_gdd_reg.h | 145 ++++
arch/arm/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 | 108 +++
drivers/misc/Kconfig | 2
drivers/misc/Makefile | 2
drivers/misc/ssi/Kconfig | 20
drivers/misc/ssi/Makefile | 11
drivers/misc/ssi/ssi_driver.c | 572 +++++++++++++++++++
drivers/misc/ssi/ssi_driver.h | 228 +++++++
drivers/misc/ssi/ssi_driver_bus.c | 218 +++++++
drivers/misc/ssi/ssi_driver_dma.c | 461 +++++++++++++++
drivers/misc/ssi/ssi_driver_if.c | 374 ++++++++++++
drivers/misc/ssi/ssi_driver_int.c | 296 +++++++++
include/linux/ssi_driver_if.h | 138 ++++
18 files changed, 3213 insertions(+), 5 deletions(-)
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 0/5] RFC: OMAP Synchronous Serial Interface (SSI) driver (take II)
2008-09-16 14:30 [PATCH 0/5] RFC: OMAP Synchronous Serial Interface (SSI) driver Carlos Chinea
@ 2008-10-03 10:25 ` Carlos Chinea
2008-10-03 10:29 ` [PATCH 1/5] OMAP SSI hardware interface definitions Carlos Chinea
2008-10-03 10:34 ` [PATCH 0/5] RFC: OMAP Synchronous Serial Interface (SSI) driver (take II) Tony Lindgren
0 siblings, 2 replies; 9+ messages in thread
From: Carlos Chinea @ 2008-10-03 10:25 UTC (permalink / raw)
To: linux-omap
Hi guys !
A couple of weeks ago, I sent a set of patches for comments.
I am sending a new set of those patches with debug statements removed and a bug fix.
The new 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(-)
On Tue, 2008-09-16 at 17:30 +0300, ext Carlos Chinea wrote:
> 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-rc6.
>
> Any comments will be appreciated.
>
> Br,
> Carlos
>
> Documentation/arm/OMAP/ssi/board-ssi.c.example | 216 +++++++
> Documentation/arm/OMAP/ssi/ssi | 233 +++++++
> arch/arm/plat-omap/include/mach/ssi/ssi_gdd_reg.h | 145 ++++
> arch/arm/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 | 108 +++
> drivers/misc/Kconfig | 2
> drivers/misc/Makefile | 2
> drivers/misc/ssi/Kconfig | 20
> drivers/misc/ssi/Makefile | 11
> drivers/misc/ssi/ssi_driver.c | 572 +++++++++++++++++++
> drivers/misc/ssi/ssi_driver.h | 228 +++++++
> drivers/misc/ssi/ssi_driver_bus.c | 218 +++++++
> drivers/misc/ssi/ssi_driver_dma.c | 461 +++++++++++++++
> drivers/misc/ssi/ssi_driver_if.c | 374 ++++++++++++
> drivers/misc/ssi/ssi_driver_int.c | 296 +++++++++
> include/linux/ssi_driver_if.h | 138 ++++
> 18 files changed, 3213 insertions(+), 5 deletions(-)
>
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/5] OMAP SSI hardware interface definitions
2008-10-03 10:25 ` [PATCH 0/5] RFC: OMAP Synchronous Serial Interface (SSI) driver (take II) Carlos Chinea
@ 2008-10-03 10:29 ` Carlos Chinea
2008-10-03 10:29 ` [PATCH 2/5] OMAP SSI driver interface Carlos Chinea
2008-10-03 14:27 ` [PATCH 1/5] OMAP SSI hardware interface definitions David Brownell
2008-10-03 10:34 ` [PATCH 0/5] RFC: OMAP Synchronous Serial Interface (SSI) driver (take II) Tony Lindgren
1 sibling, 2 replies; 9+ messages in thread
From: Carlos Chinea @ 2008-10-03 10:29 UTC (permalink / raw)
To: 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] 9+ messages in thread
* [PATCH 2/5] OMAP SSI driver interface
2008-10-03 10:29 ` [PATCH 1/5] OMAP SSI hardware interface definitions Carlos Chinea
@ 2008-10-03 10:29 ` Carlos Chinea
2008-10-03 10:29 ` [PATCH 3/5] OMAP SSI driver code Carlos Chinea
2008-10-03 14:27 ` [PATCH 1/5] OMAP SSI hardware interface definitions David Brownell
1 sibling, 1 reply; 9+ messages in thread
From: Carlos Chinea @ 2008-10-03 10:29 UTC (permalink / raw)
To: 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] 9+ messages in thread
* [PATCH 3/5] OMAP SSI driver code
2008-10-03 10:29 ` [PATCH 2/5] OMAP SSI driver interface Carlos Chinea
@ 2008-10-03 10:29 ` Carlos Chinea
2008-10-03 10:29 ` [PATCH 4/5] OMAP SSI integration into misc drivers Carlos Chinea
0 siblings, 1 reply; 9+ messages in thread
From: Carlos Chinea @ 2008-10-03 10:29 UTC (permalink / raw)
To: 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] 9+ messages in thread
* [PATCH 4/5] OMAP SSI integration into misc drivers
2008-10-03 10:29 ` [PATCH 3/5] OMAP SSI driver code Carlos Chinea
@ 2008-10-03 10:29 ` Carlos Chinea
2008-10-03 10:29 ` [PATCH 5/5] OMAP SSI API documentation Carlos Chinea
0 siblings, 1 reply; 9+ messages in thread
From: Carlos Chinea @ 2008-10-03 10:29 UTC (permalink / raw)
To: 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] 9+ messages in thread
* [PATCH 5/5] OMAP SSI API documentation
2008-10-03 10:29 ` [PATCH 4/5] OMAP SSI integration into misc drivers Carlos Chinea
@ 2008-10-03 10:29 ` Carlos Chinea
0 siblings, 0 replies; 9+ messages in thread
From: Carlos Chinea @ 2008-10-03 10:29 UTC (permalink / raw)
To: 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] 9+ messages in thread
* Re: [PATCH 0/5] RFC: OMAP Synchronous Serial Interface (SSI) driver (take II)
2008-10-03 10:25 ` [PATCH 0/5] RFC: OMAP Synchronous Serial Interface (SSI) driver (take II) Carlos Chinea
2008-10-03 10:29 ` [PATCH 1/5] OMAP SSI hardware interface definitions Carlos Chinea
@ 2008-10-03 10:34 ` Tony Lindgren
1 sibling, 0 replies; 9+ messages in thread
From: Tony Lindgren @ 2008-10-03 10:34 UTC (permalink / raw)
To: Carlos Chinea; +Cc: linux-omap
* Carlos Chinea <carlos.chinea@nokia.com> [081003 13:26]:
> Hi guys !
>
> A couple of weeks ago, I sent a set of patches for comments.
> I am sending a new set of those patches with debug statements removed and a bug fix.
>
> The new patch set is based on linux-omap 2.6.27-rc7
>
> Any comments will be appreciated.
You should send this to LKML, and cc linux-omap to get proper review
of these patches.
Tony
>
> 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(-)
>
>
> On Tue, 2008-09-16 at 17:30 +0300, ext Carlos Chinea wrote:
> > 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-rc6.
> >
> > Any comments will be appreciated.
> >
> > Br,
> > Carlos
> >
> > Documentation/arm/OMAP/ssi/board-ssi.c.example | 216 +++++++
> > Documentation/arm/OMAP/ssi/ssi | 233 +++++++
> > arch/arm/plat-omap/include/mach/ssi/ssi_gdd_reg.h | 145 ++++
> > arch/arm/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 | 108 +++
> > drivers/misc/Kconfig | 2
> > drivers/misc/Makefile | 2
> > drivers/misc/ssi/Kconfig | 20
> > drivers/misc/ssi/Makefile | 11
> > drivers/misc/ssi/ssi_driver.c | 572 +++++++++++++++++++
> > drivers/misc/ssi/ssi_driver.h | 228 +++++++
> > drivers/misc/ssi/ssi_driver_bus.c | 218 +++++++
> > drivers/misc/ssi/ssi_driver_dma.c | 461 +++++++++++++++
> > drivers/misc/ssi/ssi_driver_if.c | 374 ++++++++++++
> > drivers/misc/ssi/ssi_driver_int.c | 296 +++++++++
> > include/linux/ssi_driver_if.h | 138 ++++
> > 18 files changed, 3213 insertions(+), 5 deletions(-)
> >
> >
> >
> > --
> > 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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/5] OMAP SSI hardware interface definitions
2008-10-03 10:29 ` [PATCH 1/5] OMAP SSI hardware interface definitions Carlos Chinea
2008-10-03 10:29 ` [PATCH 2/5] OMAP SSI driver interface Carlos Chinea
@ 2008-10-03 14:27 ` David Brownell
1 sibling, 0 replies; 9+ messages in thread
From: David Brownell @ 2008-10-03 14:27 UTC (permalink / raw)
To: Carlos Chinea; +Cc: linux-omap
On Friday 03 October 2008, Carlos Chinea wrote:
>
> Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
You should provide patch comments, in the form of at least
one whole sentence. $SUBJECT is rarely felt to be enough.
> ---
> 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(-)
Why is this five files instead of one?
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2008-10-03 14:27 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-09-16 14:30 [PATCH 0/5] RFC: OMAP Synchronous Serial Interface (SSI) driver Carlos Chinea
2008-10-03 10:25 ` [PATCH 0/5] RFC: OMAP Synchronous Serial Interface (SSI) driver (take II) Carlos Chinea
2008-10-03 10:29 ` [PATCH 1/5] OMAP SSI hardware interface definitions Carlos Chinea
2008-10-03 10:29 ` [PATCH 2/5] OMAP SSI driver interface Carlos Chinea
2008-10-03 10:29 ` [PATCH 3/5] OMAP SSI driver code Carlos Chinea
2008-10-03 10:29 ` [PATCH 4/5] OMAP SSI integration into misc drivers Carlos Chinea
2008-10-03 10:29 ` [PATCH 5/5] OMAP SSI API documentation Carlos Chinea
2008-10-03 14:27 ` [PATCH 1/5] OMAP SSI hardware interface definitions David Brownell
2008-10-03 10:34 ` [PATCH 0/5] RFC: OMAP Synchronous Serial Interface (SSI) driver (take II) Tony Lindgren
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.