#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>
}
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 = { \
/* 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);
{
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];
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