From 13b70a7c75b777a14031a567da48803de53e6ce3 Mon Sep 17 00:00:00 2001
From: z3deverp <z3@vmsv-debian.(none)>
Date: Wed, 18 Jun 2008 18:38:16 +0900
Subject: [PATCH] pxa:Basic support for sha_pon0xx

---
 arch/arm/mach-pxa/Kconfig           |    8 +
 arch/arm/mach-pxa/Makefile          |    1 +
 arch/arm/mach-pxa/sha_pon.c         |  420 +++++++++++++++++++++++++++++++++++
 drivers/input/keyboard/Kconfig      |   11 +
 drivers/input/keyboard/Makefile     |    1 +
 drivers/input/keyboard/sha_ponkbd.c |  360 ++++++++++++++++++++++++++++++
 include/asm-arm/arch-pxa/sha_pon.h  |  240 ++++++++++++++++++++
 7 files changed, 1041 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig
index ac01c3a..25f95d8 100644
--- a/arch/arm/mach-pxa/Kconfig
+++ b/arch/arm/mach-pxa/Kconfig
@@ -68,6 +68,14 @@ config MACH_ARMCORE
 	select PXA27x
 	select IWMMXT
 
+config ARCH_SHA_PON
+	bool "Sharp W-ZERO3 WS0xxSH series smartphone"
+	select PXA27x
+	select FB_PXA_NOTDRIVE_ACBIAS
+	help
+	  Say Y here if you intend to run this kernel on a
+	  Sharp W-ZERO3 WS0xxSH series(Sha_pon0xx) smartphone.
+
 endchoice
 
 if PXA_SHARPSL
diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
index 4263527..eef8e21 100644
--- a/arch/arm/mach-pxa/Makefile
+++ b/arch/arm/mach-pxa/Makefile
@@ -30,6 +30,7 @@ ifeq ($(CONFIG_MACH_ZYLONITE),y)
 endif
 
 obj-$(CONFIG_MACH_ARMCORE)      += cm-x270.o
+obj-$(CONFIG_ARCH_SHA_PON) += sha_pon.o
 
 # Support for blinky lights
 led-y := leds.o
diff --git a/arch/arm/mach-pxa/sha_pon.c b/arch/arm/mach-pxa/sha_pon.c
new file mode 100755
index 0000000..88a5ff7
--- /dev/null
+++ b/arch/arm/mach-pxa/sha_pon.c
@@ -0,0 +1,420 @@
+/*
+ * linux/arch/arm/mach-pxa/sha_pon.c
+ *
+ * Support for the Sharp W-ZERO3 series(sha_pon0**).
+ * based on lpd270.c, corgi.c
+ *
+ * Author:	zaki
+ *
+ * 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.
+ */
+
+#define	UDC	0	/* 0:disable udc driver */
+#define	TS	0	/* 0:disable touchscreen driver */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+
+#include <asm/setup.h>
+#include <asm/memory.h>
+#include <asm/mach-types.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/pxafb.h>
+#if UDC
+#include <asm/arch/udc.h>
+#endif
+#include <asm/arch/ohci.h>
+
+#include "generic.h"
+
+#include <asm/arch/sha_pon.h>
+#if TS
+#include "sha_pon.h"
+#endif
+
+
+static struct map_desc sha_pon_io_desc[] __initdata = {
+	{
+		.virtual	= EXT_GPIO_VBASE,
+		.pfn            = __phys_to_pfn(EXT_GPIO_PBASE),
+		.length         = EXT_GPIO_LENGTH,
+		.type           = MT_DEVICE
+	},
+};
+
+
+/*
+ * Keyboard Device
+ */
+static struct platform_device sha_ponkbd_device = {
+	.name		= "sha_pon-keyboard",
+	.id		= -1,
+};
+
+
+/* sha_pon003, sha_pon004 LCD panel */
+static struct pxafb_mode_info sharp_ls037v7dw01_mode = {
+	.pixclock       = 39700,
+	.xres           = 480,
+	.yres           = 640,
+	.bpp            = 16,
+	.hsync_len      = 2,
+	.left_margin    = 78+64,	/* datasheet value +/- tuned value */
+	.right_margin   = 88-64,	/* datasheet value +/- tuned value */
+	.vsync_len      = 1,
+	.upper_margin   = 2,
+	.lower_margin   = 6,
+	.sync           = 0,	/*FB_SYNC_HOR_LOW_ACT | FB_SYNC_VERT_LOW_ACT*/
+};
+
+static struct pxafb_mach_info sharp_ls037v7dw01 = {
+	.modes			= &sharp_ls037v7dw01_mode,
+	.num_modes		= 1,
+	.lccr0          = LCCR0_Color | LCCR0_Sngl | LCCR0_Act | LCCR0_LDDALT | LCCR0_OUC | (LCCR0_CMDIM) | (LCCR0_RDSTM),
+	.lccr3          = LCCR3_PixRsEdg | LCCR3_OutEnH,
+};
+
+
+/* sha_pon007 LCD panel */
+#define	sharp_ls028v7pw01	sharp_ls037v7dw01	/* compatible LCD panel */
+
+
+/* sha_pon011 LCD panel */
+static struct pxafb_mode_info pxa_sharp_ws011sh_mode = {
+	.pixclock       = 31384,	/* guessed value */
+	.xres           = 480,
+	.yres           = 800,
+	.bpp            = 16,
+	.hsync_len      = 2,	/* guessed value */
+	.left_margin    = 78-57,	/* guessed value +/- tuned value */
+	.right_margin   = 88+57,	/* guessed value +/- tuned value */
+	.vsync_len      = 1,	/* guessed value */
+	.upper_margin   = 2,	/* guessed value */
+	.lower_margin   = 6,	/* guessed value */
+	.sync           = 0,	/*FB_SYNC_HOR_LOW_ACT | FB_SYNC_VERT_LOW_ACT*/
+};
+
+static struct pxafb_mach_info pxa_sharp_ws011sh = {
+	.modes			= &pxa_sharp_ws011sh_mode,
+	.num_modes		= 1,
+	.lccr0          = LCCR0_Color | LCCR0_Sngl | LCCR0_Act | LCCR0_LDDALT | LCCR0_OUC | (LCCR0_CMDIM) | (LCCR0_RDSTM),
+	.lccr3          = LCCR3_PixRsEdg | LCCR3_OutEnH,
+};
+
+
+#if TS
+/* SSP Device */
+struct sha_ponssp_machinfo sha_pon003ssp_machinfo = {	/* not tested */
+        .port		= 2, /* guessed value */
+	.cs_tsc2046	= SHA_PON_GPIO_TSC2046_CS,
+	.clk_tsc2046	= 6, /* datasheet value : div >= source13MHz / dclk2.5MHz(max) = 5.2 */
+	.cs_unknown	= SHA_PON_GPIO_UNKNOWN_CS,
+};
+
+
+struct sha_ponssp_machinfo sha_pon007ssp_machinfo = {
+        .port		= 2, /* guessed value */
+	.cs_tsc2046	= SHA_PON_GPIO_TSC2046_CS,
+	.clk_tsc2046	= 6, /* datasheet value : div >= source13MHz / dclk2.5MHz(max) = 5.2 */
+	.cs_unknown	= SHA_PON_GPIO_UNKNOWN_CS,
+};
+
+
+struct sha_ponssp_machinfo sha_pon011ssp_machinfo = {	/* not tested */
+        .port		= 2, /* guessed value */
+	.cs_tsc2046	= SHA_PON_GPIO_TSC2046_CS,
+	.clk_tsc2046	= 6, /* datasheet value : div >= source13MHz / dclk2.5MHz(max) = 5.2 */
+	.cs_unknown	= SHA_PON_GPIO_UNKNOWN_CS,
+};
+
+
+struct platform_device sha_ponssp_device = {
+	.name		= "sha_pon-ssp",
+	.id		= -1,
+};
+
+
+/* Touch Screen Device */
+static int sha_pon003ts_status(void)
+{
+	/* true:pendown, false:penup */
+	return( 0 );	/* FIXME */
+}
+
+
+static int sha_pon007ts_status(void)
+{
+	/* true:pendown, false:penup */
+	return( (GPLR(SHA_PON007_GPIO_TP) & GPIO_bit(SHA_PON007_GPIO_TP)) == 0 );
+}
+
+
+static int sha_pon011ts_status(void)
+{
+	/* true:pendown, false:penup */
+	return( 0 );	/* FIXME */
+}
+
+
+static struct platform_device sha_pon003ts_device = {
+	.name		= "sha_pon-ts",
+	.dev		= {
+ 		.parent = &sha_ponssp_device.dev,
+		.platform_data	= sha_pon003ts_status,
+	},
+	.id		= -1,
+};
+
+static struct platform_device sha_pon007ts_device = {
+	.name		= "sha_pon-ts",
+	.dev		= {
+ 		.parent = &sha_ponssp_device.dev,
+		.platform_data	= sha_pon007ts_status,
+	},
+	.id		= -1,
+};
+
+static struct platform_device sha_pon011ts_device = {
+	.name		= "sha_pon-ts",
+	.dev		= {
+ 		.parent = &sha_ponssp_device.dev,
+		.platform_data	= sha_pon011ts_status,
+	},
+	.id		= -1,
+};
+#endif
+
+/* W-SIM */
+static void sha_pon_reset_wsim(void)
+{
+	/* Power-ON-reset W-SIM device as a modem connected to FFUART */
+	/* power down */
+	SHA_PON_CPLD10 = 0x00;
+	/* wait to discharge power */
+	mdelay(800*4);		/* tested value=800msec(min) --> margine factor=4 */
+	/* power up */
+	SHA_PON_CPLD10 = 0x10;
+}
+
+
+#if UDC
+/* USB gadget */
+static void sha_pon_udc_command(int cmd)
+{
+	switch( cmd ) {
+	case PXA2XX_UDC_CMD_CONNECT:
+		/* enable USB port2, Differencial, Device mode, D+ Pullup(Full speed) */
+		UP2OCR = UP2OCR_HXOE | UP2OCR_DPPUE;
+		break;
+	case PXA2XX_UDC_CMD_DISCONNECT:
+		/* disable USB port2 */
+		UP2OCR = 0;
+		break;
+	}
+}
+
+static struct pxa2xx_udc_mach_info sha_pon_udc_info = {
+	.udc_command = sha_pon_udc_command,
+};
+#endif
+
+
+/* USB Host */
+static int sha_pon_usb_init(struct device *dev)
+{
+	/* enable USB port2, Differencial, Host mode, D+/D- Pulldown */
+	UP2OCR = UP2OCR_HXOE | UP2OCR_HXS | UP2OCR_DMPDE | UP2OCR_DPPDE;
+	return(0);
+}
+
+static void sha_pon_usb_exit(struct device *dev)
+{
+	/* disable USB port2 */
+	UP2OCR = 0;
+}
+
+
+static struct pxaohci_platform_data sha_pon_usb_info = {
+	.init		= sha_pon_usb_init,
+	.exit		= sha_pon_usb_exit,
+	.port_mode	= PMM_NPS_MODE,		/* FIXME: Non-power switching mode */
+	.power_budget	= 0,			/* FIXME: ??? */
+};
+
+
+
+static struct platform_device *devices003[] __initdata = {
+	&sha_ponkbd_device,
+#if TS
+	&sha_ponssp_device,
+	&sha_pon003ts_device,
+#endif
+};
+
+static struct platform_device *devices007[] __initdata = {
+	&sha_ponkbd_device,
+#if TS
+	&sha_ponssp_device,
+	&sha_pon007ts_device,
+#endif
+};
+
+static struct platform_device *devices011[] __initdata = {
+	&sha_ponkbd_device,
+#if TS
+	&sha_ponssp_device,
+	&sha_pon011ts_device,
+#endif
+};
+
+
+static void __init sha_pon003_init(void)
+{
+	set_pxa_fb_info(&sharp_ls037v7dw01);
+#if UDC
+	pxa_set_udc_info(&sha_pon_udc_info);
+#endif
+	pxa_set_ohci_info(&sha_pon_usb_info);
+#if TS
+	sha_pon_ssp_set_machinfo(&sha_pon003ssp_machinfo);
+#endif
+	platform_add_devices(devices003, ARRAY_SIZE(devices003));
+}
+
+
+static void __init sha_pon007_init(void)
+{
+	set_pxa_fb_info(&sharp_ls028v7pw01);
+#if UDC
+	pxa_set_udc_info(&sha_pon_udc_info);
+#endif
+	pxa_set_ohci_info(&sha_pon_usb_info);
+#if TS
+	sha_pon_ssp_set_machinfo(&sha_pon007ssp_machinfo);
+#endif
+	platform_add_devices(devices007, ARRAY_SIZE(devices007));
+}
+
+
+static void __init sha_pon011_init(void)
+{
+	set_pxa_fb_info(&pxa_sharp_ws011sh);
+#if UDC
+	pxa_set_udc_info(&sha_pon_udc_info);
+#endif
+	pxa_set_ohci_info(&sha_pon_usb_info);
+#if TS
+	sha_pon_ssp_set_machinfo(&sha_pon011ssp_machinfo);
+#endif
+	platform_add_devices(devices011, ARRAY_SIZE(devices011));
+}
+
+
+ARCH_SHA_PON_DEBUG_GLOBALVAR;		/* for debug */
+
+static void __init sha_pon_map_io(void)
+{
+	
+	pxa_map_io();
+	iotable_init(sha_pon_io_desc, ARRAY_SIZE(sha_pon_io_desc));
+
+	ARCH_SHA_PON_DEBUG_CAPTUREVAR;		/* for debug */
+	
+	/* GPIO mode */
+	pxa_gpio_mode( 98 | GPIO_ALT_FN_3_OUT );	/* GPIO98's mode is FFRTS,OUTPUT */
+	pxa_gpio_mode( 99 | GPIO_ALT_FN_3_OUT );	/* GPIO99's mode is FFTXD,OUTPUT */
+	
+	/* for use I SRAM as framebuffer.  */
+	PSLR = (PSLR&~0x002ff0f3) | 0x00000F04;
+			/* SL_R0-3 = 1111 : SRAM0-3 retains state in sleep mode */
+			/* SL_PI = 01 : PI power domain retains state in sleep mode */
+			/*   PI domain -- PWR_I2C & timer13M */
+			/* write 0 to reserve bit */
+
+	PSTR = 0x00000f04;		/* test value */
+	PCFR = 0x00000000;		/* test value */
+
+	/* FIXME: LCCR4-bit31 PCDDIV=1 is better than PCDDIV=0, if pxafb.c allows. */
+	LCCR4 = 0x00000000;		/* PCDDIV=1(WM)->0(Linux) */
+
+	/* GPIO mode */
+	pxa_gpio_mode( 98 | GPIO_ALT_FN_3_OUT );	/* GPIO98's mode is FFRTS,OUTPUT */
+	pxa_gpio_mode( 99 | GPIO_ALT_FN_3_OUT );	/* GPIO99's mode is FFTXD,OUTPUT */
+	
+#if TS
+#if 0	/* GPIO for detect touchscreen, default:input */
+	pxa_gpio_mode( SHA_PON007_GPIO_TP | GPIO_IN );	/* test for 007 */
+#endif
+#endif
+
+	sha_pon_reset_wsim();
+
+	/* initialize sleep mode regs (wake-up sources, etc) : NOT tested */
+/*
+	PGSR0 = 0x00008800;
+	PGSR1 = 0x00000002;
+	PGSR2 = 0x0001FC00;
+	PGSR3 = 0x00001F81;
+	PWER  = 0xC0000002;
+	PRER  = 0x00000002;
+	PFER  = 0x00000002;
+*/
+
+}
+
+
+MACHINE_START(SHA_PON003, "Sharp W-ZERO3 WS003SH")
+	.phys_io	= 0x40000000,
+	.io_pg_offst	= (io_p2v(0x40000000) >> 18) & 0xfffc,
+	.boot_params	= 0xa0000000,
+	.map_io		= sha_pon_map_io,
+	.init_irq	= pxa27x_init_irq,
+	.timer		= &pxa_timer,
+	.init_machine	= sha_pon003_init,
+MACHINE_END
+
+
+MACHINE_START(SHA_PON004, "Sharp W-ZERO3 WS004SH")
+	.phys_io	= 0x40000000,
+	.io_pg_offst	= (io_p2v(0x40000000) >> 18) & 0xfffc,
+	.boot_params	= 0xa0000000,
+	.map_io		= sha_pon_map_io,
+	.init_irq	= pxa27x_init_irq,
+	.timer		= &pxa_timer,
+	.init_machine	= sha_pon003_init,
+MACHINE_END
+
+
+MACHINE_START(SHA_PON007, "Sharp W-ZERO3[es] WS007SH")
+	.phys_io	= 0x40000000,
+	.io_pg_offst	= (io_p2v(0x40000000) >> 18) & 0xfffc,
+	.boot_params	= 0xa0000000,
+	.map_io		= sha_pon_map_io,
+	.init_irq	= pxa27x_init_irq,
+	.timer		= &pxa_timer,
+	.init_machine	= sha_pon007_init,
+MACHINE_END
+
+
+MACHINE_START(SHA_PON011, "Sharp Advanced/W-ZERO3[es] WS011SH")
+	.phys_io	= 0x40000000,
+	.io_pg_offst	= (io_p2v(0x40000000) >> 18) & 0xfffc,
+	.boot_params	= 0xa0000000,
+	.map_io		= sha_pon_map_io,
+	.init_irq	= pxa27x_init_irq,
+	.timer		= &pxa_timer,
+	.init_machine	= sha_pon011_init,
+MACHINE_END
+
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 086d58c..f048d40 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -154,6 +154,17 @@ config KEYBOARD_SPITZ
 	  To compile this driver as a module, choose M here: the
 	  module will be called spitzkbd.
 
+config KEYBOARD_SHA_PON
+	tristate "Sha_pon keyboard"
+	depends on ARCH_SHA_PON
+	default y
+	help
+	  Say Y here to enable the keyboard on the Sharp W-ZERO3 series
+	  of smartphones.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sha_ponkbd.
+
 config KEYBOARD_AMIGA
 	tristate "Amiga keyboard"
 	depends on AMIGA
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index e97455f..7fb001a 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_KEYBOARD_NEWTON)		+= newtonkbd.o
 obj-$(CONFIG_KEYBOARD_STOWAWAY)		+= stowaway.o
 obj-$(CONFIG_KEYBOARD_CORGI)		+= corgikbd.o
 obj-$(CONFIG_KEYBOARD_SPITZ)		+= spitzkbd.o
+obj-$(CONFIG_KEYBOARD_SHA_PON)		+= sha_ponkbd.o
 obj-$(CONFIG_KEYBOARD_HIL)		+= hil_kbd.o
 obj-$(CONFIG_KEYBOARD_HIL_OLD)		+= hilkbd.o
 obj-$(CONFIG_KEYBOARD_OMAP)		+= omap-keypad.o
diff --git a/drivers/input/keyboard/sha_ponkbd.c b/drivers/input/keyboard/sha_ponkbd.c
new file mode 100644
index 0000000..69862e4
--- /dev/null
+++ b/drivers/input/keyboard/sha_ponkbd.c
@@ -0,0 +1,360 @@
+/*
+ *  Keyboard driver for the Sharp W-ZERO3 series(sha_pon0xx)
+ *
+ *  Auther: zaki
+ *
+ *  Based on aaed2000_kbd.c
+ *
+ *  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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/sha_pon.h>
+
+#define KB_ROWS			8
+#define KB_COLS			12
+
+#define KB_ROWMASK(r)		(1 << (r))
+#define SCANCODE(r,c)		(((c) * KB_ROWS) + (r))
+#define NR_SCANCODES		(KB_COLS * KB_ROWS)
+
+#if( ARCH_SHA_PON_DEBUG==2 || ARCH_SHA_PON_DEBUG==3 )
+#define SCAN_INTERVAL		(5000) /* ms */	/* for debug */
+#elif( ARCH_SHA_PON_DEBUG==4 || ARCH_SHA_PON_DEBUG==6 )
+#define SCAN_INTERVAL		(100) /* ms */	/* for debug */
+#else
+#define SCAN_INTERVAL		(50) /* ms */
+#endif
+
+#define KB_ACTIVATE_DELAY	(20) /* us */
+#define KBDSCAN_STABLE_COUNT 2
+
+
+#define	KEY_ALT	KEY_LEFTALT
+#define	KEY_CTRL	KEY_LEFTCTRL
+#define	KEY_SHIFT	KEY_LEFTSHIFT	/* LEFT&RIGHT is same circuit */
+
+
+static unsigned int sha_pon_keycode[] = {
+/* KEY_ZENKAKUHANKAKU, KEY_MUHENKAN: WS011SH only */
+	KEY_CTRL,	0,	KEY_TAB,	0,	0,	0,	0,	0,
+	KEY_1,	KEY_2,	KEY_Q,	KEY_W,	KEY_A,	KEY_Z,	KEY_HENKAN,	0,
+	KEY_3,	KEY_4,	KEY_E,	KEY_S,	KEY_D,	KEY_X,	KEY_ZENKAKUHANKAKU,	0,
+	KEY_5,	KEY_R,	KEY_T,	KEY_F,	KEY_C,	KEY_MINUS,	KEY_MUHENKAN,	0,
+
+	KEY_6,	KEY_Y,	KEY_G,	KEY_V,	KEY_B,	KEY_SPACE,	0,	0,
+	KEY_7,	KEY_8,	KEY_U,	KEY_H,	KEY_N,	KEY_SLASH,	KEY_COMMA,	0,
+	KEY_9,	KEY_I,	KEY_J,	KEY_M,	KEY_DOT,	0,	KEY_LEFT,	0,
+	KEY_0,	KEY_O,	KEY_K,	KEY_L,	0,	KEY_UP,	KEY_DOWN,	0,
+
+	KEY_BACKSPACE,	KEY_P,	0,	0,	KEY_ENTER,	0,	KEY_RIGHT,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	KEY_SWITCHVIDEOMODE,	0,	0,	KEY_SHIFT,	0,	0,	0,	0,
+	KEY_VOLUMEDOWN,	KEY_VOLUMEUP,	0,	0,	0,	KEY_ALT,	0,	0,
+};
+
+
+static unsigned int sha_pon_togglekey[] = {
+	KEY_SHIFT,
+	KEY_CTRL,
+	KEY_ALT, 
+	0	/* 0:end mark */
+};
+
+
+struct sha_ponkbd {
+	unsigned int keycode[KB_ROWS*KB_COLS];
+	struct input_dev *input;
+	struct delayed_work workq;
+	int kbdscan_state[KB_COLS];
+	int kbdscan_state2[KB_COLS];
+	int kbdscan_count[KB_COLS];
+	int kbdscan_togglekey[KB_COLS];
+	int kbdscan_toggle_state[KB_COLS];
+	int toggle_enable;
+};
+
+static struct sha_ponkbd *sha_ponkbd;
+
+
+static void sha_pon_clear_toggle(struct sha_ponkbd *sha_ponkbd)
+{
+	unsigned int col, i;
+	
+	for( i=0; sha_pon_togglekey[i]!=0; i++ ) {
+		input_report_key(sha_ponkbd->input, sha_pon_togglekey[i], 0);
+	}
+	for (col = 0; col < KB_COLS; col++) {
+		sha_ponkbd->kbdscan_toggle_state[col] = 0;
+	}
+}
+
+
+/*
+ * Toggle-keys : Shift, Ctrl, Fn(Alt)
+ * 
+ * Pressing toggle-key, toggle mode ON/OFF.
+ * Pressing toggle-key continuous and pressing NOT-toggle-key, turn mode to OFF after input.
+ * 
+ */
+static void sha_pon_report_col(struct sha_ponkbd *sha_ponkbd,
+				unsigned int col, unsigned int rowd, unsigned int rowd_changed, unsigned int toggle_pressed)
+{
+	unsigned int scancode, pressed, keycode;
+	unsigned int row, changed, togglekey;
+	
+	for (row = 0; row < KB_ROWS; row++) {
+		changed = rowd_changed & KB_ROWMASK(row);
+		pressed = rowd & KB_ROWMASK(row);
+		togglekey = sha_ponkbd->kbdscan_togglekey[col] & KB_ROWMASK(row);
+		
+		scancode = SCANCODE(row, col);
+		keycode = sha_ponkbd->keycode[scancode];
+		
+#if(ARCH_SHA_PON_DEBUG==1)
+		/* print keycode */
+		if( changed && pressed ) {
+			printk( KERN_WARNING "(sha_ponkbd: keycode=%d/0x%04x, col=%d, row=%d)\n", 
+					keycode, keycode, col, row );
+		}
+#endif
+		if(togglekey) {
+			if( changed && pressed ) {
+				input_report_key(sha_ponkbd->input, keycode, pressed);
+			}
+			if( changed && !pressed ) {
+				if( sha_ponkbd->toggle_enable ) {
+					sha_ponkbd->kbdscan_toggle_state[col] ^= KB_ROWMASK(row);
+					input_report_key(sha_ponkbd->input, keycode, 
+							sha_ponkbd->kbdscan_toggle_state[col]&KB_ROWMASK(row));
+				} else {
+					sha_pon_clear_toggle(sha_ponkbd);
+					sha_ponkbd->toggle_enable = 1;
+				}
+			}
+		} else {
+			if( changed || pressed ) {
+				input_report_key(sha_ponkbd->input, keycode, pressed);
+				if( toggle_pressed ) {
+					sha_ponkbd->toggle_enable = 0;
+				} else {
+					sha_pon_clear_toggle(sha_ponkbd);
+					sha_ponkbd->toggle_enable = 1;
+				}
+			}
+		}
+	}
+}
+
+
+static inline void sha_pon_discharge(void)
+{
+	SHA_PON_CPLD00 = 0x00;
+	SHA_PON_CPLD04 = 0x00;
+	SHA_PON_CPLD08 = 0x01;
+	udelay(KB_ACTIVATE_DELAY);
+	SHA_PON_CPLD08 = 0x00;
+}
+
+
+static inline unsigned int sha_pon_scanline(unsigned int col)
+{
+	unsigned int rowd;
+	
+	SHA_PON_CPLD00 = ((1<<col)>>0)&0xff;
+	SHA_PON_CPLD04 = ((1<<col)>>8)&0xff;
+	udelay(KB_ACTIVATE_DELAY);
+	SHA_PON_CPLD08 = 0x00;
+	
+	rowd = SHA_PON_CPLD08 & 0xff;
+	return( rowd );
+}
+
+
+/* Scan the hardware keyboard and push any changes up through the input layer */
+static void sha_pon_work(struct work_struct *not_use)
+{
+	unsigned int col, rowd;
+	unsigned int toggle_pressed;
+	
+	toggle_pressed = 0;
+	for (col = 0; col < KB_COLS; col++) {
+		toggle_pressed |= ( sha_ponkbd->kbdscan_state2[col] & sha_ponkbd->kbdscan_togglekey[col] );
+	}
+	
+	col = 0;
+	do {
+		sha_pon_discharge();
+		rowd = sha_pon_scanline(col);
+		
+		if (rowd != sha_ponkbd->kbdscan_state[col]) {
+			sha_ponkbd->kbdscan_count[col] = 0;
+			sha_ponkbd->kbdscan_state[col] = rowd;
+		} else if (++sha_ponkbd->kbdscan_count[col] >= KBDSCAN_STABLE_COUNT) {
+			sha_ponkbd->kbdscan_count[col] = KBDSCAN_STABLE_COUNT;
+			sha_pon_report_col(sha_ponkbd, col, rowd, rowd^sha_ponkbd->kbdscan_state2[col], toggle_pressed);
+			sha_ponkbd->kbdscan_state2[col] = rowd;
+			col++;
+		}
+	} while (col < KB_COLS);
+
+#if( ARCH_SHA_PON_DEBUG==3 )
+	ARCH_SHA_PON_DEBUG_CAPTUREVAR;
+	printk( KERN_WARNING "\n\nNow capture GPIO mode." );
+#endif
+#if( ARCH_SHA_PON_DEBUG==2 || ARCH_SHA_PON_DEBUG==3 )
+	ARCH_SHA_PON_DEBUG_PRINTVAR
+#endif
+#if( ARCH_SHA_PON_DEBUG==4 )
+	ARCH_SHA_PON_DEBUG_GPIOIN
+#endif
+#if( ARCH_SHA_PON_DEBUG==5 )
+	ARCH_SHA_PON_DEBUG_GPIOOUT
+#endif
+#if( ARCH_SHA_PON_DEBUG==6 )
+	ARCH_SHA_PON_DEBUG_CPLDIN
+#endif
+#if( ARCH_SHA_PON_DEBUG==7 )
+	ARCH_SHA_PON_DEBUG_CPLDOUT
+#endif
+	
+	sha_pon_discharge();
+	input_sync(sha_ponkbd->input);
+
+	schedule_delayed_work(&sha_ponkbd->workq, msecs_to_jiffies(SCAN_INTERVAL));
+}
+
+static int sha_pon_open(struct input_dev *indev)
+{
+	struct sha_ponkbd *sha_ponkbd = indev->private;
+
+	schedule_delayed_work(&sha_ponkbd->workq, msecs_to_jiffies(SCAN_INTERVAL));
+
+	return 0;
+}
+
+static void sha_pon_close(struct input_dev *indev)
+{
+	struct sha_ponkbd *sha_ponkbd = indev->private;
+
+	cancel_delayed_work(&sha_ponkbd->workq);
+	flush_scheduled_work();
+}
+
+static int __devinit sha_pon_probe(struct platform_device *pdev)
+{
+	struct input_dev *input_dev;
+	int i;
+	int error;
+	unsigned int row, col, scancode;
+	
+	sha_ponkbd = kzalloc(sizeof(struct sha_ponkbd), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!sha_ponkbd || !input_dev) {
+		error = -ENOMEM;
+		goto fail;
+	}
+
+	sha_ponkbd->input = input_dev;
+
+	/* Init keyboard rescan workqueue */
+	INIT_DELAYED_WORK(&sha_ponkbd->workq, sha_pon_work);
+
+	input_dev->name = "Sha_pon Keyboard";
+	input_dev->phys = "sha_ponkbd/input0";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->cdev.dev = &pdev->dev;
+	input_dev->private = sha_ponkbd;
+
+	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+	input_dev->keycode = sha_ponkbd->keycode;
+	input_dev->keycodesize = sizeof(unsigned char);
+	input_dev->keycodemax = KB_ROWS*KB_COLS;
+
+	for( col=0; col<KB_COLS; col++ ) {
+		sha_ponkbd->kbdscan_state[col] = 0;
+		sha_ponkbd->kbdscan_state2[col] = 0;
+		sha_ponkbd->kbdscan_count[col] = 0;
+		sha_ponkbd->kbdscan_togglekey[col] = 0;
+		sha_ponkbd->kbdscan_toggle_state[col] = 0;
+	}
+	sha_ponkbd->toggle_enable = 1;
+	for (col = 0; col < KB_COLS; col++) {
+		for (row = 0; row < KB_ROWS; row++) {
+			scancode = SCANCODE(row, col);
+			sha_ponkbd->keycode[scancode] = sha_pon_keycode[scancode];
+			set_bit(sha_ponkbd->keycode[scancode], input_dev->keybit);
+			for( i=0; sha_pon_togglekey[i]!=0; i++ ) {
+				if( sha_ponkbd->keycode[scancode] == sha_pon_togglekey[i] ) {
+					sha_ponkbd->kbdscan_togglekey[col] |= KB_ROWMASK(row);
+				}
+			}
+		}
+	}
+	clear_bit(0, input_dev->keybit);
+	
+	input_dev->open = sha_pon_open;
+	input_dev->close = sha_pon_close;
+
+	error = input_register_device(sha_ponkbd->input);
+	if (error)
+		goto fail;
+
+	return 0;
+
+ fail:	kfree(sha_ponkbd);
+	input_free_device(input_dev);
+	return error;
+}
+
+static int __devexit sha_pon_remove(struct platform_device *pdev)
+{
+	struct sha_ponkbd *sha_ponkbd = platform_get_drvdata(pdev);
+
+	input_unregister_device(sha_ponkbd->input);
+	kfree(sha_ponkbd);
+
+	return 0;
+}
+
+static struct platform_driver sha_pon_driver = {
+	.probe		= sha_pon_probe,
+	.remove		= __devexit_p(sha_pon_remove),
+	.driver		= {
+		.name	= "sha_pon-keyboard",
+	},
+};
+
+static int __init sha_pon_init(void)
+{
+	return platform_driver_register(&sha_pon_driver);
+}
+
+static void __exit sha_pon_exit(void)
+{
+	platform_driver_unregister(&sha_pon_driver);
+}
+
+module_init(sha_pon_init);
+module_exit(sha_pon_exit);
+
+MODULE_AUTHOR("zaki");
+MODULE_DESCRIPTION("Sha_pon Keyboard Driver");
+MODULE_LICENSE("GPLv2");
diff --git a/include/asm-arm/arch-pxa/sha_pon.h b/include/asm-arm/arch-pxa/sha_pon.h
new file mode 100644
index 0000000..2e8cde1
--- /dev/null
+++ b/include/asm-arm/arch-pxa/sha_pon.h
@@ -0,0 +1,240 @@
+/*
+ *  linux/include/asm-arm/arch-pxa/sha_pon.h
+ *
+ *  SHA_PON specific bits definition
+ *
+ *  Auther: zaki
+ *
+ *  Based on linux/include/asm-arm/arch-aaec2000/aaed2000.h
+ *
+ *  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.
+ */
+
+#ifndef __ASM_ARCH_SHA_PON_H
+#define __ASM_ARCH_SHA_PON_H
+
+/* 0:not debug mode, 1:keyboard, 2:initial gpio, 3:running gpio, 4:gpio input, 5:gpio output */
+/* 6:cpld input, 7:cpld output */
+#define	ARCH_SHA_PON_DEBUG	0
+
+
+/* External GPIOs. */
+
+#define EXT_GPIO_PBASE	PXA_CS2_PHYS
+#define EXT_GPIO_VBASE	0xf8100000
+#define EXT_GPIO_LENGTH	0x00100000
+
+#define __ext_gpio_p2v(x)	((x) - EXT_GPIO_PBASE + EXT_GPIO_VBASE)
+#define __ext_gpio_v2p(x)	((x) + EXT_GPIO_PBASE - EXT_GPIO_VBASE)
+
+#define __EXT_GPIO_REG(x)	(*((volatile u8 *)__ext_gpio_p2v(x)))
+#define __EXT_GPIO_PREG(x)	(__ext_gpio_v2p((u32)&(x)))
+
+
+#define SHA_PON_CPLD00	__EXT_GPIO_REG(EXT_GPIO_PBASE+0x00)
+#define SHA_PON_CPLD04	__EXT_GPIO_REG(EXT_GPIO_PBASE+0x04)
+#define SHA_PON_CPLD08	__EXT_GPIO_REG(EXT_GPIO_PBASE+0x08)
+#define SHA_PON_CPLD0c	__EXT_GPIO_REG(EXT_GPIO_PBASE+0x0c)
+#define SHA_PON_CPLD10	__EXT_GPIO_REG(EXT_GPIO_PBASE+0x10)
+#define SHA_PON_CPLD14	__EXT_GPIO_REG(EXT_GPIO_PBASE+0x14)
+#define SHA_PON_CPLD18	__EXT_GPIO_REG(EXT_GPIO_PBASE+0x18)
+#define SHA_PON_CPLD1c	__EXT_GPIO_REG(EXT_GPIO_PBASE+0x1c)
+
+
+/* for debug mode */
+#if( ARCH_SHA_PON_DEBUG==2 || ARCH_SHA_PON_DEBUG==3 || ARCH_SHA_PON_DEBUG==4 || ARCH_SHA_PON_DEBUG==5 || ARCH_SHA_PON_DEBUG==6 || ARCH_SHA_PON_DEBUG==7 )
+
+extern unsigned int
+	sha_gafr0u,sha_gafr0l,
+	sha_gafr1u,sha_gafr1l,
+	sha_gafr2u,sha_gafr2l,
+	sha_gafr3u,sha_gafr3l,
+	sha_gpdr0,sha_gpdr1,sha_gpdr2,sha_gpdr3,sha_gpioout;
+
+#define	ARCH_SHA_PON_DEBUG_GLOBALVAR				\
+	unsigned int									\
+		sha_gafr0u,sha_gafr0l,						\
+		sha_gafr1u,sha_gafr1l,						\
+		sha_gafr2u,sha_gafr2l,						\
+		sha_gafr3u,sha_gafr3l,						\
+		sha_gpdr0,sha_gpdr1,sha_gpdr2,sha_gpdr3,sha_gpioout;
+
+#define	ARCH_SHA_PON_DEBUG_CAPTUREVAR				\
+	sha_gafr0u=GAFR0_U,sha_gafr0l=GAFR0_L,			\
+	sha_gafr1u=GAFR1_U,sha_gafr1l=GAFR1_L,			\
+	sha_gafr2u=GAFR2_U,sha_gafr2l=GAFR2_L,			\
+	sha_gafr3u=GAFR3_U,sha_gafr3l=GAFR3_L,			\
+	sha_gpdr0=GPDR0, sha_gpdr1=GPDR1, sha_gpdr2=GPDR2, sha_gpdr3=GPDR3 ,sha_gpioout=0;
+
+#define	ARCH_SHA_PON_DEBUG_PRINTVAR					\
+	printk( KERN_WARNING "\n\nGPIO mode\n"			\
+		"GPDR0    =%04x_%04x\n"					\
+		"GPDR1    =%04x_%04x\n"					\
+		"GPDR2    =%04x_%04x\n"					\
+		"GPDR3    =%04x_%04x\n"					\
+		"GAFR0_U/L=%04x_%04x/%04x_%04x\n"			\
+		"GAFR1_U/L=%04x_%04x/%04x_%04x\n"			\
+		"GAFR2_U/L=%04x_%04x/%04x_%04x\n"			\
+		"GAFR3_U/L=%04x_%04x/%04x_%04x\n"			\
+		,((sha_gpdr0>>16)&0xffff),((sha_gpdr0)&0xffff)		\
+		,((sha_gpdr1>>16)&0xffff),((sha_gpdr1)&0xffff)		\
+		,((sha_gpdr2>>16)&0xffff),((sha_gpdr2)&0xffff)		\
+		,((sha_gpdr3>>16)&0xffff),((sha_gpdr3)&0xffff)		\
+		,((sha_gafr0u>>16)&0xffff),((sha_gafr0u)&0xffff),((sha_gafr0l>>16)&0xffff),((sha_gafr0l)&0xffff)	\
+		,((sha_gafr1u>>16)&0xffff),((sha_gafr1u)&0xffff),((sha_gafr1l>>16)&0xffff),((sha_gafr1l)&0xffff)	\
+		,((sha_gafr2u>>16)&0xffff),((sha_gafr2u)&0xffff),((sha_gafr2l>>16)&0xffff),((sha_gafr2l)&0xffff)	\
+		,((sha_gafr3u>>16)&0xffff),((sha_gafr3u)&0xffff),((sha_gafr3l>>16)&0xffff),((sha_gafr3l)&0xffff)	\
+	);
+
+
+#define	ARCH_SHA_PON_DEBUG_GPIOIN16( data16, dir16, mode32, msgp )			\
+{																			\
+	int	x;																	\
+	for( x=0; x<16; x++ ) {													\
+		if( ((dir16)&(1<<(15-x)))==0 && ((mode32)&(3<<((15-x)*2)))==0 ) {		\
+			(msgp)[x+(x>>2)] = (((data16)>>(15-x))&1)?'1':'0';							\
+		} else {															\
+			(msgp)[x+(x>>2)] = '*';													\
+		}																	\
+	}																		\
+	(msgp)[4] = '_';													\
+	(msgp)[9] = '_';													\
+	(msgp)[14] = '_';													\
+	(msgp)[19] = '_';													\
+}
+
+#define	ARCH_SHA_PON_DEBUG_GPIOIN					\
+{																				\
+	unsigned int	r;															\
+	char	msg[80];															\
+	printk( KERN_WARNING "\n\n\n\nGPIO INPUT LEVEL\n" );				\
+	r=GPLR0;																	\
+	ARCH_SHA_PON_DEBUG_GPIOIN16( r>>16, sha_gpdr0>>16, sha_gafr0u, msg );	\
+	ARCH_SHA_PON_DEBUG_GPIOIN16( r, sha_gpdr0, sha_gafr0l, msg+20 );			\
+	msg[39] = '\0';																\
+	printk( KERN_WARNING "GPLR0=%s\n", msg );				\
+	r=GPLR1;																	\
+	ARCH_SHA_PON_DEBUG_GPIOIN16( r>>16, sha_gpdr1>>16, sha_gafr1u, msg );	\
+	ARCH_SHA_PON_DEBUG_GPIOIN16( r, sha_gpdr1, sha_gafr1l, msg+20 );			\
+	msg[39] = '\0';																\
+	printk( KERN_WARNING "GPLR1=%s\n", msg );				\
+	r=GPLR2;																	\
+	ARCH_SHA_PON_DEBUG_GPIOIN16( r>>16, sha_gpdr2>>16, sha_gafr2u, msg );	\
+	ARCH_SHA_PON_DEBUG_GPIOIN16( r, sha_gpdr2, sha_gafr2l, msg+20 );			\
+	msg[39] = '\0';																\
+	printk( KERN_WARNING "GPLR2=%s\n", msg );				\
+	r=GPLR3;																	\
+	ARCH_SHA_PON_DEBUG_GPIOIN16( r>>16, sha_gpdr3>>16, sha_gafr3u, msg );	\
+	ARCH_SHA_PON_DEBUG_GPIOIN16( r, sha_gpdr3, sha_gafr3l, msg+20 );			\
+	msg[39] = '\0';																\
+	printk( KERN_WARNING "GPLR3=%s\n", msg );				\
+}
+
+
+#define	ARCH_SHA_PON_DEBUG_GPIOOUT										\
+{																		\
+	int	set, port, bl;													\
+	unsigned int	bit;												\
+	port = (sha_gpioout>>7)&0x7f;										\
+	if( port>120 ) {													\
+		port = sha_gpioout = 0;											\
+	}																	\
+	set = sha_gpioout&0x7f;												\
+	bl = sha_gpioout&0x04;												\
+	bit = 1<<(port&0x1f);												\
+	if( set==0 ) {														\
+		printk( KERN_WARNING "GPIO(%d) blinking...\n", port );			\
+	}																	\
+	if( bl ) {															\
+		if( port<32 ) {														\
+			GPSR0 = bit;											\
+		} else if( port<64 ) {												\
+			GPSR1 = bit;											\
+		} else if( port<96 ) {												\
+			GPSR2 = bit;											\
+		} else if( port<128 ) {												\
+			GPSR3 = bit;											\
+		}																	\
+	} else {																\
+		if( port<32 ) {														\
+			GPCR0 = bit;											\
+		} else if( port<64 ) {												\
+			GPCR1 = bit;											\
+		} else if( port<96 ) {												\
+			GPCR2 = bit;											\
+		} else if( port<128 ) {												\
+			GPCR3 = bit;											\
+		}																	\
+	}																	\
+	sha_gpioout++;														\
+}
+
+
+#define	ARCH_SHA_PON_DEBUG_CPLDIN8( data8, msgp )			\
+{																			\
+	int	x;																	\
+	for( x=0; x<8; x++ ) {													\
+		(msgp)[x+(x>>2)] = (((data8)>>(7-x))&1)?'1':'0';							\
+	}																		\
+	(msgp)[4] = '_';													\
+}
+
+#define	ARCH_SHA_PON_DEBUG_CPLDIN										\
+{																		\
+	unsigned int	a, r;												\
+	char	msg[80];													\
+	printk( KERN_WARNING "\n\n\n\nCPLD INPUT DATA\n" );					\
+	for( a=0x00; a<0x1f; a+=0x04 ) {									\
+		r = __EXT_GPIO_REG(EXT_GPIO_PBASE+a);							\
+		ARCH_SHA_PON_DEBUG_CPLDIN8( r, msg )							\
+		msg[9] = '\0';													\
+		printk( KERN_WARNING "CPLD(%02x)=%s\n", a, msg );				\
+	}																	\
+}
+
+
+#define	ARCH_SHA_PON_DEBUG_CPLDOUT										\
+{																		\
+	int	set, port, bl;													\
+	unsigned int	bit;												\
+	unsigned int	a, r;												\
+	char	msg[80];													\
+	port = (sha_gpioout>>7)&0x7f;										\
+	if( port>=8*8 ) {													\
+		port = sha_gpioout = 0;											\
+	}																	\
+	set = sha_gpioout&0x07;												\
+	bl = sha_gpioout&0x04;												\
+	bit = port&0x07;												\
+	if( set==1 ) {														\
+		printk( KERN_WARNING "\n\n\nCPLD(0x%02x,bit%d) blinking...\n", (port>>3)<<2, bit );		\
+		for( a=0x00; a<0x1f; a+=0x04 ) {									\
+			r = __EXT_GPIO_REG(EXT_GPIO_PBASE+a);							\
+			ARCH_SHA_PON_DEBUG_CPLDIN8( r, msg )							\
+			msg[9] = '\0';													\
+			printk( KERN_WARNING "CPLD IN(%02x)=%s\n", a, msg );				\
+		}																	\
+	}																	\
+	__EXT_GPIO_REG(EXT_GPIO_PBASE+((port>>3)<<2)) = (bl)?0:(1<<bit);	\
+	sha_gpioout++;														\
+}
+
+
+#else
+
+#define	ARCH_SHA_PON_DEBUG_GLOBALVAR		/* NOP */
+#define	ARCH_SHA_PON_DEBUG_CAPTUREVAR		/* NOP */
+#define	ARCH_SHA_PON_DEBUG_PRINTVAR			/* NOP */
+
+#define	ARCH_SHA_PON_DEBUG_GPIOIN			/* NOP */
+#define	ARCH_SHA_PON_DEBUG_GPIOOUT			/* NOP */
+#define	ARCH_SHA_PON_DEBUG_CPLDIN			/* NOP */
+#define	ARCH_SHA_PON_DEBUG_CPLDOUT			/* NOP */
+
+#endif	/*ARCH_SHA_PON_DEBUG*/
+
+
+
+#endif /* __ARM_ARCH_SHA_PON_H */
-- 
1.4.4.4

