Path: blob/master/arch/arm/mach-integrator/impd1.c
10817 views
/*1* linux/arch/arm/mach-integrator/impd1.c2*3* Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License version 2 as7* published by the Free Software Foundation.8*9* This file provides the core support for the IM-PD1 module.10*11* Module / boot parameters.12* lmid=n impd1.lmid=n - set the logic module position in stack to 'n'13*/14#include <linux/module.h>15#include <linux/moduleparam.h>16#include <linux/init.h>17#include <linux/device.h>18#include <linux/errno.h>19#include <linux/mm.h>20#include <linux/amba/bus.h>21#include <linux/amba/clcd.h>22#include <linux/io.h>23#include <linux/slab.h>24#include <linux/clkdev.h>2526#include <asm/hardware/icst.h>27#include <mach/lm.h>28#include <mach/impd1.h>29#include <asm/sizes.h>3031static int module_id;3233module_param_named(lmid, module_id, int, 0444);34MODULE_PARM_DESC(lmid, "logic module stack position");3536struct impd1_module {37void __iomem *base;38struct clk vcos[2];39struct clk_lookup *clks[3];40};4142static const struct icst_params impd1_vco_params = {43.ref = 24000000, /* 24 MHz */44.vco_max = ICST525_VCO_MAX_3V,45.vco_min = ICST525_VCO_MIN,46.vd_min = 12,47.vd_max = 519,48.rd_min = 3,49.rd_max = 120,50.s2div = icst525_s2div,51.idx2s = icst525_idx2s,52};5354static void impd1_setvco(struct clk *clk, struct icst_vco vco)55{56struct impd1_module *impd1 = clk->data;57u32 val = vco.v | (vco.r << 9) | (vco.s << 16);5859writel(0xa05f, impd1->base + IMPD1_LOCK);60writel(val, clk->vcoreg);61writel(0, impd1->base + IMPD1_LOCK);6263#ifdef DEBUG64vco.v = val & 0x1ff;65vco.r = (val >> 9) & 0x7f;66vco.s = (val >> 16) & 7;6768pr_debug("IM-PD1: VCO%d clock is %ld Hz\n",69vconr, icst525_hz(&impd1_vco_params, vco));70#endif71}7273static const struct clk_ops impd1_clk_ops = {74.round = icst_clk_round,75.set = icst_clk_set,76.setvco = impd1_setvco,77};7879void impd1_tweak_control(struct device *dev, u32 mask, u32 val)80{81struct impd1_module *impd1 = dev_get_drvdata(dev);82u32 cur;8384val &= mask;85cur = readl(impd1->base + IMPD1_CTRL) & ~mask;86writel(cur | val, impd1->base + IMPD1_CTRL);87}8889EXPORT_SYMBOL(impd1_tweak_control);9091/*92* CLCD support93*/94#define PANEL PROSPECTOR9596#define LTM10C209 197#define PROSPECTOR 298#define SVGA 399#define VGA 4100101#if PANEL == VGA102#define PANELTYPE vga103static struct clcd_panel vga = {104.mode = {105.name = "VGA",106.refresh = 60,107.xres = 640,108.yres = 480,109.pixclock = 39721,110.left_margin = 40,111.right_margin = 24,112.upper_margin = 32,113.lower_margin = 11,114.hsync_len = 96,115.vsync_len = 2,116.sync = 0,117.vmode = FB_VMODE_NONINTERLACED,118},119.width = -1,120.height = -1,121.tim2 = TIM2_BCD | TIM2_IPC,122.cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1),123.caps = CLCD_CAP_5551,124.connector = IMPD1_CTRL_DISP_VGA,125.bpp = 16,126.grayscale = 0,127};128129#elif PANEL == SVGA130#define PANELTYPE svga131static struct clcd_panel svga = {132.mode = {133.name = "SVGA",134.refresh = 0,135.xres = 800,136.yres = 600,137.pixclock = 27778,138.left_margin = 20,139.right_margin = 20,140.upper_margin = 5,141.lower_margin = 5,142.hsync_len = 164,143.vsync_len = 62,144.sync = 0,145.vmode = FB_VMODE_NONINTERLACED,146},147.width = -1,148.height = -1,149.tim2 = TIM2_BCD,150.cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1),151.connector = IMPD1_CTRL_DISP_VGA,152.caps = CLCD_CAP_5551,153.bpp = 16,154.grayscale = 0,155};156157#elif PANEL == PROSPECTOR158#define PANELTYPE prospector159static struct clcd_panel prospector = {160.mode = {161.name = "PROSPECTOR",162.refresh = 0,163.xres = 640,164.yres = 480,165.pixclock = 40000,166.left_margin = 33,167.right_margin = 64,168.upper_margin = 36,169.lower_margin = 7,170.hsync_len = 64,171.vsync_len = 25,172.sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,173.vmode = FB_VMODE_NONINTERLACED,174},175.width = -1,176.height = -1,177.tim2 = TIM2_BCD,178.cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1),179.caps = CLCD_CAP_5551,180.fixedtimings = 1,181.connector = IMPD1_CTRL_DISP_LCD,182.bpp = 16,183.grayscale = 0,184};185186#elif PANEL == LTM10C209187#define PANELTYPE ltm10c209188/*189* Untested.190*/191static struct clcd_panel ltm10c209 = {192.mode = {193.name = "LTM10C209",194.refresh = 0,195.xres = 640,196.yres = 480,197.pixclock = 40000,198.left_margin = 20,199.right_margin = 20,200.upper_margin = 19,201.lower_margin = 19,202.hsync_len = 20,203.vsync_len = 10,204.sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,205.vmode = FB_VMODE_NONINTERLACED,206},207.width = -1,208.height = -1,209.tim2 = TIM2_BCD,210.cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1),211.caps = CLCD_CAP_5551,212.fixedtimings = 1,213.connector = IMPD1_CTRL_DISP_LCD,214.bpp = 16,215.grayscale = 0,216};217#endif218219/*220* Disable all display connectors on the interface module.221*/222static void impd1fb_clcd_disable(struct clcd_fb *fb)223{224impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK, 0);225}226227/*228* Enable the relevant connector on the interface module.229*/230static void impd1fb_clcd_enable(struct clcd_fb *fb)231{232impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK,233fb->panel->connector | IMPD1_CTRL_DISP_ENABLE);234}235236static int impd1fb_clcd_setup(struct clcd_fb *fb)237{238unsigned long framebase = fb->dev->res.start + 0x01000000;239unsigned long framesize = SZ_1M;240int ret = 0;241242fb->panel = &PANELTYPE;243244if (!request_mem_region(framebase, framesize, "clcd framebuffer")) {245printk(KERN_ERR "IM-PD1: unable to reserve framebuffer\n");246return -EBUSY;247}248249fb->fb.screen_base = ioremap(framebase, framesize);250if (!fb->fb.screen_base) {251printk(KERN_ERR "IM-PD1: unable to map framebuffer\n");252ret = -ENOMEM;253goto free_buffer;254}255256fb->fb.fix.smem_start = framebase;257fb->fb.fix.smem_len = framesize;258259return 0;260261free_buffer:262release_mem_region(framebase, framesize);263return ret;264}265266static int impd1fb_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)267{268unsigned long start, size;269270start = vma->vm_pgoff + (fb->fb.fix.smem_start >> PAGE_SHIFT);271size = vma->vm_end - vma->vm_start;272273return remap_pfn_range(vma, vma->vm_start, start, size,274vma->vm_page_prot);275}276277static void impd1fb_clcd_remove(struct clcd_fb *fb)278{279iounmap(fb->fb.screen_base);280release_mem_region(fb->fb.fix.smem_start, fb->fb.fix.smem_len);281}282283static struct clcd_board impd1_clcd_data = {284.name = "IM-PD/1",285.caps = CLCD_CAP_5551 | CLCD_CAP_888,286.check = clcdfb_check,287.decode = clcdfb_decode,288.disable = impd1fb_clcd_disable,289.enable = impd1fb_clcd_enable,290.setup = impd1fb_clcd_setup,291.mmap = impd1fb_clcd_mmap,292.remove = impd1fb_clcd_remove,293};294295struct impd1_device {296unsigned long offset;297unsigned int irq[2];298unsigned int id;299void *platform_data;300};301302static struct impd1_device impd1_devs[] = {303{304.offset = 0x03000000,305.id = 0x00041190,306}, {307.offset = 0x00100000,308.irq = { 1 },309.id = 0x00141011,310}, {311.offset = 0x00200000,312.irq = { 2 },313.id = 0x00141011,314}, {315.offset = 0x00300000,316.irq = { 3 },317.id = 0x00041022,318}, {319.offset = 0x00400000,320.irq = { 4 },321.id = 0x00041061,322}, {323.offset = 0x00500000,324.irq = { 5 },325.id = 0x00041061,326}, {327.offset = 0x00600000,328.irq = { 6 },329.id = 0x00041130,330}, {331.offset = 0x00700000,332.irq = { 7, 8 },333.id = 0x00041181,334}, {335.offset = 0x00800000,336.irq = { 9 },337.id = 0x00041041,338}, {339.offset = 0x01000000,340.irq = { 11 },341.id = 0x00041110,342.platform_data = &impd1_clcd_data,343}344};345346static struct clk fixed_14745600 = {347.rate = 14745600,348};349350static int impd1_probe(struct lm_device *dev)351{352struct impd1_module *impd1;353int i, ret;354355if (dev->id != module_id)356return -EINVAL;357358if (!request_mem_region(dev->resource.start, SZ_4K, "LM registers"))359return -EBUSY;360361impd1 = kzalloc(sizeof(struct impd1_module), GFP_KERNEL);362if (!impd1) {363ret = -ENOMEM;364goto release_lm;365}366367impd1->base = ioremap(dev->resource.start, SZ_4K);368if (!impd1->base) {369ret = -ENOMEM;370goto free_impd1;371}372373lm_set_drvdata(dev, impd1);374375printk("IM-PD1 found at 0x%08lx\n",376(unsigned long)dev->resource.start);377378for (i = 0; i < ARRAY_SIZE(impd1->vcos); i++) {379impd1->vcos[i].ops = &impd1_clk_ops,380impd1->vcos[i].owner = THIS_MODULE,381impd1->vcos[i].params = &impd1_vco_params,382impd1->vcos[i].data = impd1;383}384impd1->vcos[0].vcoreg = impd1->base + IMPD1_OSC1;385impd1->vcos[1].vcoreg = impd1->base + IMPD1_OSC2;386387impd1->clks[0] = clkdev_alloc(&impd1->vcos[0], NULL, "lm%x:01000",388dev->id);389impd1->clks[1] = clkdev_alloc(&fixed_14745600, NULL, "lm%x:00100",390dev->id);391impd1->clks[2] = clkdev_alloc(&fixed_14745600, NULL, "lm%x:00200",392dev->id);393for (i = 0; i < ARRAY_SIZE(impd1->clks); i++)394clkdev_add(impd1->clks[i]);395396for (i = 0; i < ARRAY_SIZE(impd1_devs); i++) {397struct impd1_device *idev = impd1_devs + i;398struct amba_device *d;399unsigned long pc_base;400401pc_base = dev->resource.start + idev->offset;402403d = kzalloc(sizeof(struct amba_device), GFP_KERNEL);404if (!d)405continue;406407dev_set_name(&d->dev, "lm%x:%5.5lx", dev->id, idev->offset >> 12);408d->dev.parent = &dev->dev;409d->res.start = dev->resource.start + idev->offset;410d->res.end = d->res.start + SZ_4K - 1;411d->res.flags = IORESOURCE_MEM;412d->irq[0] = dev->irq;413d->irq[1] = dev->irq;414d->periphid = idev->id;415d->dev.platform_data = idev->platform_data;416417ret = amba_device_register(d, &dev->resource);418if (ret) {419dev_err(&d->dev, "unable to register device: %d\n", ret);420kfree(d);421}422}423424return 0;425426free_impd1:427if (impd1 && impd1->base)428iounmap(impd1->base);429kfree(impd1);430release_lm:431release_mem_region(dev->resource.start, SZ_4K);432return ret;433}434435static int impd1_remove_one(struct device *dev, void *data)436{437device_unregister(dev);438return 0;439}440441static void impd1_remove(struct lm_device *dev)442{443struct impd1_module *impd1 = lm_get_drvdata(dev);444int i;445446device_for_each_child(&dev->dev, NULL, impd1_remove_one);447448for (i = 0; i < ARRAY_SIZE(impd1->clks); i++)449clkdev_drop(impd1->clks[i]);450451lm_set_drvdata(dev, NULL);452453iounmap(impd1->base);454kfree(impd1);455release_mem_region(dev->resource.start, SZ_4K);456}457458static struct lm_driver impd1_driver = {459.drv = {460.name = "impd1",461},462.probe = impd1_probe,463.remove = impd1_remove,464};465466static int __init impd1_init(void)467{468return lm_driver_register(&impd1_driver);469}470471static void __exit impd1_exit(void)472{473lm_driver_unregister(&impd1_driver);474}475476module_init(impd1_init);477module_exit(impd1_exit);478479MODULE_LICENSE("GPL");480MODULE_DESCRIPTION("Integrator/IM-PD1 logic module core driver");481MODULE_AUTHOR("Deep Blue Solutions Ltd");482483484