a62c80e559
Since the ARM AMBA bus is used on MIPS as well as ARM, we need to make the bus available for other architectures to use. Move the AMBA include files from include/asm-arm/hardware/ to include/linux/amba/ Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
475 lines
9.9 KiB
C
475 lines
9.9 KiB
C
/*
|
|
* 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 <linux/amba/bus.h>
|
|
#include <linux/amba/clcd.h>
|
|
|
|
#include <asm/io.h>
|
|
#include <asm/hardware/icst525.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);
|
|
|
|
#ifdef 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);
|
|
kfree(impd1);
|
|
release_lm:
|
|
release_mem_region(dev->resource.start, SZ_4K);
|
|
return ret;
|
|
}
|
|
|
|
static int impd1_remove_one(struct device *dev, void *data)
|
|
{
|
|
device_unregister(dev);
|
|
return 0;
|
|
}
|
|
|
|
static void impd1_remove(struct lm_device *dev)
|
|
{
|
|
struct impd1_module *impd1 = lm_get_drvdata(dev);
|
|
int i;
|
|
|
|
device_for_each_child(&dev->dev, NULL, impd1_remove_one);
|
|
|
|
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");
|