vserver 1.9.3
[linux-2.6.git] / arch / arm / mach-versatile / core.c
index 4c8646c..239d989 100644 (file)
 #include <linux/config.h>
 #include <linux/init.h>
 #include <linux/device.h>
+#include <linux/dma-mapping.h>
 #include <linux/sysdev.h>
+#include <linux/interrupt.h>
 
+#include <asm/system.h>
 #include <asm/hardware.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/leds.h>
 #include <asm/mach-types.h>
 #include <asm/hardware/amba.h>
+#include <asm/hardware/amba_clcd.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/flash.h>
 #include <asm/mach/irq.h>
+#include <asm/mach/time.h>
 #include <asm/mach/map.h>
 #ifdef CONFIG_MMC
 #include <asm/mach/mmc.h>
@@ -312,18 +317,223 @@ static unsigned int mmc_status(struct device *dev)
 }
 
 static struct mmc_platform_data mmc0_plat_data = {
-       .mclk           = 33000000,
        .ocr_mask       = MMC_VDD_32_33|MMC_VDD_33_34,
        .status         = mmc_status,
 };
 
 static struct mmc_platform_data mmc1_plat_data = {
-       .mclk           = 33000000,
        .ocr_mask       = MMC_VDD_32_33|MMC_VDD_33_34,
        .status         = mmc_status,
 };
 #endif
 
+/*
+ * CLCD support.
+ */
+#define SYS_CLCD_MODE_MASK     (3 << 0)
+#define SYS_CLCD_MODE_5551     (0 << 0)
+#define SYS_CLCD_MODE_565      (1 << 0)
+#define SYS_CLCD_MODE_888      (2 << 0)
+#define SYS_CLCD_MODE_LT       (3 << 0)
+#define SYS_CLCD_NLCDIOON      (1 << 2)
+#define SYS_CLCD_VDDPOSSWITCH  (1 << 3)
+#define SYS_CLCD_PWR3V5SWITCH  (1 << 4)
+#define SYS_CLCD_ID_MASK       (0x1f << 8)
+#define SYS_CLCD_ID_SANYO_3_8  (0x00 << 8)
+#define SYS_CLCD_ID_UNKNOWN_8_4        (0x01 << 8)
+#define SYS_CLCD_ID_EPSON_2_2  (0x02 << 8)
+#define SYS_CLCD_ID_VGA                (0x1f << 8)
+
+static struct clcd_panel vga = {
+       .mode           = {
+               .name           = "VGA",
+               .refresh        = 60,
+               .xres           = 640,
+               .yres           = 480,
+               .pixclock       = 39721,
+               .left_margin    = 40,
+               .right_margin   = 24,
+               .upper_margin   = 32,
+               .lower_margin   = 11,
+               .hsync_len      = 96,
+               .vsync_len      = 2,
+               .sync           = 0,
+               .vmode          = FB_VMODE_NONINTERLACED,
+       },
+       .width          = -1,
+       .height         = -1,
+       .tim2           = TIM2_BCD | TIM2_IPC,
+       .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
+       .bpp            = 16,
+};
+
+static struct clcd_panel sanyo_3_8_in = {
+       .mode           = {
+               .name           = "Sanyo QVGA",
+               .refresh        = 116,
+               .xres           = 320,
+               .yres           = 240,
+               .pixclock       = 100000,
+               .left_margin    = 6,
+               .right_margin   = 6,
+               .upper_margin   = 5,
+               .lower_margin   = 5,
+               .hsync_len      = 6,
+               .vsync_len      = 6,
+               .sync           = 0,
+               .vmode          = FB_VMODE_NONINTERLACED,
+       },
+       .width          = -1,
+       .height         = -1,
+       .tim2           = TIM2_BCD,
+       .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
+       .bpp            = 16,
+};
+
+static struct clcd_panel epson_2_2_in = {
+       .mode           = {
+               .name           = "Epson QCIF",
+               .refresh        = 390,
+               .xres           = 176,
+               .yres           = 220,
+               .pixclock       = 62500,
+               .left_margin    = 3,
+               .right_margin   = 2,
+               .upper_margin   = 1,
+               .lower_margin   = 0,
+               .hsync_len      = 3,
+               .vsync_len      = 2,
+               .sync           = 0,
+               .vmode          = FB_VMODE_NONINTERLACED,
+       },
+       .width          = -1,
+       .height         = -1,
+       .tim2           = TIM2_BCD | TIM2_IPC,
+       .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
+       .bpp            = 16,
+};
+
+/*
+ * Detect which LCD panel is connected, and return the appropriate
+ * clcd_panel structure.  Note: we do not have any information on
+ * the required timings for the 8.4in panel, so we presently assume
+ * VGA timings.
+ */
+static struct clcd_panel *versatile_clcd_panel(void)
+{
+       unsigned long sys_clcd = IO_ADDRESS(VERSATILE_SYS_BASE) + VERSATILE_SYS_CLCD_OFFSET;
+       struct clcd_panel *panel = &vga;
+       u32 val;
+
+       val = readl(sys_clcd) & SYS_CLCD_ID_MASK;
+       if (val == SYS_CLCD_ID_SANYO_3_8)
+               panel = &sanyo_3_8_in;
+       else if (val == SYS_CLCD_ID_EPSON_2_2)
+               panel = &epson_2_2_in;
+       else if (val == SYS_CLCD_ID_VGA)
+               panel = &vga;
+       else {
+               printk(KERN_ERR "CLCD: unknown LCD panel ID 0x%08x, using VGA\n",
+                       val);
+       }
+
+       return &vga;
+}
+
+/*
+ * Disable all display connectors on the interface module.
+ */
+static void versatile_clcd_disable(struct clcd_fb *fb)
+{
+       unsigned long sys_clcd = IO_ADDRESS(VERSATILE_SYS_BASE) + VERSATILE_SYS_CLCD_OFFSET;
+       u32 val;
+
+       val = readl(sys_clcd);
+       val &= ~SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH;
+       writel(val, sys_clcd);
+}
+
+/*
+ * Enable the relevant connector on the interface module.
+ */
+static void versatile_clcd_enable(struct clcd_fb *fb)
+{
+       unsigned long sys_clcd = IO_ADDRESS(VERSATILE_SYS_BASE) + VERSATILE_SYS_CLCD_OFFSET;
+       u32 val;
+
+       val = readl(sys_clcd);
+       val &= ~SYS_CLCD_MODE_MASK;
+
+       switch (fb->fb.var.green.length) {
+       case 5:
+#if 0
+               /*
+                * For some undocumented reason, we need to select 565 mode
+                * even when using 555 with VGA.  Maybe this is only true
+                * for the VGA output and needs to be done for LCD panels?
+                * I can't get an explaination from the people who should
+                * know.
+                */
+               val |= SYS_CLCD_MODE_5551;
+               break;
+#endif
+       case 6:
+               val |= SYS_CLCD_MODE_565;
+               break;
+       case 8:
+               val |= SYS_CLCD_MODE_888;
+               break;
+       }
+
+       /*
+        * Set the MUX
+        */
+       writel(val, sys_clcd);
+
+       /*
+        * And now enable the PSUs
+        */
+       val |= SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH;
+       writel(val, sys_clcd);
+}
+
+static unsigned long framesize = SZ_1M;
+
+static int versatile_clcd_setup(struct clcd_fb *fb)
+{
+       dma_addr_t dma;
+
+       fb->panel               = versatile_clcd_panel();
+
+       fb->fb.screen_base = dma_alloc_writecombine(&fb->dev->dev, framesize,
+                                                   &dma, GFP_KERNEL);
+       if (!fb->fb.screen_base) {
+               printk(KERN_ERR "CLCD: unable to map framebuffer\n");
+               return -ENOMEM;
+       }
+
+       fb->fb.fix.smem_start   = dma;
+       fb->fb.fix.smem_len     = framesize;
+
+       return 0;
+}
+
+static void versatile_clcd_remove(struct clcd_fb *fb)
+{
+       dma_free_writecombine(&fb->dev->dev, fb->fb.fix.smem_len,
+                             fb->fb.screen_base, fb->fb.fix.smem_start);
+}
+
+static struct clcd_board clcd_plat_data = {
+       .name           = "Versatile PB",
+       .check          = clcdfb_check,
+       .decode         = clcdfb_decode,
+       .disable        = versatile_clcd_disable,
+       .enable         = versatile_clcd_enable,
+       .setup          = versatile_clcd_setup,
+       .remove         = versatile_clcd_remove,
+};
+
 #define AMBA_DEVICE(name,busid,base,plat)                      \
 static struct amba_device name##_device = {                    \
        .dev            = {                                     \
@@ -416,7 +626,7 @@ AMBA_DEVICE(mmc1,  "fpga:0b", MMCI1,    &mmc1_plat_data);
 /* DevChip Primecells */
 AMBA_DEVICE(smc,   "dev:00",  SMC,      NULL);
 AMBA_DEVICE(mpmc,  "dev:10",  MPMC,     NULL);
-AMBA_DEVICE(clcd,  "dev:20",  CLCD,     NULL);
+AMBA_DEVICE(clcd,  "dev:20",  CLCD,     &clcd_plat_data);
 AMBA_DEVICE(dmac,  "dev:30",  DMAC,     NULL);
 AMBA_DEVICE(sctl,  "dev:e0",  SCTL,     NULL);
 AMBA_DEVICE(wdog,  "dev:e1",  WATCHDOG, NULL);
@@ -500,8 +710,8 @@ static void __init versatile_init(void)
 {
        int i;
 
-       platform_add_device(&versatile_flash_device);
-       platform_add_device(&smc91x_device);
+       platform_device_register(&versatile_flash_device);
+       platform_device_register(&smc91x_device);
 
        for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
                struct amba_device *d = amba_devs[i];
@@ -511,11 +721,153 @@ static void __init versatile_init(void)
        leds_event = versatile_leds_event;
 }
 
+/*
+ * Where is the timer (VA)?
+ */
+#define TIMER0_VA_BASE          IO_ADDRESS(VERSATILE_TIMER0_1_BASE)
+#define TIMER1_VA_BASE         (IO_ADDRESS(VERSATILE_TIMER0_1_BASE) + 0x20)
+#define TIMER2_VA_BASE          IO_ADDRESS(VERSATILE_TIMER2_3_BASE)
+#define TIMER3_VA_BASE         (IO_ADDRESS(VERSATILE_TIMER2_3_BASE) + 0x20)
+#define VA_IC_BASE              IO_ADDRESS(VERSATILE_VIC_BASE) 
+
+/*
+ * How long is the timer interval?
+ */
+#define TIMER_INTERVAL (TICKS_PER_uSEC * mSEC_10)
+#if TIMER_INTERVAL >= 0x100000
+#define TIMER_RELOAD   (TIMER_INTERVAL >> 8)           /* Divide by 256 */
+#define TIMER_CTRL     0x88                            /* Enable, Clock / 256 */
+#define TICKS2USECS(x) (256 * (x) / TICKS_PER_uSEC)
+#elif TIMER_INTERVAL >= 0x10000
+#define TIMER_RELOAD   (TIMER_INTERVAL >> 4)           /* Divide by 16 */
+#define TIMER_CTRL     0x84                            /* Enable, Clock / 16 */
+#define TICKS2USECS(x) (16 * (x) / TICKS_PER_uSEC)
+#else
+#define TIMER_RELOAD   (TIMER_INTERVAL)
+#define TIMER_CTRL     0x80                            /* Enable */
+#define TICKS2USECS(x) ((x) / TICKS_PER_uSEC)
+#endif
+
+#define TIMER_CTRL_IE  (1 << 5)        /* Interrupt Enable */
+
+/*
+ * What does it look like?
+ */
+typedef struct TimerStruct {
+       unsigned long TimerLoad;
+       unsigned long TimerValue;
+       unsigned long TimerControl;
+       unsigned long TimerClear;
+} TimerStruct_t;
+
+extern unsigned long (*gettimeoffset)(void);
+
+/*
+ * Returns number of ms since last clock interrupt.  Note that interrupts
+ * will have been disabled by do_gettimeoffset()
+ */
+static unsigned long versatile_gettimeoffset(void)
+{
+       volatile TimerStruct_t *timer0 = (TimerStruct_t *)TIMER0_VA_BASE;
+       unsigned long ticks1, ticks2, status;
+
+       /*
+        * Get the current number of ticks.  Note that there is a race
+        * condition between us reading the timer and checking for
+        * an interrupt.  We get around this by ensuring that the
+        * counter has not reloaded between our two reads.
+        */
+       ticks2 = timer0->TimerValue & 0xffff;
+       do {
+               ticks1 = ticks2;
+               status = __raw_readl(VA_IC_BASE + VIC_IRQ_RAW_STATUS);
+               ticks2 = timer0->TimerValue & 0xffff;
+       } while (ticks2 > ticks1);
+
+       /*
+        * Number of ticks since last interrupt.
+        */
+       ticks1 = TIMER_RELOAD - ticks2;
+
+       /*
+        * Interrupt pending?  If so, we've reloaded once already.
+        *
+        * FIXME: Need to check this is effectively timer 0 that expires
+        */
+       if (status & IRQMASK_TIMERINT0_1)
+               ticks1 += TIMER_RELOAD;
+
+       /*
+        * Convert the ticks to usecs
+        */
+       return TICKS2USECS(ticks1);
+}
+
+/*
+ * IRQ handler for the timer
+ */
+static irqreturn_t versatile_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       volatile TimerStruct_t *timer0 = (volatile TimerStruct_t *)TIMER0_VA_BASE;
+
+       // ...clear the interrupt
+       timer0->TimerClear = 1;
+
+       timer_tick(regs);
+
+       return IRQ_HANDLED;
+}
+
+static struct irqaction versatile_timer_irq = {
+       .name           = "Versatile Timer Tick",
+       .flags          = SA_INTERRUPT,
+       .handler        = versatile_timer_interrupt
+};
+
+/*
+ * Set up timer interrupt, and return the current time in seconds.
+ */
+void __init versatile_init_time(void)
+{
+       volatile TimerStruct_t *timer0 = (volatile TimerStruct_t *)TIMER0_VA_BASE;
+       volatile TimerStruct_t *timer1 = (volatile TimerStruct_t *)TIMER1_VA_BASE;
+       volatile TimerStruct_t *timer2 = (volatile TimerStruct_t *)TIMER2_VA_BASE;
+       volatile TimerStruct_t *timer3 = (volatile TimerStruct_t *)TIMER3_VA_BASE;
+
+       /* 
+        * set clock frequency: 
+        *      VERSATILE_REFCLK is 32KHz
+        *      VERSATILE_TIMCLK is 1MHz
+        */
+       *(volatile unsigned int *)IO_ADDRESS(VERSATILE_SCTL_BASE) |= 
+         ((VERSATILE_TIMCLK << VERSATILE_TIMER1_EnSel) | (VERSATILE_TIMCLK << VERSATILE_TIMER2_EnSel) | 
+          (VERSATILE_TIMCLK << VERSATILE_TIMER3_EnSel) | (VERSATILE_TIMCLK << VERSATILE_TIMER4_EnSel));
+
+       /*
+        * Initialise to a known state (all timers off)
+        */
+       timer0->TimerControl = 0;
+       timer1->TimerControl = 0;
+       timer2->TimerControl = 0;
+       timer3->TimerControl = 0;
+
+       timer0->TimerLoad    = TIMER_RELOAD;
+       timer0->TimerValue   = TIMER_RELOAD;
+       timer0->TimerControl = TIMER_CTRL | 0x40 | TIMER_CTRL_IE;  /* periodic + IE */
+
+       /* 
+        * Make irqs happen for the system timer
+        */
+       setup_irq(IRQ_TIMERINT0_1, &versatile_timer_irq);
+       gettimeoffset = versatile_gettimeoffset;
+}
+
 MACHINE_START(VERSATILE_PB, "ARM-Versatile PB")
        MAINTAINER("ARM Ltd/Deep Blue Solutions Ltd")
        BOOT_MEM(0x00000000, 0x101f1000, 0xf11f1000)
        BOOT_PARAMS(0x00000100)
        MAPIO(versatile_map_io)
        INITIRQ(versatile_init_irq)
+       INITTIME(versatile_init_time)
        INIT_MACHINE(versatile_init)
 MACHINE_END