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);
+}
+
 /** @}
  */
Index: kernel/genarch/include/drivers/s3c24xx_irqc/s3c24xx_irqc.h
===================================================================
--- kernel/genarch/include/drivers/s3c24xx_irqc/s3c24xx_irqc.h	(revision 41ce4d90d8c47b133bd70c5513dcbf39004b6833)
+++ kernel/genarch/include/drivers/s3c24xx_irqc/s3c24xx_irqc.h	(revision 41ce4d90d8c47b133bd70c5513dcbf39004b6833)
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2010 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup genarch
+ * @{
+ */
+/**
+ * @file
+ * @brief Samsung S3C24xx on-chip interrupt controller driver.
+ */
+
+#ifndef KERN_S3C24XX_IRQC_H_
+#define KERN_S3C24XX_IRQC_H_
+
+#include <typedefs.h>
+
+/** Physical address where S3C24XX Interrupt controller is mapped */
+#define S3C24XX_IRQC_ADDRESS	0x4a000000
+
+/** S3C24xx on-chip interrupt controller registers */
+typedef struct {
+	ioport32_t srcpnd;	/**< Source pending */
+	ioport32_t intmod;	/**< Interrupt mode */
+	ioport32_t intmsk;	/**< Interrupt mask */
+	ioport32_t priority;	/**< Priority */
+	ioport32_t intpnd;	/**< Interrupt pending */
+	ioport32_t intoffset;	/**< Interrupt offset */
+	ioport32_t subsrcpnd;	/**< Sub source pending */
+	ioport32_t intsubmsk;	/** Interrupt sub mask */
+} s3c24xx_irqc_t;
+
+/** S3C24xx Interrupt source numbers.
+ *
+ * These correspond to bit numbers in srcpnd, intmod, intmsk and intpnd
+ * registers as well as to the values read from the intoffset register.
+ */
+enum s3c24xx_int_source {
+	S3C24XX_INT_ADC		= 31,
+	S3C24XX_INT_RTC		= 30,
+	S3C24XX_INT_SPI1	= 29,
+	S3C24XX_INT_UART0	= 28,
+	S3C24XX_INT_IIC		= 27,
+	S3C24XX_INT_USBH	= 26,
+	S3C24XX_INT_USBD	= 25,
+	S3C24XX_INT_NFCON	= 24,
+	S3C24XX_INT_UART1	= 23,
+	S3C24XX_INT_SPI0	= 22,
+	S3C24XX_INT_SDI		= 21,
+	S3C24XX_INT_DMA3	= 20,
+	S3C24XX_INT_DMA2	= 19,
+	S3C24XX_INT_DMA1	= 18,
+	S3C24XX_INT_DMA0	= 17,
+	S3C24XX_INT_LCD		= 16,
+	S3C24XX_INT_UART2	= 15,
+	S3C24XX_INT_TIMER4	= 14,
+	S3C24XX_INT_TIMER3	= 13,
+	S3C24XX_INT_TIMER2	= 12,
+	S3C24XX_INT_TIMER1	= 11,
+	S3C24XX_INT_TIMER0	= 10,
+	S3C24XX_INT_WDT_AC97	= 9,
+	S3C24XX_INT_TICK	= 8,
+	S3C24XX_nBATT_FLT	= 7,
+	S3C24XX_INT_CAM		= 6,
+	S3C24XX_EINT8_23	= 5,
+	S3C24XX_EINT4_7		= 4,
+	S3C24XX_EINT3		= 3,
+	S3C24XX_EINT2		= 2,
+	S3C24XX_EINT1		= 1,
+	S3C24XX_EINT0		= 0
+};
+
+/** S3C24xx Interrupt sub-source numbers.
+ *
+ * These correspond to bit numbers in the intsubmsk register.
+ */
+enum s3c24xx_int_subsource {
+	S3C24XX_SUBINT_AC97	= 14,
+	S3C24XX_SUBINT_WDT	= 13,
+	S3C24XX_SUBINT_CAM_P	= 12,
+	S3C24XX_SUBINT_CAM_C	= 11,
+	S3C24XX_SUBINT_ADC_S	= 10,
+	S3C24XX_SUBINT_TC	= 9,
+	S3C24XX_SUBINT_ERR2	= 8,
+	S3C24XX_SUBINT_TXD2	= 7,
+	S3C24XX_SUBINT_RXD2	= 6,
+	S3C24XX_SUBINT_ERR1	= 5,
+	S3C24XX_SUBINT_TXD1	= 4,
+	S3C24XX_SUBINT_RXD1	= 3,
+	S3C24XX_SUBINT_ERR0	= 2,
+	S3C24XX_SUBINT_TXD0	= 1,
+	S3C24XX_SUBINT_RXD0	= 0
+};
+
+#define S3C24XX_INT_BIT(source) (1 << (source))
+#define S3C24XX_SUBINT_BIT(subsource) (1 << (subsource))
+
+#endif
+
+/** @}
+ */
Index: kernel/genarch/include/drivers/s3c24xx_timer/s3c24xx_timer.h
===================================================================
--- kernel/genarch/include/drivers/s3c24xx_timer/s3c24xx_timer.h	(revision 41ce4d90d8c47b133bd70c5513dcbf39004b6833)
+++ kernel/genarch/include/drivers/s3c24xx_timer/s3c24xx_timer.h	(revision 41ce4d90d8c47b133bd70c5513dcbf39004b6833)
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2010 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup genarch
+ * @{
+ */
+/**
+ * @file
+ * @brief Samsung S3C24xx on-chip PWM timer driver.
+ */
+
+#ifndef KERN_S3C24XX_TIMER_H_
+#define KERN_S3C24XX_TIMER_H_
+
+#include <typedefs.h>
+
+/** Physical address where S3C24XX on-chip PWM timer is mapped */
+#define S3C24XX_TIMER_ADDRESS	0x51000000
+
+/** S3C24xx on-chip PWM timer registers */
+typedef struct {
+	ioport32_t tcfg0;	/**< Timer configuration register 0 */
+	ioport32_t tcfg1;	/**< Timer configuration register 1 */
+	ioport32_t tcon;	/**< Timer control register */
+
+	struct {
+		ioport32_t cntb;	/**< Count buffer register */
+		ioport32_t cmpb;	/**< Compare buffer register */
+		ioport32_t cnto;	/**< Count observation register */
+	} timer[5];
+} s3c24xx_timer_t;
+
+/** Bits in the S3C24xx PWM timer TCON register. */
+enum s3c24xx_tcon_bits {
+	TCON_T0_START		= (1 << 0),	/**< Timer 0 start */
+	TCON_T0_MUPDATE		= (1 << 1),	/**< Timer 0 manual update */
+	TCON_T0_INVERT		= (1 << 2),	/**< Timer 0 inverter on */
+	TCON_T0_AUTO_RLD	= (1 << 3),	/**< Timer 0 auto reload */
+
+	TCON_DEAD_ZONE		= (1 << 4),	/**< Dead zone enable */
+
+	TCON_T1_START		= (1 << 8),	/**< Timer 1 start */
+	TCON_T1_MUPDATE		= (1 << 9),	/**< Timer 1 manual update */
+	TCON_T1_INVERT		= (1 << 10),	/**< Timer 1 inverter on */
+	TCON_T1_AUTO_RLD	= (1 << 11),	/**< Timer 1 auto reload */
+
+	TCON_T2_START		= (1 << 12),	/**< Timer 2 start */
+	TCON_T2_MUPDATE		= (1 << 13),	/**< Timer 2 manual update */
+	TCON_T2_INVERT		= (1 << 14),	/**< Timer 2 inverter on */
+	TCON_T2_AUTO_RLD	= (1 << 15),	/**< Timer 2 auto reload */
+
+	TCON_T3_START		= (1 << 16),	/**< Timer 3 start */
+	TCON_T3_MUPDATE		= (1 << 17),	/**< Timer 3 manual update */
+	TCON_T3_INVERT		= (1 << 18),	/**< Timer 3 inverter on */
+	TCON_T3_AUTO_RLD	= (1 << 19),	/**< Timer 3 auto reload */
+
+	TCON_T4_START		= (1 << 20),	/**< Timer 4 start */
+	TCON_T4_MUPDATE		= (1 << 21),	/**< Timer 4 manual update */
+	TCON_T4_AUTO_RLD	= (1 << 22)	/**< Timer 4 auto reload */
+};
+
+#endif
+
+/** @}
+ */
