diff options
Diffstat (limited to 'arch/arm/mach-integrator/impd1.c')
-rw-r--r-- | arch/arm/mach-integrator/impd1.c | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/arch/arm/mach-integrator/impd1.c b/arch/arm/mach-integrator/impd1.c new file mode 100644 index 000000000000..c3c2f17d030e --- /dev/null +++ b/arch/arm/mach-integrator/impd1.c @@ -0,0 +1,475 @@ +/* + * linux/arch/arm/mach-integrator/impd1.c + * + * Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This file provides the core support for the IM-PD1 module. + * + * Module / boot parameters. + * lmid=n impd1.lmid=n - set the logic module position in stack to 'n' + */ +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/mm.h> + +#include <asm/io.h> +#include <asm/hardware/icst525.h> +#include <asm/hardware/amba.h> +#include <asm/hardware/amba_clcd.h> +#include <asm/arch/lm.h> +#include <asm/arch/impd1.h> +#include <asm/sizes.h> + +#include "clock.h" + +static int module_id; + +module_param_named(lmid, module_id, int, 0444); +MODULE_PARM_DESC(lmid, "logic module stack position"); + +struct impd1_module { + void __iomem *base; + struct clk vcos[2]; +}; + +static const struct icst525_params impd1_vco_params = { + .ref = 24000, /* 24 MHz */ + .vco_max = 200000, /* 200 MHz */ + .vd_min = 12, + .vd_max = 519, + .rd_min = 3, + .rd_max = 120, +}; + +static void impd1_setvco(struct clk *clk, struct icst525_vco vco) +{ + struct impd1_module *impd1 = clk->data; + int vconr = clk - impd1->vcos; + u32 val; + + val = vco.v | (vco.r << 9) | (vco.s << 16); + + writel(0xa05f, impd1->base + IMPD1_LOCK); + switch (vconr) { + case 0: + writel(val, impd1->base + IMPD1_OSC1); + break; + case 1: + writel(val, impd1->base + IMPD1_OSC2); + break; + } + writel(0, impd1->base + IMPD1_LOCK); + +#if DEBUG + vco.v = val & 0x1ff; + vco.r = (val >> 9) & 0x7f; + vco.s = (val >> 16) & 7; + + pr_debug("IM-PD1: VCO%d clock is %ld kHz\n", + vconr, icst525_khz(&impd1_vco_params, vco)); +#endif +} + +void impd1_tweak_control(struct device *dev, u32 mask, u32 val) +{ + struct impd1_module *impd1 = dev_get_drvdata(dev); + u32 cur; + + val &= mask; + cur = readl(impd1->base + IMPD1_CTRL) & ~mask; + writel(cur | val, impd1->base + IMPD1_CTRL); +} + +EXPORT_SYMBOL(impd1_tweak_control); + +/* + * CLCD support + */ +#define PANEL PROSPECTOR + +#define LTM10C209 1 +#define PROSPECTOR 2 +#define SVGA 3 +#define VGA 4 + +#if PANEL == VGA +#define PANELTYPE vga +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), + .connector = IMPD1_CTRL_DISP_VGA, + .bpp = 16, + .grayscale = 0, +}; + +#elif PANEL == SVGA +#define PANELTYPE svga +static struct clcd_panel svga = { + .mode = { + .name = "SVGA", + .refresh = 0, + .xres = 800, + .yres = 600, + .pixclock = 27778, + .left_margin = 20, + .right_margin = 20, + .upper_margin = 5, + .lower_margin = 5, + .hsync_len = 164, + .vsync_len = 62, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, + .width = -1, + .height = -1, + .tim2 = TIM2_BCD, + .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1), + .connector = IMPD1_CTRL_DISP_VGA, + .bpp = 16, + .grayscale = 0, +}; + +#elif PANEL == PROSPECTOR +#define PANELTYPE prospector +static struct clcd_panel prospector = { + .mode = { + .name = "PROSPECTOR", + .refresh = 0, + .xres = 640, + .yres = 480, + .pixclock = 40000, + .left_margin = 33, + .right_margin = 64, + .upper_margin = 36, + .lower_margin = 7, + .hsync_len = 64, + .vsync_len = 25, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED, + }, + .width = -1, + .height = -1, + .tim2 = TIM2_BCD, + .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1), + .fixedtimings = 1, + .connector = IMPD1_CTRL_DISP_LCD, + .bpp = 16, + .grayscale = 0, +}; + +#elif PANEL == LTM10C209 +#define PANELTYPE ltm10c209 +/* + * Untested. + */ +static struct clcd_panel ltm10c209 = { + .mode = { + .name = "LTM10C209", + .refresh = 0, + .xres = 640, + .yres = 480, + .pixclock = 40000, + .left_margin = 20, + .right_margin = 20, + .upper_margin = 19, + .lower_margin = 19, + .hsync_len = 20, + .vsync_len = 10, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED, + }, + .width = -1, + .height = -1, + .tim2 = TIM2_BCD, + .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1), + .fixedtimings = 1, + .connector = IMPD1_CTRL_DISP_LCD, + .bpp = 16, + .grayscale = 0, +}; +#endif + +/* + * Disable all display connectors on the interface module. + */ +static void impd1fb_clcd_disable(struct clcd_fb *fb) +{ + impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK, 0); +} + +/* + * Enable the relevant connector on the interface module. + */ +static void impd1fb_clcd_enable(struct clcd_fb *fb) +{ + impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK, + fb->panel->connector | IMPD1_CTRL_DISP_ENABLE); +} + +static int impd1fb_clcd_setup(struct clcd_fb *fb) +{ + unsigned long framebase = fb->dev->res.start + 0x01000000; + unsigned long framesize = SZ_1M; + int ret = 0; + + fb->panel = &PANELTYPE; + + if (!request_mem_region(framebase, framesize, "clcd framebuffer")) { + printk(KERN_ERR "IM-PD1: unable to reserve framebuffer\n"); + return -EBUSY; + } + + fb->fb.screen_base = ioremap(framebase, framesize); + if (!fb->fb.screen_base) { + printk(KERN_ERR "IM-PD1: unable to map framebuffer\n"); + ret = -ENOMEM; + goto free_buffer; + } + + fb->fb.fix.smem_start = framebase; + fb->fb.fix.smem_len = framesize; + + return 0; + + free_buffer: + release_mem_region(framebase, framesize); + return ret; +} + +static int impd1fb_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma) +{ + unsigned long start, size; + + start = vma->vm_pgoff + (fb->fb.fix.smem_start >> PAGE_SHIFT); + size = vma->vm_end - vma->vm_start; + + return remap_pfn_range(vma, vma->vm_start, start, size, + vma->vm_page_prot); +} + +static void impd1fb_clcd_remove(struct clcd_fb *fb) +{ + iounmap(fb->fb.screen_base); + release_mem_region(fb->fb.fix.smem_start, fb->fb.fix.smem_len); +} + +static struct clcd_board impd1_clcd_data = { + .name = "IM-PD/1", + .check = clcdfb_check, + .decode = clcdfb_decode, + .disable = impd1fb_clcd_disable, + .enable = impd1fb_clcd_enable, + .setup = impd1fb_clcd_setup, + .mmap = impd1fb_clcd_mmap, + .remove = impd1fb_clcd_remove, +}; + +struct impd1_device { + unsigned long offset; + unsigned int irq[2]; + unsigned int id; + void *platform_data; +}; + +static struct impd1_device impd1_devs[] = { + { + .offset = 0x03000000, + .id = 0x00041190, + }, { + .offset = 0x00100000, + .irq = { 1 }, + .id = 0x00141011, + }, { + .offset = 0x00200000, + .irq = { 2 }, + .id = 0x00141011, + }, { + .offset = 0x00300000, + .irq = { 3 }, + .id = 0x00041022, + }, { + .offset = 0x00400000, + .irq = { 4 }, + .id = 0x00041061, + }, { + .offset = 0x00500000, + .irq = { 5 }, + .id = 0x00041061, + }, { + .offset = 0x00600000, + .irq = { 6 }, + .id = 0x00041130, + }, { + .offset = 0x00700000, + .irq = { 7, 8 }, + .id = 0x00041181, + }, { + .offset = 0x00800000, + .irq = { 9 }, + .id = 0x00041041, + }, { + .offset = 0x01000000, + .irq = { 11 }, + .id = 0x00041110, + .platform_data = &impd1_clcd_data, + } +}; + +static const char *impd1_vconames[2] = { + "CLCDCLK", + "AUXVCO2", +}; + +static int impd1_probe(struct lm_device *dev) +{ + struct impd1_module *impd1; + int i, ret; + + if (dev->id != module_id) + return -EINVAL; + + if (!request_mem_region(dev->resource.start, SZ_4K, "LM registers")) + return -EBUSY; + + impd1 = kmalloc(sizeof(struct impd1_module), GFP_KERNEL); + if (!impd1) { + ret = -ENOMEM; + goto release_lm; + } + memset(impd1, 0, sizeof(struct impd1_module)); + + impd1->base = ioremap(dev->resource.start, SZ_4K); + if (!impd1->base) { + ret = -ENOMEM; + goto free_impd1; + } + + lm_set_drvdata(dev, impd1); + + printk("IM-PD1 found at 0x%08lx\n", dev->resource.start); + + for (i = 0; i < ARRAY_SIZE(impd1->vcos); i++) { + impd1->vcos[i].owner = THIS_MODULE, + impd1->vcos[i].name = impd1_vconames[i], + impd1->vcos[i].params = &impd1_vco_params, + impd1->vcos[i].data = impd1, + impd1->vcos[i].setvco = impd1_setvco; + + clk_register(&impd1->vcos[i]); + } + + for (i = 0; i < ARRAY_SIZE(impd1_devs); i++) { + struct impd1_device *idev = impd1_devs + i; + struct amba_device *d; + unsigned long pc_base; + + pc_base = dev->resource.start + idev->offset; + + d = kmalloc(sizeof(struct amba_device), GFP_KERNEL); + if (!d) + continue; + + memset(d, 0, sizeof(struct amba_device)); + + snprintf(d->dev.bus_id, sizeof(d->dev.bus_id), + "lm%x:%5.5lx", dev->id, idev->offset >> 12); + + d->dev.parent = &dev->dev; + d->res.start = dev->resource.start + idev->offset; + d->res.end = d->res.start + SZ_4K - 1; + d->res.flags = IORESOURCE_MEM; + d->irq[0] = dev->irq; + d->irq[1] = dev->irq; + d->periphid = idev->id; + d->dev.platform_data = idev->platform_data; + + ret = amba_device_register(d, &dev->resource); + if (ret) { + printk("unable to register device %s: %d\n", + d->dev.bus_id, ret); + kfree(d); + } + } + + return 0; + + free_impd1: + if (impd1 && impd1->base) + iounmap(impd1->base); + if (impd1) + kfree(impd1); + release_lm: + release_mem_region(dev->resource.start, SZ_4K); + return ret; +} + +static void impd1_remove(struct lm_device *dev) +{ + struct impd1_module *impd1 = lm_get_drvdata(dev); + struct list_head *l, *n; + int i; + + list_for_each_safe(l, n, &dev->dev.children) { + struct device *d = list_to_dev(l); + + device_unregister(d); + } + + for (i = 0; i < ARRAY_SIZE(impd1->vcos); i++) + clk_unregister(&impd1->vcos[i]); + + lm_set_drvdata(dev, NULL); + + iounmap(impd1->base); + kfree(impd1); + release_mem_region(dev->resource.start, SZ_4K); +} + +static struct lm_driver impd1_driver = { + .drv = { + .name = "impd1", + }, + .probe = impd1_probe, + .remove = impd1_remove, +}; + +static int __init impd1_init(void) +{ + return lm_driver_register(&impd1_driver); +} + +static void __exit impd1_exit(void) +{ + lm_driver_unregister(&impd1_driver); +} + +module_init(impd1_init); +module_exit(impd1_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Integrator/IM-PD1 logic module core driver"); +MODULE_AUTHOR("Deep Blue Solutions Ltd"); |