Index: kernel/arch/arm32/src/mach/gta02/gta02.c
===================================================================
--- kernel/arch/arm32/src/mach/gta02/gta02.c	(revision 0e796cc3e0abc77c3580a6189cb9b4ce08a72f79)
+++ kernel/arch/arm32/src/mach/gta02/gta02.c	(revision 41ce4d90d8c47b133bd70c5513dcbf39004b6833)
@@ -41,5 +41,9 @@
 #include <genarch/fb/visuals.h>
 #include <genarch/drivers/s3c24xx_uart/s3c24xx_uart.h>
+#include <genarch/drivers/s3c24xx_irqc/s3c24xx_irqc.h>
+#include <genarch/drivers/s3c24xx_timer/s3c24xx_timer.h>
+#include <interrupt.h>
 #include <ddi/ddi.h>
+#include <ddi/device.h>
 
 #define GTA02_MEMORY_START	0x30000000	/* physical */
@@ -52,4 +56,7 @@
 /** GTA02 framebuffer base address */
 #define GTA02_FB_BASE		0x08800000
+
+/** IRQ number used for clock */
+#define GTA02_TIMER_IRQ		S3C24XX_INT_TIMER0
 
 static void gta02_init(void);
@@ -62,5 +69,14 @@
 static void gta02_input_init(void);
 
+static void gta02_timer_irq_init(void);
+static void gta02_timer_start(void);
+static irq_ownership_t gta02_timer_irq_claim(irq_t *irq);
+static void gta02_timer_irq_handler(irq_t *irq);
+
 static void *gta02_scons_out;
+static s3c24xx_irqc_t *gta02_irqc;
+static s3c24xx_timer_t *gta02_timer;
+
+static irq_t gta02_timer_irq;
 
 struct arm_machine_ops gta02_machine_ops = {
@@ -78,8 +94,21 @@
 {
 	gta02_scons_out = (void *) hw_map(GTA02_SCONS_BASE, PAGE_SIZE);
+	gta02_irqc = (void *) hw_map(S3C24XX_IRQC_ADDRESS, PAGE_SIZE);
+	gta02_timer = (void *) hw_map(S3C24XX_TIMER_ADDRESS, PAGE_SIZE);
+
+	/* Make all interrupt sources use IRQ mode (not FIQ). */
+	pio_write_32(&gta02_irqc->intmod, 0x00000000);
+
+	/* Disable all interrupt sources. */
+	pio_write_32(&gta02_irqc->intmsk, 0xffffffff);
+
+	/* Disable interrupts from all sub-sources. */
+	pio_write_32(&gta02_irqc->intsubmsk, 0xffffffff);
 }
 
 static void gta02_timer_irq_start(void)
 {
+	gta02_timer_irq_init();
+	gta02_timer_start();
 }
 
@@ -101,4 +130,22 @@
 static void gta02_irq_exception(unsigned int exc_no, istate_t *istate)
 {
+	uint32_t inum;
+
+	inum = pio_read_32(&gta02_irqc->intoffset);
+
+	irq_t *irq = irq_dispatch_and_lock(inum);
+	if (irq) {
+		/* The IRQ handler was found. */
+		irq->handler(irq);
+		spinlock_unlock(&irq->lock);
+	} else {
+		/* Spurious interrupt.*/
+		printf("cpu%d: spurious interrupt (inum=%d)\n",
+		    CPU->id, inum);
+	}
+
+	/* Clear interrupt condition in the interrupt controller. */
+	pio_write_32(&gta02_irqc->srcpnd, S3C24XX_INT_BIT(inum));
+	pio_write_32(&gta02_irqc->intpnd, S3C24XX_INT_BIT(inum));
 }
 
@@ -140,4 +187,66 @@
 }
 
+static void gta02_timer_irq_init(void)
+{
+	irq_initialize(&gta02_timer_irq);
+	gta02_timer_irq.devno = device_assign_devno();
+	gta02_timer_irq.inr = GTA02_TIMER_IRQ;
+	gta02_timer_irq.claim = gta02_timer_irq_claim;
+	gta02_timer_irq.handler = gta02_timer_irq_handler;
+
+	irq_register(&gta02_timer_irq);
+}
+
+static irq_ownership_t gta02_timer_irq_claim(irq_t *irq)
+{
+	return IRQ_ACCEPT;
+}
+
+static void gta02_timer_irq_handler(irq_t *irq)
+{
+	/*
+	 * We are holding a lock which prevents preemption.
+	 * Release the lock, call clock() and reacquire the lock again.
+	 */
+	spinlock_unlock(&irq->lock);
+	clock();
+	spinlock_lock(&irq->lock);
+}
+
+static void gta02_timer_start(void)
+{
+	s3c24xx_timer_t *timer = gta02_timer;
+
+	/*
+	 * See S3C2442B user manual chapter 10 (PWM Timer) for description
+	 * of timer operation. Starting a timer is described in the
+	 * section 'Timer initialization using manual update bit and
+	 * inverter bit'.
+	 */
+
+	/* Set prescaler values to zero. (no pre-divison), no dead zone. */
+	pio_write_32(&timer->tcfg0, 0);
+
+	/* No DMA request, divider value = 2 for all timers. */
+	pio_write_32(&timer->tcfg1, 0);
+
+	/* Stop all timers. */
+	pio_write_32(&timer->tcon, 0);
+
+	/* Start counting from 64k-1. Compare value is irrelevant. */
+	pio_write_32(&timer->timer[0].cntb, 0xffff);
+	pio_write_32(&timer->timer[0].cmpb, 0);
+
+	/* Enable interrupts from timer0 */
+	pio_write_32(&gta02_irqc->intmsk, pio_read_32(&gta02_irqc->intmsk) &
+	    ~S3C24XX_INT_BIT(S3C24XX_INT_TIMER0));
+
+	/* Load data from tcntb0/tcmpb0 into tcnt0/tcmp0. */
+	pio_write_32(&timer->tcon, TCON_T0_AUTO_RLD | TCON_T0_MUPDATE);
+
+	/* Start timer 0. Inverter is off. */
+	pio_write_32(&timer->tcon, TCON_T0_AUTO_RLD | TCON_T0_START);
+}
+
 /** @}
  */
