diff --git a/arch/sh/configs/ap325rxa_defconfig b/arch/sh/configs/ap325rxa_defconfig
index 29926a9b9ce2248a3f8cb03feee5d38ea75a86c0..851c870adf3b0a1447270f9cd5033fe49e8bd45c 100644
--- a/arch/sh/configs/ap325rxa_defconfig
+++ b/arch/sh/configs/ap325rxa_defconfig
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.26
-# Wed Jul 30 01:18:59 2008
+# Linux kernel version: 2.6.27-rc4
+# Tue Aug 26 14:21:17 2008
 #
 CONFIG_SUPERH=y
 CONFIG_SUPERH32=y
@@ -11,6 +11,7 @@ CONFIG_GENERIC_BUG=y
 CONFIG_GENERIC_FIND_NEXT_BIT=y
 CONFIG_GENERIC_HWEIGHT=y
 CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
 CONFIG_GENERIC_IRQ_PROBE=y
 CONFIG_GENERIC_CALIBRATE_DELAY=y
 CONFIG_GENERIC_TIME=y
@@ -20,7 +21,6 @@ CONFIG_LOCKDEP_SUPPORT=y
 # CONFIG_ARCH_HAS_ILOG2_U32 is not set
 # CONFIG_ARCH_HAS_ILOG2_U64 is not set
 CONFIG_ARCH_NO_VIRT_TO_BUS=y
-CONFIG_ARCH_SUPPORTS_AOUT=y
 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
 
 #
@@ -58,7 +58,6 @@ CONFIG_SYSCTL=y
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
 CONFIG_SYSCTL_SYSCALL=y
-CONFIG_SYSCTL_SYSCALL_CHECK=y
 # CONFIG_KALLSYMS is not set
 CONFIG_HOTPLUG=y
 CONFIG_PRINTK=y
@@ -89,6 +88,7 @@ CONFIG_HAVE_OPROFILE=y
 # CONFIG_USE_GENERIC_SMP_HELPERS is not set
 CONFIG_HAVE_CLK=y
 CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
 CONFIG_SLABINFO=y
 CONFIG_RT_MUTEXES=y
 # CONFIG_TINY_SHMEM is not set
@@ -261,9 +261,10 @@ CONFIG_HZ_250=y
 # CONFIG_HZ_300 is not set
 # CONFIG_HZ_1000 is not set
 CONFIG_HZ=250
-# CONFIG_SCHED_HRTICK is not set
+CONFIG_SCHED_HRTICK=y
 # CONFIG_KEXEC is not set
 # CONFIG_CRASH_DUMP is not set
+CONFIG_SECCOMP=y
 # CONFIG_PREEMPT_NONE is not set
 # CONFIG_PREEMPT_VOLUNTARY is not set
 CONFIG_PREEMPT=y
@@ -289,10 +290,6 @@ CONFIG_CMDLINE="console=tty1 console=ttySC5,38400 root=/dev/nfs ip=dhcp"
 #
 CONFIG_BINFMT_ELF=y
 # CONFIG_BINFMT_MISC is not set
-
-#
-# Networking
-#
 CONFIG_NET=y
 
 #
@@ -647,6 +644,7 @@ CONFIG_SSB_POSSIBLE=y
 # CONFIG_MFD_CORE is not set
 # CONFIG_MFD_SM501 is not set
 # CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
 
 #
 # Multimedia devices
@@ -690,7 +688,10 @@ CONFIG_DUMMY_CONSOLE=y
 # CONFIG_ACCESSIBILITY is not set
 # CONFIG_RTC_CLASS is not set
 # CONFIG_DMADEVICES is not set
-# CONFIG_UIO is not set
+CONFIG_UIO=y
+# CONFIG_UIO_PDRV is not set
+CONFIG_UIO_PDRV_GENIRQ=y
+# CONFIG_UIO_SMX is not set
 
 #
 # File systems
@@ -854,6 +855,7 @@ CONFIG_FRAME_WARN=1024
 # CONFIG_DEBUG_KERNEL is not set
 # CONFIG_DEBUG_BUGVERBOSE is not set
 # CONFIG_DEBUG_MEMORY_INIT is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
 # CONFIG_SAMPLES is not set
 # CONFIG_SH_STANDARD_BIOS is not set
 # CONFIG_EARLY_SCIF_CONSOLE is not set
diff --git a/arch/sh/configs/migor_defconfig b/arch/sh/configs/migor_defconfig
index c4b3e1d8950d9bac9766e56e560231ab6ff11024..4f8b1974f2c7a5b3d4ba66b5daf962992519ed6b 100644
--- a/arch/sh/configs/migor_defconfig
+++ b/arch/sh/configs/migor_defconfig
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.26
-# Wed Jul 30 01:44:41 2008
+# Linux kernel version: 2.6.27-rc4
+# Tue Aug 26 14:18:17 2008
 #
 CONFIG_SUPERH=y
 CONFIG_SUPERH32=y
@@ -11,6 +11,7 @@ CONFIG_GENERIC_BUG=y
 CONFIG_GENERIC_FIND_NEXT_BIT=y
 CONFIG_GENERIC_HWEIGHT=y
 CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
 CONFIG_GENERIC_IRQ_PROBE=y
 CONFIG_GENERIC_CALIBRATE_DELAY=y
 CONFIG_GENERIC_TIME=y
@@ -21,7 +22,6 @@ CONFIG_LOCKDEP_SUPPORT=y
 # CONFIG_ARCH_HAS_ILOG2_U32 is not set
 # CONFIG_ARCH_HAS_ILOG2_U64 is not set
 CONFIG_ARCH_NO_VIRT_TO_BUS=y
-CONFIG_ARCH_SUPPORTS_AOUT=y
 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
 
 #
@@ -87,6 +87,7 @@ CONFIG_HAVE_OPROFILE=y
 # CONFIG_USE_GENERIC_SMP_HELPERS is not set
 CONFIG_HAVE_CLK=y
 CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
 CONFIG_SLABINFO=y
 CONFIG_RT_MUTEXES=y
 # CONFIG_TINY_SHMEM is not set
@@ -270,6 +271,7 @@ CONFIG_HZ=250
 # CONFIG_SCHED_HRTICK is not set
 # CONFIG_KEXEC is not set
 # CONFIG_CRASH_DUMP is not set
+CONFIG_SECCOMP=y
 CONFIG_PREEMPT_NONE=y
 # CONFIG_PREEMPT_VOLUNTARY is not set
 # CONFIG_PREEMPT is not set
@@ -294,10 +296,6 @@ CONFIG_CMDLINE="console=ttySC0,115200 earlyprintk=serial ip=on"
 #
 CONFIG_BINFMT_ELF=y
 # CONFIG_BINFMT_MISC is not set
-
-#
-# Networking
-#
 CONFIG_NET=y
 
 #
@@ -649,6 +647,7 @@ CONFIG_HW_RANDOM=y
 CONFIG_I2C=y
 CONFIG_I2C_BOARDINFO=y
 # CONFIG_I2C_CHARDEV is not set
+CONFIG_I2C_HELPER_AUTO=y
 
 #
 # I2C Hardware Bus support
@@ -709,6 +708,7 @@ CONFIG_SSB_POSSIBLE=y
 # CONFIG_MFD_CORE is not set
 # CONFIG_MFD_SM501 is not set
 # CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
 
 #
 # Multimedia devices
@@ -755,6 +755,8 @@ CONFIG_USB_ARCH_HAS_HCD=y
 # CONFIG_USB is not set
 # CONFIG_USB_OTG_WHITELIST is not set
 # CONFIG_USB_OTG_BLACKLIST_HUB is not set
+# CONFIG_USB_MUSB_HDRC is not set
+# CONFIG_USB_GADGET_MUSB_HDRC is not set
 
 #
 # NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
@@ -842,7 +844,10 @@ CONFIG_RTC_DRV_RS5C372=y
 #
 CONFIG_RTC_DRV_SH=y
 # CONFIG_DMADEVICES is not set
-# CONFIG_UIO is not set
+CONFIG_UIO=y
+# CONFIG_UIO_PDRV is not set
+CONFIG_UIO_PDRV_GENIRQ=y
+# CONFIG_UIO_SMX is not set
 
 #
 # File systems
diff --git a/arch/sh/include/asm/uaccess_64.h b/arch/sh/include/asm/uaccess_64.h
index 81b3d515fcb36920938de61b13412a0d75e95fe4..5580fd471003e156b843df49f3eec236471e7335 100644
--- a/arch/sh/include/asm/uaccess_64.h
+++ b/arch/sh/include/asm/uaccess_64.h
@@ -76,4 +76,6 @@ extern long __put_user_asm_l(void *, long);
 extern long __put_user_asm_q(void *, long);
 extern void __put_user_unknown(void);
 
+extern long __strnlen_user(const char *__s, long __n);
+
 #endif /* __ASM_SH_UACCESS_64_H */
diff --git a/arch/sh/kernel/cpu/sh5/entry.S b/arch/sh/kernel/cpu/sh5/entry.S
index 04c7da968146636084c58bef6639907093d60368..e640c63d58113046e422e30f9a1b6e6475087700 100644
--- a/arch/sh/kernel/cpu/sh5/entry.S
+++ b/arch/sh/kernel/cpu/sh5/entry.S
@@ -2,7 +2,7 @@
  * arch/sh/kernel/cpu/sh5/entry.S
  *
  * Copyright (C) 2000, 2001  Paolo Alberelli
- * Copyright (C) 2004 - 2007  Paul Mundt
+ * Copyright (C) 2004 - 2008  Paul Mundt
  * Copyright (C) 2003, 2004  Richard Curnow
  *
  * This file is subject to the terms and conditions of the GNU General Public
@@ -923,6 +923,8 @@ ret_from_exception:
 	blink   tr0, ZERO
 
 resume_kernel:
+	CLI()
+
 	pta	restore_all, tr0
 
 	getcon	KCR0, r6
@@ -939,19 +941,11 @@ need_resched:
 	andi	r7, 0xf0, r7
 	bne	r7, ZERO, tr0
 
-	movi	((PREEMPT_ACTIVE >> 16) & 65535), r8
-	shori	(PREEMPT_ACTIVE & 65535), r8
-	st.l	r6, TI_PRE_COUNT, r8
-
-	STI()
-	movi	schedule, r7
+	movi	preempt_schedule_irq, r7
 	ori	r7, 1, r7
 	ptabs	r7, tr1
 	blink	tr1, LINK
 
-	st.l	r6, TI_PRE_COUNT, ZERO
-	CLI()
-
 	pta	need_resched, tr1
 	blink	tr1, ZERO
 #endif
diff --git a/arch/sh/kernel/entry-common.S b/arch/sh/kernel/entry-common.S
index 0bc17def55a70ae6bc0d7398d92ffd826f005a14..efbb4268875e3a2b1fc43de0789b08b834cf2b3e 100644
--- a/arch/sh/kernel/entry-common.S
+++ b/arch/sh/kernel/entry-common.S
@@ -92,6 +92,7 @@ ENTRY(ret_from_irq)
 	bra	resume_userspace
 	 nop
 ENTRY(resume_kernel)
+	cli
 	mov.l	@(TI_PRE_COUNT,r8), r0	! current_thread_info->preempt_count
 	tst	r0, r0
 	bf	noresched
@@ -105,28 +106,9 @@ need_resched:
 	and	#0xf0, r0		! interrupts off (exception path)?
 	cmp/eq	#0xf0, r0
 	bt	noresched
-
-	mov.l	1f, r0
-	mov.l	r0, @(TI_PRE_COUNT,r8)
-
-#ifdef CONFIG_TRACE_IRQFLAGS
 	mov.l	3f, r0
-	jsr	@r0
-	 nop
-#endif
-	sti
-	mov.l	2f, r0
-	jsr	@r0
-	 nop
-	mov	#0, r0
-	mov.l	r0, @(TI_PRE_COUNT,r8)
-	cli
-#ifdef CONFIG_TRACE_IRQFLAGS
-	mov.l	4f, r0
-	jsr	@r0
+	jsr	@r0			! call preempt_schedule_irq
 	 nop
-#endif
-
 	bra	need_resched
 	 nop
 
@@ -137,10 +119,7 @@ noresched:
 	.align 2
 1:	.long	PREEMPT_ACTIVE
 2:	.long	schedule
-#ifdef CONFIG_TRACE_IRQFLAGS
-3:	.long	trace_hardirqs_on
-4:	.long	trace_hardirqs_off
-#endif
+3:	.long	preempt_schedule_irq
 #endif
 
 ENTRY(resume_userspace)
diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c
index 4703dff174d59bb7084c832f92a1c364d6ff77df..94df56b0d1f65612d76196ec8ef1bfbb1fb4eeae 100644
--- a/arch/sh/kernel/machine_kexec.c
+++ b/arch/sh/kernel/machine_kexec.c
@@ -102,7 +102,7 @@ void machine_kexec(struct kimage *image)
 
 	/* now call it */
 	rnk = (relocate_new_kernel_t) reboot_code_buffer;
-	(*rnk)(page_list, reboot_code_buffer, image->start, vbr_reg);
+	(*rnk)(page_list, reboot_code_buffer, P2SEGADDR(image->start), vbr_reg);
 }
 
 void arch_crash_save_vmcoreinfo(void)
diff --git a/arch/sh/kernel/ptrace_64.c b/arch/sh/kernel/ptrace_64.c
index 5922edd416dbc2b049063d58248fc75132a25fb8..9c6424892bd37fcca32ae16e02e8691198c6c788 100644
--- a/arch/sh/kernel/ptrace_64.c
+++ b/arch/sh/kernel/ptrace_64.c
@@ -131,6 +131,8 @@ void user_enable_single_step(struct task_struct *child)
 
 void user_disable_single_step(struct task_struct *child)
 {
+	struct pt_regs *regs = child->thread.uregs;
+
 	regs->sr &= ~SR_SSTEP;
 }
 
diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c
index a35207655e7b64fba60f4dee70a2fc682b716932..de832056bf1b28ae1196633d0bf23633144f8f26 100644
--- a/arch/sh/kernel/setup.c
+++ b/arch/sh/kernel/setup.c
@@ -171,6 +171,7 @@ static void __init reserve_crashkernel(void)
 				(unsigned long)(free_mem >> 20));
 		crashk_res.start = crash_base;
 		crashk_res.end   = crash_base + crash_size - 1;
+		insert_resource(&iomem_resource, &crashk_res);
 	}
 }
 #else
@@ -204,11 +205,6 @@ void __init __add_active_range(unsigned int nid, unsigned long start_pfn,
 	request_resource(res, &data_resource);
 	request_resource(res, &bss_resource);
 
-#ifdef CONFIG_KEXEC
-	if (crashk_res.start != crashk_res.end)
-		request_resource(res, &crashk_res);
-#endif
-
 	add_active_range(nid, start_pfn, end_pfn);
 }
 
diff --git a/arch/sh/kernel/sys_sh.c b/arch/sh/kernel/sys_sh.c
index 59cd2859ce9b247e70c27728dd029eaba0fb370f..9061b86d73fadefef5b20d7834a6c25299fcc289 100644
--- a/arch/sh/kernel/sys_sh.c
+++ b/arch/sh/kernel/sys_sh.c
@@ -170,7 +170,7 @@ asmlinkage int sys_ipc(uint call, int first, int second,
 	version = call >> 16; /* hack for backward compatibility */
 	call &= 0xffff;
 
-	if (call <= SEMCTL)
+	if (call <= SEMTIMEDOP)
 		switch (call) {
 		case SEMOP:
 			return sys_semtimedop(first,
diff --git a/arch/sh/mm/consistent.c b/arch/sh/mm/consistent.c
index 895bb3f335c77f39481191e54739e8871674f7a1..64b8f7f96f9aed038e87be11ee374d5b0797f239 100644
--- a/arch/sh/mm/consistent.c
+++ b/arch/sh/mm/consistent.c
@@ -101,7 +101,7 @@ static int __init memchunk_setup(char *str)
 }
 __setup("memchunk.", memchunk_setup);
 
-static void memchunk_cmdline_override(char *name, unsigned long *sizep)
+static void __init memchunk_cmdline_override(char *name, unsigned long *sizep)
 {
 	char *p = boot_command_line;
 	int k = strlen(name);
@@ -118,8 +118,8 @@ static void memchunk_cmdline_override(char *name, unsigned long *sizep)
 	}
 }
 
-int platform_resource_setup_memory(struct platform_device *pdev,
-				   char *name, unsigned long memsize)
+int __init platform_resource_setup_memory(struct platform_device *pdev,
+					  char *name, unsigned long memsize)
 {
 	struct resource *r;
 	dma_addr_t dma_handle;
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index 840e634fa31f82286d2ff1be195847a598c0fc58..640cbb237328196b1b3b22a91fea4b9ee42bf3b1 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -31,13 +31,84 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 
+/* Transmit operation:                                                      */
+/*                                                                          */
+/* 0 byte transmit                                                          */
+/* BUS:     S     A8     ACK   P                                            */
+/* IRQ:       DTE   WAIT                                                    */
+/* ICIC:                                                                    */
+/* ICCR: 0x94 0x90                                                          */
+/* ICDR:      A8                                                            */
+/*                                                                          */
+/* 1 byte transmit                                                          */
+/* BUS:     S     A8     ACK   D8(1)   ACK   P                              */
+/* IRQ:       DTE   WAIT         WAIT                                       */
+/* ICIC:      -DTE                                                          */
+/* ICCR: 0x94       0x90                                                    */
+/* ICDR:      A8    D8(1)                                                   */
+/*                                                                          */
+/* 2 byte transmit                                                          */
+/* BUS:     S     A8     ACK   D8(1)   ACK   D8(2)   ACK   P                */
+/* IRQ:       DTE   WAIT         WAIT          WAIT                         */
+/* ICIC:      -DTE                                                          */
+/* ICCR: 0x94                    0x90                                       */
+/* ICDR:      A8    D8(1)        D8(2)                                      */
+/*                                                                          */
+/* 3 bytes or more, +---------+ gets repeated                               */
+/*                                                                          */
+/*                                                                          */
+/* Receive operation:                                                       */
+/*                                                                          */
+/* 0 byte receive - not supported since slave may hold SDA low              */
+/*                                                                          */
+/* 1 byte receive       [TX] | [RX]                                         */
+/* BUS:     S     A8     ACK | D8(1)   ACK   P                              */
+/* IRQ:       DTE   WAIT     |   WAIT     DTE                               */
+/* ICIC:      -DTE           |   +DTE                                       */
+/* ICCR: 0x94       0x81     |   0xc0                                       */
+/* ICDR:      A8             |            D8(1)                             */
+/*                                                                          */
+/* 2 byte receive        [TX]| [RX]                                         */
+/* BUS:     S     A8     ACK | D8(1)   ACK   D8(2)   ACK   P                */
+/* IRQ:       DTE   WAIT     |   WAIT          WAIT     DTE                 */
+/* ICIC:      -DTE           |                 +DTE                         */
+/* ICCR: 0x94       0x81     |                 0xc0                         */
+/* ICDR:      A8             |                 D8(1)    D8(2)               */
+/*                                                                          */
+/* 3 byte receive       [TX] | [RX]                                         */
+/* BUS:     S     A8     ACK | D8(1)   ACK   D8(2)   ACK   D8(3)   ACK    P */
+/* IRQ:       DTE   WAIT     |   WAIT          WAIT         WAIT      DTE   */
+/* ICIC:      -DTE           |                              +DTE            */
+/* ICCR: 0x94       0x81     |                              0xc0            */
+/* ICDR:      A8             |                 D8(1)        D8(2)     D8(3) */
+/*                                                                          */
+/* 4 bytes or more, this part is repeated    +---------+                    */
+/*                                                                          */
+/*                                                                          */
+/* Interrupt order and BUSY flag                                            */
+/*     ___                                                 _                */
+/* SDA ___\___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAAAAAA___/                 */
+/* SCL      \_/1\_/2\_/3\_/4\_/5\_/6\_/7\_/8\___/9\_____/                   */
+/*                                                                          */
+/*        S   D7  D6  D5  D4  D3  D2  D1  D0              P                 */
+/*                                           ___                            */
+/* WAIT IRQ ________________________________/   \___________                */
+/* TACK IRQ ____________________________________/   \_______                */
+/* DTE  IRQ __________________________________________/   \_                */
+/* AL   IRQ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX                */
+/*         _______________________________________________                  */
+/* BUSY __/                                               \_                */
+/*                                                                          */
+
 enum sh_mobile_i2c_op {
 	OP_START = 0,
-	OP_TX_ONLY,
+	OP_TX_FIRST,
+	OP_TX,
 	OP_TX_STOP,
 	OP_TX_TO_RX,
-	OP_RX_ONLY,
+	OP_RX,
 	OP_RX_STOP,
+	OP_RX_STOP_DATA,
 };
 
 struct sh_mobile_i2c_data {
@@ -127,25 +198,34 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
 	spin_lock_irqsave(&pd->lock, flags);
 
 	switch (op) {
-	case OP_START:
+	case OP_START: /* issue start and trigger DTE interrupt */
 		iowrite8(0x94, ICCR(pd));
 		break;
-	case OP_TX_ONLY:
+	case OP_TX_FIRST: /* disable DTE interrupt and write data */
+		iowrite8(ICIC_WAITE | ICIC_ALE | ICIC_TACKE, ICIC(pd));
 		iowrite8(data, ICDR(pd));
 		break;
-	case OP_TX_STOP:
+	case OP_TX: /* write data */
 		iowrite8(data, ICDR(pd));
-		iowrite8(0x90, ICCR(pd));
-		iowrite8(ICIC_ALE | ICIC_TACKE, ICIC(pd));
 		break;
-	case OP_TX_TO_RX:
+	case OP_TX_STOP: /* write data and issue a stop afterwards */
 		iowrite8(data, ICDR(pd));
+		iowrite8(0x90, ICCR(pd));
+		break;
+	case OP_TX_TO_RX: /* select read mode */
 		iowrite8(0x81, ICCR(pd));
 		break;
-	case OP_RX_ONLY:
+	case OP_RX: /* just read data */
 		ret = ioread8(ICDR(pd));
 		break;
-	case OP_RX_STOP:
+	case OP_RX_STOP: /* enable DTE interrupt, issue stop */
+		iowrite8(ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE,
+			 ICIC(pd));
+		iowrite8(0xc0, ICCR(pd));
+		break;
+	case OP_RX_STOP_DATA: /* enable DTE interrupt, read data, issue stop */
+		iowrite8(ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE,
+			 ICIC(pd));
 		ret = ioread8(ICDR(pd));
 		iowrite8(0xc0, ICCR(pd));
 		break;
@@ -157,58 +237,120 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
 	return ret;
 }
 
+static int sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd)
+{
+	if (pd->pos == -1)
+		return 1;
+
+	return 0;
+}
+
+static int sh_mobile_i2c_is_last_byte(struct sh_mobile_i2c_data *pd)
+{
+	if (pd->pos == (pd->msg->len - 1))
+		return 1;
+
+	return 0;
+}
+
+static void sh_mobile_i2c_get_data(struct sh_mobile_i2c_data *pd,
+				   unsigned char *buf)
+{
+	switch (pd->pos) {
+	case -1:
+		*buf = (pd->msg->addr & 0x7f) << 1;
+		*buf |= (pd->msg->flags & I2C_M_RD) ? 1 : 0;
+		break;
+	default:
+		*buf = pd->msg->buf[pd->pos];
+	}
+}
+
+static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd)
+{
+	unsigned char data;
+
+	if (pd->pos == pd->msg->len)
+		return 1;
+
+	sh_mobile_i2c_get_data(pd, &data);
+
+	if (sh_mobile_i2c_is_last_byte(pd))
+		i2c_op(pd, OP_TX_STOP, data);
+	else if (sh_mobile_i2c_is_first_byte(pd))
+		i2c_op(pd, OP_TX_FIRST, data);
+	else
+		i2c_op(pd, OP_TX, data);
+
+	pd->pos++;
+	return 0;
+}
+
+static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd)
+{
+	unsigned char data;
+	int real_pos;
+
+	do {
+		if (pd->pos <= -1) {
+			sh_mobile_i2c_get_data(pd, &data);
+
+			if (sh_mobile_i2c_is_first_byte(pd))
+				i2c_op(pd, OP_TX_FIRST, data);
+			else
+				i2c_op(pd, OP_TX, data);
+			break;
+		}
+
+		if (pd->pos == 0) {
+			i2c_op(pd, OP_TX_TO_RX, 0);
+			break;
+		}
+
+		real_pos = pd->pos - 2;
+
+		if (pd->pos == pd->msg->len) {
+			if (real_pos < 0) {
+				i2c_op(pd, OP_RX_STOP, 0);
+				break;
+			}
+			data = i2c_op(pd, OP_RX_STOP_DATA, 0);
+		} else
+			data = i2c_op(pd, OP_RX, 0);
+
+		pd->msg->buf[real_pos] = data;
+	} while (0);
+
+	pd->pos++;
+	return pd->pos == (pd->msg->len + 2);
+}
+
 static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
 {
 	struct platform_device *dev = dev_id;
 	struct sh_mobile_i2c_data *pd = platform_get_drvdata(dev);
-	struct i2c_msg *msg = pd->msg;
-	unsigned char data, sr;
-	int wakeup = 0;
+	unsigned char sr;
+	int wakeup;
 
 	sr = ioread8(ICSR(pd));
-	pd->sr |= sr;
+	pd->sr |= sr; /* remember state */
 
 	dev_dbg(pd->dev, "i2c_isr 0x%02x 0x%02x %s %d %d!\n", sr, pd->sr,
-	       (msg->flags & I2C_M_RD) ? "read" : "write",
-	       pd->pos, msg->len);
+	       (pd->msg->flags & I2C_M_RD) ? "read" : "write",
+	       pd->pos, pd->msg->len);
 
 	if (sr & (ICSR_AL | ICSR_TACK)) {
-		iowrite8(0, ICIC(pd)); /* disable interrupts */
-		wakeup = 1;
-		goto do_wakeup;
-	}
+		/* don't interrupt transaction - continue to issue stop */
+		iowrite8(sr & ~(ICSR_AL | ICSR_TACK), ICSR(pd));
+		wakeup = 0;
+	} else if (pd->msg->flags & I2C_M_RD)
+		wakeup = sh_mobile_i2c_isr_rx(pd);
+	else
+		wakeup = sh_mobile_i2c_isr_tx(pd);
 
-	if (pd->pos == msg->len) {
-		i2c_op(pd, OP_RX_ONLY, 0);
-		wakeup = 1;
-		goto do_wakeup;
-	}
+	if (sr & ICSR_WAIT) /* TODO: add delay here to support slow acks */
+		iowrite8(sr & ~ICSR_WAIT, ICSR(pd));
 
-	if (pd->pos == -1) {
-		data = (msg->addr & 0x7f) << 1;
-		data |= (msg->flags & I2C_M_RD) ? 1 : 0;
-	} else
-		data = msg->buf[pd->pos];
-
-	if ((pd->pos == -1) || !(msg->flags & I2C_M_RD)) {
-		if (msg->flags & I2C_M_RD)
-			i2c_op(pd, OP_TX_TO_RX, data);
-		else if (pd->pos == (msg->len - 1)) {
-			i2c_op(pd, OP_TX_STOP, data);
-			wakeup = 1;
-		} else
-			i2c_op(pd, OP_TX_ONLY, data);
-	} else {
-		if (pd->pos == (msg->len - 1))
-			data = i2c_op(pd, OP_RX_STOP, 0);
-		else
-			data = i2c_op(pd, OP_RX_ONLY, 0);
-
-		msg->buf[pd->pos] = data;
-	}
-	pd->pos++;
-
- do_wakeup:
 	if (wakeup) {
 		pd->sr |= SW_DONE;
 		wake_up(&pd->wait);
@@ -219,6 +361,11 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
 
 static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg)
 {
+	if (usr_msg->len == 0 && (usr_msg->flags & I2C_M_RD)) {
+		dev_err(pd->dev, "Unsupported zero length i2c read\n");
+		return -EIO;
+	}
+
 	/* Initialize channel registers */
 	iowrite8(ioread8(ICCR(pd)) & ~ICCR_ICE, ICCR(pd));
 
@@ -233,9 +380,8 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg)
 	pd->pos = -1;
 	pd->sr = 0;
 
-	/* Enable all interrupts except wait */
-	iowrite8(ioread8(ICIC(pd)) | ICIC_ALE | ICIC_TACKE | ICIC_DTEE,
-		 ICIC(pd));
+	/* Enable all interrupts to begin with */
+	iowrite8(ICIC_WAITE | ICIC_ALE | ICIC_TACKE | ICIC_DTEE, ICIC(pd));
 	return 0;
 }
 
@@ -268,25 +414,18 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
 		if (!k)
 			dev_err(pd->dev, "Transfer request timed out\n");
 
-		retry_count = 10;
+		retry_count = 1000;
 again:
 		val = ioread8(ICSR(pd));
 
 		dev_dbg(pd->dev, "val 0x%02x pd->sr 0x%02x\n", val, pd->sr);
 
-		if ((val | pd->sr) & (ICSR_TACK | ICSR_AL)) {
-			err = -EIO;
-			break;
-		}
-
 		/* the interrupt handler may wake us up before the
 		 * transfer is finished, so poll the hardware
 		 * until we're done.
 		 */
-
-		if (!(!(val & ICSR_BUSY) && (val & ICSR_SCLM) &&
-		      (val & ICSR_SDAM))) {
-			msleep(1);
+		if (val & ICSR_BUSY) {
+			udelay(10);
 			if (retry_count--)
 				goto again;
 
@@ -294,6 +433,12 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
 			dev_err(pd->dev, "Polling timed out\n");
 			break;
 		}
+
+		/* handle missing acknowledge and arbitration lost */
+		if ((val | pd->sr) & (ICSR_TACK | ICSR_AL)) {
+			err = -EIO;
+			break;
+		}
 	}
 
 	deactivate_ch(pd);