[ARM] 4331/3: Support for Micrel/Kendin KS8695 processor

Add core support for the Kendin/Micrel KS8695 processor family.

It is an ARM922-T based SoC with integrated USART, 4-port Ethernet
Switch, WAN Ethernet port, and optional PCI Host bridge, etc.
 http://www.micrel.com/page.do?page=product-info/sys_on_chip.jsp

This patch is based on earlier patches from Lennert Buytenhek, Ben
Dooks and Greg Ungerer posted to the arm-linux-kernel mailing list in
March 2006;  and Micrel's 2.6.9 port.

Signed-off-by: Andrew Victor <andrew@sanpeople.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 52c209e..638f1d8 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -305,6 +305,12 @@
 	  If you have any questions or comments about the Linux kernel port
 	  to this board, send e-mail to <sjhill@cotw.com>.
 
+config ARCH_KS8695
+	bool "Micrel/Kendin KS8695"
+	help
+	  Support for Micrel/Kendin KS8695 "Centaur" (ARM922T) based
+	  System-on-Chip devices.
+
 config ARCH_NS9XXX
 	bool "NetSilicon NS9xxx"
 	help
@@ -451,6 +457,8 @@
 
 source "arch/arm/mach-davinci/Kconfig"
 
+source "arch/arm/mach-ks8695/Kconfig"
+
 # Definitions to make life easier
 config ARCH_ACORN
 	bool
@@ -511,7 +519,7 @@
 	bool
 
 config PCI
-	bool "PCI support" if ARCH_INTEGRATOR_AP || ARCH_VERSATILE_PB || ARCH_IXP4XX
+	bool "PCI support" if ARCH_INTEGRATOR_AP || ARCH_VERSATILE_PB || ARCH_IXP4XX || ARCH_KS8695
 	help
 	  Find out whether you have a PCI motherboard. PCI is the name of a
 	  bus system, i.e. the way the CPU talks to the other stuff inside
@@ -680,7 +688,8 @@
 		   ARCH_LUBBOCK || MACH_MAINSTONE || ARCH_NETWINDER || \
 		   ARCH_OMAP || ARCH_P720T || ARCH_PXA_IDP || \
 		   ARCH_SA1100 || ARCH_SHARK || ARCH_VERSATILE || \
-		   ARCH_AT91 || MACH_TRIZEPS4 || ARCH_DAVINCI
+		   ARCH_AT91 || MACH_TRIZEPS4 || ARCH_DAVINCI || \
+		   ARCH_KS8695
 	help
 	  If you say Y here, the LEDs on your machine will be used
 	  to provide useful information about your current system status.
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index d568d86..cbd5010 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -136,6 +136,7 @@
  machine-$(CONFIG_ARCH_NS9XXX)	   := ns9xxx
  textofs-$(CONFIG_ARCH_NS9XXX)	   := 0x00108000
  machine-$(CONFIG_ARCH_DAVINCI)	   := davinci
+ machine-$(CONFIG_ARCH_KS8695)     := ks8695
 
 ifeq ($(CONFIG_ARCH_EBSA110),y)
 # This is what happens if you forget the IOCS16 line.
diff --git a/arch/arm/mach-ks8695/Kconfig b/arch/arm/mach-ks8695/Kconfig
new file mode 100644
index 0000000..44d14aa
--- /dev/null
+++ b/arch/arm/mach-ks8695/Kconfig
@@ -0,0 +1,8 @@
+if ARCH_KS8695
+
+menu "Kendin/Micrel KS8695 Implementations"
+
+
+endmenu
+
+endif
diff --git a/arch/arm/mach-ks8695/Makefile b/arch/arm/mach-ks8695/Makefile
new file mode 100644
index 0000000..0a0f496
--- /dev/null
+++ b/arch/arm/mach-ks8695/Makefile
@@ -0,0 +1,14 @@
+# arch/arm/mach-ks8695/Makefile
+#
+# Makefile for KS8695 architecture support
+#
+
+obj-y				:= cpu.o irq.o time.o devices.o
+obj-m				:=
+obj-n				:=
+obj-				:=
+
+# PCI support is optional
+#obj-$(CONFIG_PCI)		+= pci.o
+
+# Board-specific support
diff --git a/arch/arm/mach-ks8695/Makefile.boot b/arch/arm/mach-ks8695/Makefile.boot
new file mode 100644
index 0000000..48eb2cb
--- /dev/null
+++ b/arch/arm/mach-ks8695/Makefile.boot
@@ -0,0 +1,8 @@
+# Note: the following conditions must always be true:
+#   ZRELADDR == virt_to_phys(TEXTADDR)
+#   PARAMS_PHYS must be within 4MB of ZRELADDR
+#   INITRD_PHYS must be in RAM
+
+   zreladdr-y	:= 0x00008000
+params_phys-y	:= 0x00000100
+initrd_phys-y	:= 0x00800000
diff --git a/arch/arm/mach-ks8695/cpu.c b/arch/arm/mach-ks8695/cpu.c
new file mode 100644
index 0000000..407d255
--- /dev/null
+++ b/arch/arm/mach-ks8695/cpu.c
@@ -0,0 +1,73 @@
+/*
+ * arch/arm/mach-ks8695/cpu.c
+ *
+ * Copyright (C) 2006 Ben Dooks <ben@simtec.co.uk>
+ * Copyright (C) 2006 Simtec Electronics
+ *
+ * KS8695 CPU support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <asm/arch/regs-sys.h>
+#include <asm/arch/regs-misc.h>
+
+
+static struct __initdata map_desc ks8695_io_desc[] = {
+	{
+		.virtual	= KS8695_IO_VA,
+		.pfn		= __phys_to_pfn(KS8695_IO_PA),
+		.length		= KS8695_IO_SIZE,
+		.type		= MT_DEVICE,
+	}
+};
+
+static void __init ks8695_processor_info(void)
+{
+	unsigned long id, rev;
+
+	id = __raw_readl(KS8695_MISC_VA + KS8695_DID);
+	rev = __raw_readl(KS8695_MISC_VA + KS8695_RID);
+
+	printk("KS8695 ID=%04lx  SubID=%02lx  Revision=%02lx\n", (id & DID_ID), (rev & RID_SUBID), (rev & RID_REVISION));
+}
+
+static unsigned int sysclk[8] = { 125000000, 100000000, 62500000, 50000000, 41700000, 33300000, 31300000, 25000000 };
+static unsigned int cpuclk[8] = { 166000000, 166000000, 83000000, 83000000, 55300000, 55300000, 41500000, 41500000 };
+
+static void __init ks8695_clock_info(void)
+{
+	unsigned int scdc = __raw_readl(KS8695_SYS_VA + KS8695_CLKCON) & CLKCON_SCDC;
+
+	printk("Clocks: System %u MHz, CPU %u MHz\n",
+			sysclk[scdc] / 1000000, cpuclk[scdc] / 1000000);
+}
+
+void __init ks8695_map_io(void)
+{
+	iotable_init(ks8695_io_desc, ARRAY_SIZE(ks8695_io_desc));
+
+	ks8695_processor_info();
+	ks8695_clock_info();
+}
diff --git a/arch/arm/mach-ks8695/devices.c b/arch/arm/mach-ks8695/devices.c
new file mode 100644
index 0000000..386593f8
--- /dev/null
+++ b/arch/arm/mach-ks8695/devices.c
@@ -0,0 +1,191 @@
+/*
+ * arch/arm/mach-ks8695/devices.c
+ *
+ * Copyright (C) 2006 Andrew Victor
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <linux/platform_device.h>
+
+#include <asm/arch/regs-wan.h>
+#include <asm/arch/regs-lan.h>
+#include <asm/arch/regs-hpna.h>
+
+
+/* --------------------------------------------------------------------
+ *  Ethernet
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_ARM_KS8695_ETHER) || defined(CONFIG_ARM_KS8695_ETHER_MODULE)
+static u64 eth_dmamask = 0xffffffffUL;
+
+static struct resource ks8695_wan_resources[] = {
+	[0] = {
+		.start	= KS8695_WAN_VA,
+		.end	= KS8695_WAN_VA + 0x00ff,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.name	= "WAN RX",
+		.start	= KS8695_IRQ_WAN_RX_STATUS,
+		.end	= KS8695_IRQ_WAN_RX_STATUS,
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.name	= "WAN TX",
+		.start	= KS8695_IRQ_WAN_TX_STATUS,
+		.end	= KS8695_IRQ_WAN_TX_STATUS,
+		.flags	= IORESOURCE_IRQ,
+	},
+	[3] = {
+		.name	= "WAN Link",
+		.start	= KS8695_IRQ_WAN_LINK,
+		.end	= KS8695_IRQ_WAN_LINK,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device ks8695_wan_device = {
+	.name		= "ks8695_ether",
+	.id		= 0,
+	.dev		= {
+				.dma_mask		= &eth_dmamask,
+				.coherent_dma_mask	= 0xffffffff,
+	},
+	.resource	= ks8695_wan_resources,
+	.num_resources	= ARRAY_SIZE(ks8695_wan_resources),
+};
+
+
+static struct resource ks8695_lan_resources[] = {
+	[0] = {
+		.start	= KS8695_LAN_VA,
+		.end	= KS8695_LAN_VA + 0x00ff,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.name	= "LAN RX",
+		.start	= KS8695_IRQ_LAN_RX_STATUS,
+		.end	= KS8695_IRQ_LAN_RX_STATUS,
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.name	= "LAN TX",
+		.start	= KS8695_IRQ_LAN_TX_STATUS,
+		.end	= KS8695_IRQ_LAN_TX_STATUS,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device ks8695_lan_device = {
+	.name		= "ks8695_ether",
+	.id		= 1,
+	.dev		= {
+				.dma_mask		= &eth_dmamask,
+				.coherent_dma_mask	= 0xffffffff,
+	},
+	.resource	= ks8695_lan_resources,
+	.num_resources	= ARRAY_SIZE(ks8695_lan_resources),
+};
+
+
+static struct resource ks8695_hpna_resources[] = {
+	[0] = {
+		.start	= KS8695_HPNA_VA,
+		.end	= KS8695_HPNA_VA + 0x00ff,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.name	= "HPNA RX",
+		.start	= KS8695_IRQ_HPNA_RX_STATUS,
+		.end	= KS8695_IRQ_HPNA_RX_STATUS,
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.name	= "HPNA TX",
+		.start	= KS8695_IRQ_HPNA_TX_STATUS,
+		.end	= KS8695_IRQ_HPNA_TX_STATUS,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device ks8695_hpna_device = {
+	.name		= "ks8695_ether",
+	.id		= 2,
+	.dev		= {
+				.dma_mask		= &eth_dmamask,
+				.coherent_dma_mask	= 0xffffffff,
+	},
+	.resource	= ks8695_hpna_resources,
+	.num_resources	= ARRAY_SIZE(ks8695_hpna_resources),
+};
+
+void __init ks8695_add_device_wan(void)
+{
+	platform_device_register(&ks8695_wan_device);
+}
+
+void __init ks8695_add_device_lan(void)
+{
+	platform_device_register(&ks8695_lan_device);
+}
+
+void __init ks8696_add_device_hpna(void)
+{
+	platform_device_register(&ks8695_hpna_device);
+}
+#else
+void __init ks8695_add_device_wan(void) {}
+void __init ks8695_add_device_lan(void) {}
+void __init ks8696_add_device_hpna(void) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ *  Watchdog
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_KS8695_WATCHDOG) || defined(CONFIG_KS8695_WATCHDOG_MODULE)
+static struct platform_device ks8695_wdt_device = {
+	.name		= "ks8695_wdt",
+	.id		= -1,
+	.num_resources	= 0,
+};
+
+static void __init ks8695_add_device_watchdog(void)
+{
+	platform_device_register(&ks8695_wdt_device);
+}
+#else
+static void __init ks8695_add_device_watchdog(void) {}
+#endif
+
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * These devices are always present and don't need any board-specific
+ * setup.
+ */
+static int __init ks8695_add_standard_devices(void)
+{
+	ks8695_add_device_watchdog();
+	return 0;
+}
+
+arch_initcall(ks8695_add_standard_devices);
diff --git a/arch/arm/mach-ks8695/generic.h b/arch/arm/mach-ks8695/generic.h
new file mode 100644
index 0000000..2fbfab8
--- /dev/null
+++ b/arch/arm/mach-ks8695/generic.h
@@ -0,0 +1,15 @@
+/*
+ * arch/arm/mach-ks8695/generic.h
+ *
+ * Copyright (C) 2006 Ben Dooks <ben@simtec.co.uk>
+ * Copyright (C) 2006 Simtec Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+*/
+
+extern __init void ks8695_map_io(void);
+extern __init void ks8695_init_irq(void);
+extern struct sys_timer ks8695_timer;
diff --git a/arch/arm/mach-ks8695/irq.c b/arch/arm/mach-ks8695/irq.c
new file mode 100644
index 0000000..2407bba
--- /dev/null
+++ b/arch/arm/mach-ks8695/irq.c
@@ -0,0 +1,175 @@
+/*
+ * arch/arm/mach-ks8695/irq.c
+ *
+ * Copyright (C) 2006 Ben Dooks <ben@simtec.co.uk>
+ * Copyright (C) 2006 Simtec Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/ptrace.h>
+#include <linux/sysdev.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#include <asm/mach/irq.h>
+
+#include <asm/arch/regs-irq.h>
+#include <asm/arch/regs-gpio.h>
+
+static void ks8695_irq_mask(unsigned int irqno)
+{
+	unsigned long inten;
+
+	inten = __raw_readl(KS8695_IRQ_VA + KS8695_INTEN);
+	inten &= ~(1 << irqno);
+
+	__raw_writel(inten, KS8695_IRQ_VA + KS8695_INTEN);
+}
+
+static void ks8695_irq_unmask(unsigned int irqno)
+{
+	unsigned long inten;
+
+	inten = __raw_readl(KS8695_IRQ_VA + KS8695_INTEN);
+	inten |= (1 << irqno);
+
+	__raw_writel(inten, KS8695_IRQ_VA + KS8695_INTEN);
+}
+
+static void ks8695_irq_ack(unsigned int irqno)
+{
+	__raw_writel((1 << irqno), KS8695_IRQ_VA + KS8695_INTST);
+}
+
+
+static struct irq_chip ks8695_irq_level_chip;
+static struct irq_chip ks8695_irq_edge_chip;
+
+
+static int ks8695_irq_set_type(unsigned int irqno, unsigned int type)
+{
+	unsigned long ctrl, mode;
+	unsigned short level_triggered = 0;
+
+	ctrl = __raw_readl(KS8695_GPIO_VA + KS8695_IOPC);
+
+	switch (type) {
+		case IRQT_HIGH:
+			mode = IOPC_TM_HIGH;
+			level_triggered = 1;
+			break;
+		case IRQT_LOW:
+			mode = IOPC_TM_LOW;
+			level_triggered = 1;
+			break;
+		case IRQT_RISING:
+			mode = IOPC_TM_RISING;
+			break;
+		case IRQT_FALLING:
+			mode = IOPC_TM_FALLING;
+			break;
+		case IRQT_BOTHEDGE:
+			mode = IOPC_TM_EDGE;
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	switch (irqno) {
+		case KS8695_IRQ_EXTERN0:
+			ctrl &= ~IOPC_IOEINT0TM;
+			ctrl |= IOPC_IOEINT0_MODE(mode);
+			break;
+		case KS8695_IRQ_EXTERN1:
+			ctrl &= ~IOPC_IOEINT1TM;
+			ctrl |= IOPC_IOEINT1_MODE(mode);
+			break;
+		case KS8695_IRQ_EXTERN2:
+			ctrl &= ~IOPC_IOEINT2TM;
+			ctrl |= IOPC_IOEINT2_MODE(mode);
+			break;
+		case KS8695_IRQ_EXTERN3:
+			ctrl &= ~IOPC_IOEINT3TM;
+			ctrl |= IOPC_IOEINT3_MODE(mode);
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	if (level_triggered) {
+		set_irq_chip(irqno, &ks8695_irq_level_chip);
+		set_irq_handler(irqno, handle_level_irq);
+	}
+	else {
+		set_irq_chip(irqno, &ks8695_irq_edge_chip);
+		set_irq_handler(irqno, handle_edge_irq);
+	}
+
+	__raw_writel(ctrl, KS8695_GPIO_VA + KS8695_IOPC);
+	return 0;
+}
+
+static struct irq_chip ks8695_irq_level_chip = {
+	.ack		= ks8695_irq_mask,
+	.mask		= ks8695_irq_mask,
+	.unmask		= ks8695_irq_unmask,
+	.set_type	= ks8695_irq_set_type,
+};
+
+static struct irq_chip ks8695_irq_edge_chip = {
+	.ack		= ks8695_irq_ack,
+	.mask		= ks8695_irq_mask,
+	.unmask		= ks8695_irq_unmask,
+	.set_type	= ks8695_irq_set_type,
+};
+
+void __init ks8695_init_irq(void)
+{
+	unsigned int irq;
+
+	/* Disable all interrupts initially */
+	__raw_writel(0, KS8695_IRQ_VA + KS8695_INTMC);
+	__raw_writel(0, KS8695_IRQ_VA + KS8695_INTEN);
+
+	for (irq = 0; irq < NR_IRQS; irq++) {
+		switch (irq) {
+			/* Level-triggered interrupts */
+			case KS8695_IRQ_BUS_ERROR:
+			case KS8695_IRQ_UART_MODEM_STATUS:
+			case KS8695_IRQ_UART_LINE_STATUS:
+			case KS8695_IRQ_UART_RX:
+			case KS8695_IRQ_COMM_TX:
+			case KS8695_IRQ_COMM_RX:
+				set_irq_chip(irq, &ks8695_irq_level_chip);
+				set_irq_handler(irq, handle_level_irq);
+				break;
+
+			/* Edge-triggered interrupts */
+			default:
+				ks8695_irq_ack(irq);	/* clear pending bit */
+				set_irq_chip(irq, &ks8695_irq_edge_chip);
+				set_irq_handler(irq, handle_edge_irq);
+		}
+
+		set_irq_flags(irq, IRQF_VALID);
+	}
+}
diff --git a/arch/arm/mach-ks8695/time.c b/arch/arm/mach-ks8695/time.c
new file mode 100644
index 0000000..d2c86e4
--- /dev/null
+++ b/arch/arm/mach-ks8695/time.c
@@ -0,0 +1,114 @@
+/*
+ * arch/arm/mach-ks8695/time.c
+ *
+ * Copyright (C) 2006 Ben Dooks <ben@simtec.co.uk>
+ * Copyright (C) 2006 Simtec Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+#include <asm/io.h>
+#include <asm/mach/time.h>
+
+#include <asm/arch/regs-timer.h>
+#include <asm/arch/regs-irq.h>
+
+#include "generic.h"
+
+/*
+ * Returns number of ms since last clock interrupt.  Note that interrupts
+ * will have been disabled by do_gettimeoffset()
+ */
+static unsigned long ks8695_gettimeoffset (void)
+{
+	unsigned long elapsed, tick2, intpending;
+
+	/*
+	 * Get the current number of ticks.  Note that there is a race
+	 * condition between us reading the timer and checking for an
+	 * interrupt.  We solve this by ensuring that the counter has not
+	 * reloaded between our two reads.
+	 */
+	elapsed = __raw_readl(KS8695_TMR_VA + KS8695_T1TC) + __raw_readl(KS8695_TMR_VA + KS8695_T1PD);
+	do {
+		tick2 = elapsed;
+		intpending = __raw_readl(KS8695_IRQ_VA + KS8695_INTST) & (1 << KS8695_IRQ_TIMER1);
+		elapsed = __raw_readl(KS8695_TMR_VA + KS8695_T1TC) + __raw_readl(KS8695_TMR_VA + KS8695_T1PD);
+	} while (elapsed > tick2);
+
+	/* Convert to number of ticks expired (not remaining) */
+	elapsed = (CLOCK_TICK_RATE / HZ) - elapsed;
+
+	/* Is interrupt pending?  If so, then timer has been reloaded already. */
+	if (intpending)
+		elapsed += (CLOCK_TICK_RATE / HZ);
+
+	/* Convert ticks to usecs */
+	return (unsigned long)(elapsed * (tick_nsec / 1000)) / LATCH;
+}
+
+/*
+ * IRQ handler for the timer.
+ */
+static irqreturn_t ks8695_timer_interrupt(int irq, void *dev_id)
+{
+	write_seqlock(&xtime_lock);
+	timer_tick();
+	write_sequnlock(&xtime_lock);
+
+	return IRQ_HANDLED;
+}
+
+static struct irqaction ks8695_timer_irq = {
+	.name		= "ks8695_tick",
+	.flags		= IRQF_DISABLED | IRQF_TIMER,
+	.handler	= ks8695_timer_interrupt,
+};
+
+static void ks8695_timer_setup(void)
+{
+	unsigned long tmout = CLOCK_TICK_RATE / HZ;
+	unsigned long tmcon;
+
+	/* disable timer1 */
+	tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
+	__raw_writel(tmcon & ~TMCON_T1EN, KS8695_TMR_VA + KS8695_TMCON);
+
+	__raw_writel(tmout / 2, KS8695_TMR_VA + KS8695_T1TC);
+	__raw_writel(tmout / 2, KS8695_TMR_VA + KS8695_T1PD);
+
+	/* re-enable timer1 */
+	__raw_writel(tmcon | TMCON_T1EN, KS8695_TMR_VA + KS8695_TMCON);
+}
+
+static void __init ks8695_timer_init (void)
+{
+	ks8695_timer_setup();
+
+	/* Enable timer interrupts */
+	setup_irq(KS8695_IRQ_TIMER1, &ks8695_timer_irq);
+}
+
+struct sys_timer ks8695_timer = {
+	.init		= ks8695_timer_init,
+	.offset		= ks8695_gettimeoffset,
+	.resume		= ks8695_timer_setup,
+};
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index b8320a1..15f0284 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -131,8 +131,8 @@
 # ARM922T
 config CPU_ARM922T
 	bool "Support ARM922T processor" if ARCH_INTEGRATOR
-	depends on ARCH_LH7A40X || ARCH_INTEGRATOR
-	default y if ARCH_LH7A40X
+	depends on ARCH_LH7A40X || ARCH_INTEGRATOR || ARCH_KS8695
+	default y if ARCH_LH7A40X || ARCH_KS8695
 	select CPU_32v4T
 	select CPU_ABRT_EV4T
 	select CPU_CACHE_V4WT
@@ -143,7 +143,7 @@
 	help
 	  The ARM922T is a version of the ARM920T, but with smaller
 	  instruction and data caches. It is used in Altera's
-	  Excalibur XA device family.
+	  Excalibur XA device family and Micrel's KS8695 Centaur.
 
 	  Say Y if you want support for the ARM922T processor.
 	  Otherwise, say N.