linux/arch/arm/mach-integrator/impd1.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/mach-integrator/impd1.c
   3 *
   4 *  Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 *  This file provides the core support for the IM-PD1 module.
  11 *
  12 * Module / boot parameters.
  13 *   lmid=n   impd1.lmid=n - set the logic module position in stack to 'n'
  14 */
  15#include <linux/module.h>
  16#include <linux/moduleparam.h>
  17#include <linux/init.h>
  18#include <linux/device.h>
  19#include <linux/errno.h>
  20#include <linux/mm.h>
  21#include <linux/amba/bus.h>
  22#include <linux/amba/clcd.h>
  23#include <linux/io.h>
  24
  25#include <asm/clkdev.h>
  26#include <mach/clkdev.h>
  27#include <asm/hardware/icst525.h>
  28#include <mach/lm.h>
  29#include <mach/impd1.h>
  30#include <asm/sizes.h>
  31
  32static int module_id;
  33
  34module_param_named(lmid, module_id, int, 0444);
  35MODULE_PARM_DESC(lmid, "logic module stack position");
  36
  37struct impd1_module {
  38        void __iomem    *base;
  39        struct clk      vcos[2];
  40        struct clk_lookup *clks[3];
  41};
  42
  43static const struct icst525_params impd1_vco_params = {
  44        .ref            = 24000,        /* 24 MHz */
  45        .vco_max        = 200000,       /* 200 MHz */
  46        .vd_min         = 12,
  47        .vd_max         = 519,
  48        .rd_min         = 3,
  49        .rd_max         = 120,
  50};
  51
  52static void impd1_setvco(struct clk *clk, struct icst525_vco vco)
  53{
  54        struct impd1_module *impd1 = clk->data;
  55        int vconr = clk - impd1->vcos;
  56        u32 val;
  57
  58        val = vco.v | (vco.r << 9) | (vco.s << 16);
  59
  60        writel(0xa05f, impd1->base + IMPD1_LOCK);
  61        switch (vconr) {
  62        case 0:
  63                writel(val, impd1->base + IMPD1_OSC1);
  64                break;
  65        case 1:
  66                writel(val, impd1->base + IMPD1_OSC2);
  67                break;
  68        }
  69        writel(0, impd1->base + IMPD1_LOCK);
  70
  71#ifdef DEBUG
  72        vco.v = val & 0x1ff;
  73        vco.r = (val >> 9) & 0x7f;
  74        vco.s = (val >> 16) & 7;
  75
  76        pr_debug("IM-PD1: VCO%d clock is %ld kHz\n",
  77                 vconr, icst525_khz(&impd1_vco_params, vco));
  78#endif
  79}
  80
  81void impd1_tweak_control(struct device *dev, u32 mask, u32 val)
  82{
  83        struct impd1_module *impd1 = dev_get_drvdata(dev);
  84        u32 cur;
  85
  86        val &= mask;
  87        cur = readl(impd1->base + IMPD1_CTRL) & ~mask;
  88        writel(cur | val, impd1->base + IMPD1_CTRL);
  89}
  90
  91EXPORT_SYMBOL(impd1_tweak_control);
  92
  93/*
  94 * CLCD support
  95 */
  96#define PANEL           PROSPECTOR
  97
  98#define LTM10C209               1
  99#define PROSPECTOR              2
 100#define SVGA                    3
 101#define VGA                     4
 102
 103#if PANEL == VGA
 104#define PANELTYPE       vga
 105static struct clcd_panel vga = {
 106        .mode           = {
 107                .name           = "VGA",
 108                .refresh        = 60,
 109                .xres           = 640,
 110                .yres           = 480,
 111                .pixclock       = 39721,
 112                .left_margin    = 40,
 113                .right_margin   = 24,
 114                .upper_margin   = 32,
 115                .lower_margin   = 11,
 116                .hsync_len      = 96,
 117                .vsync_len      = 2,
 118                .sync           = 0,
 119                .vmode          = FB_VMODE_NONINTERLACED,
 120        },
 121        .width          = -1,
 122        .height         = -1,
 123        .tim2           = TIM2_BCD | TIM2_IPC,
 124        .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
 125        .connector      = IMPD1_CTRL_DISP_VGA,
 126        .bpp            = 16,
 127        .grayscale      = 0,
 128};
 129
 130#elif PANEL == SVGA
 131#define PANELTYPE       svga
 132static struct clcd_panel svga = {
 133        .mode           = {
 134                .name           = "SVGA",
 135                .refresh        = 0,
 136                .xres           = 800,
 137                .yres           = 600,
 138                .pixclock       = 27778,
 139                .left_margin    = 20,
 140                .right_margin   = 20,
 141                .upper_margin   = 5,
 142                .lower_margin   = 5,
 143                .hsync_len      = 164,
 144                .vsync_len      = 62,
 145                .sync           = 0,
 146                .vmode          = FB_VMODE_NONINTERLACED,
 147        },
 148        .width          = -1,
 149        .height         = -1,
 150        .tim2           = TIM2_BCD,
 151        .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
 152        .connector      = IMPD1_CTRL_DISP_VGA,
 153        .bpp            = 16,
 154        .grayscale      = 0,
 155};
 156
 157#elif PANEL == PROSPECTOR
 158#define PANELTYPE       prospector
 159static 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        .fixedtimings   = 1,
 180        .connector      = IMPD1_CTRL_DISP_LCD,
 181        .bpp            = 16,
 182        .grayscale      = 0,
 183};
 184
 185#elif PANEL == LTM10C209
 186#define PANELTYPE       ltm10c209
 187/*
 188 * Untested.
 189 */
 190static struct clcd_panel ltm10c209 = {
 191        .mode           = {
 192                .name           = "LTM10C209",
 193                .refresh        = 0,
 194                .xres           = 640,
 195                .yres           = 480,
 196                .pixclock       = 40000,
 197                .left_margin    = 20,
 198                .right_margin   = 20,
 199                .upper_margin   = 19,
 200                .lower_margin   = 19,
 201                .hsync_len      = 20,
 202                .vsync_len      = 10,
 203                .sync           = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 204                .vmode          = FB_VMODE_NONINTERLACED,
 205        },
 206        .width          = -1,
 207        .height         = -1,
 208        .tim2           = TIM2_BCD,
 209        .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
 210        .fixedtimings   = 1,
 211        .connector      = IMPD1_CTRL_DISP_LCD,
 212        .bpp            = 16,
 213        .grayscale      = 0,
 214};
 215#endif
 216
 217/*
 218 * Disable all display connectors on the interface module.
 219 */
 220static void impd1fb_clcd_disable(struct clcd_fb *fb)
 221{
 222        impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK, 0);
 223}
 224
 225/*
 226 * Enable the relevant connector on the interface module.
 227 */
 228static void impd1fb_clcd_enable(struct clcd_fb *fb)
 229{
 230        impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK,
 231                        fb->panel->connector | IMPD1_CTRL_DISP_ENABLE);
 232}
 233
 234static int impd1fb_clcd_setup(struct clcd_fb *fb)
 235{
 236        unsigned long framebase = fb->dev->res.start + 0x01000000;
 237        unsigned long framesize = SZ_1M;
 238        int ret = 0;
 239
 240        fb->panel = &PANELTYPE;
 241
 242        if (!request_mem_region(framebase, framesize, "clcd framebuffer")) {
 243                printk(KERN_ERR "IM-PD1: unable to reserve framebuffer\n");
 244                return -EBUSY;
 245        }
 246
 247        fb->fb.screen_base = ioremap(framebase, framesize);
 248        if (!fb->fb.screen_base) {
 249                printk(KERN_ERR "IM-PD1: unable to map framebuffer\n");
 250                ret = -ENOMEM;
 251                goto free_buffer;
 252        }
 253
 254        fb->fb.fix.smem_start   = framebase;
 255        fb->fb.fix.smem_len     = framesize;
 256
 257        return 0;
 258
 259 free_buffer:
 260        release_mem_region(framebase, framesize);
 261        return ret;
 262}
 263
 264static int impd1fb_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
 265{
 266        unsigned long start, size;
 267
 268        start = vma->vm_pgoff + (fb->fb.fix.smem_start >> PAGE_SHIFT);
 269        size = vma->vm_end - vma->vm_start;
 270
 271        return remap_pfn_range(vma, vma->vm_start, start, size,
 272                               vma->vm_page_prot);
 273}
 274
 275static void impd1fb_clcd_remove(struct clcd_fb *fb)
 276{
 277        iounmap(fb->fb.screen_base);
 278        release_mem_region(fb->fb.fix.smem_start, fb->fb.fix.smem_len);
 279}
 280
 281static struct clcd_board impd1_clcd_data = {
 282        .name           = "IM-PD/1",
 283        .check          = clcdfb_check,
 284        .decode         = clcdfb_decode,
 285        .disable        = impd1fb_clcd_disable,
 286        .enable         = impd1fb_clcd_enable,
 287        .setup          = impd1fb_clcd_setup,
 288        .mmap           = impd1fb_clcd_mmap,
 289        .remove         = impd1fb_clcd_remove,
 290};
 291
 292struct impd1_device {
 293        unsigned long   offset;
 294        unsigned int    irq[2];
 295        unsigned int    id;
 296        void            *platform_data;
 297};
 298
 299static struct impd1_device impd1_devs[] = {
 300        {
 301                .offset = 0x03000000,
 302                .id     = 0x00041190,
 303        }, {
 304                .offset = 0x00100000,
 305                .irq    = { 1 },
 306                .id     = 0x00141011,
 307        }, {
 308                .offset = 0x00200000,
 309                .irq    = { 2 },
 310                .id     = 0x00141011,
 311        }, {
 312                .offset = 0x00300000,
 313                .irq    = { 3 },
 314                .id     = 0x00041022,
 315        }, {
 316                .offset = 0x00400000,
 317                .irq    = { 4 },
 318                .id     = 0x00041061,
 319        }, {
 320                .offset = 0x00500000,
 321                .irq    = { 5 },
 322                .id     = 0x00041061,
 323        }, {
 324                .offset = 0x00600000,
 325                .irq    = { 6 },
 326                .id     = 0x00041130,
 327        }, {
 328                .offset = 0x00700000,
 329                .irq    = { 7, 8 },
 330                .id     = 0x00041181,
 331        }, {
 332                .offset = 0x00800000,
 333                .irq    = { 9 },
 334                .id     = 0x00041041,
 335        }, {
 336                .offset = 0x01000000,
 337                .irq    = { 11 },
 338                .id     = 0x00041110,
 339                .platform_data = &impd1_clcd_data,
 340        }
 341};
 342
 343static struct clk fixed_14745600 = {
 344        .rate = 14745600,
 345};
 346
 347static int impd1_probe(struct lm_device *dev)
 348{
 349        struct impd1_module *impd1;
 350        int i, ret;
 351
 352        if (dev->id != module_id)
 353                return -EINVAL;
 354
 355        if (!request_mem_region(dev->resource.start, SZ_4K, "LM registers"))
 356                return -EBUSY;
 357
 358        impd1 = kzalloc(sizeof(struct impd1_module), GFP_KERNEL);
 359        if (!impd1) {
 360                ret = -ENOMEM;
 361                goto release_lm;
 362        }
 363
 364        impd1->base = ioremap(dev->resource.start, SZ_4K);
 365        if (!impd1->base) {
 366                ret = -ENOMEM;
 367                goto free_impd1;
 368        }
 369
 370        lm_set_drvdata(dev, impd1);
 371
 372        printk("IM-PD1 found at 0x%08lx\n",
 373                (unsigned long)dev->resource.start);
 374
 375        for (i = 0; i < ARRAY_SIZE(impd1->vcos); i++) {
 376                impd1->vcos[i].owner = THIS_MODULE,
 377                impd1->vcos[i].params = &impd1_vco_params,
 378                impd1->vcos[i].data = impd1,
 379                impd1->vcos[i].setvco = impd1_setvco;
 380        }
 381
 382        impd1->clks[0] = clkdev_alloc(&impd1->vcos[0], NULL, "lm%x:01000",
 383                                        dev->id);
 384        impd1->clks[1] = clkdev_alloc(&fixed_14745600, NULL, "lm%x:00100",
 385                                        dev->id);
 386        impd1->clks[2] = clkdev_alloc(&fixed_14745600, NULL, "lm%x:00200",
 387                                        dev->id);
 388        for (i = 0; i < ARRAY_SIZE(impd1->clks); i++)
 389                clkdev_add(impd1->clks[i]);
 390
 391        for (i = 0; i < ARRAY_SIZE(impd1_devs); i++) {
 392                struct impd1_device *idev = impd1_devs + i;
 393                struct amba_device *d;
 394                unsigned long pc_base;
 395
 396                pc_base = dev->resource.start + idev->offset;
 397
 398                d = kzalloc(sizeof(struct amba_device), GFP_KERNEL);
 399                if (!d)
 400                        continue;
 401
 402                dev_set_name(&d->dev, "lm%x:%5.5lx", dev->id, idev->offset >> 12);
 403                d->dev.parent   = &dev->dev;
 404                d->res.start    = dev->resource.start + idev->offset;
 405                d->res.end      = d->res.start + SZ_4K - 1;
 406                d->res.flags    = IORESOURCE_MEM;
 407                d->irq[0]       = dev->irq;
 408                d->irq[1]       = dev->irq;
 409                d->periphid     = idev->id;
 410                d->dev.platform_data = idev->platform_data;
 411
 412                ret = amba_device_register(d, &dev->resource);
 413                if (ret) {
 414                        dev_err(&d->dev, "unable to register device: %d\n", ret);
 415                        kfree(d);
 416                }
 417        }
 418
 419        return 0;
 420
 421 free_impd1:
 422        if (impd1 && impd1->base)
 423                iounmap(impd1->base);
 424        kfree(impd1);
 425 release_lm:
 426        release_mem_region(dev->resource.start, SZ_4K);
 427        return ret;
 428}
 429
 430static int impd1_remove_one(struct device *dev, void *data)
 431{
 432        device_unregister(dev);
 433        return 0;
 434}
 435
 436static void impd1_remove(struct lm_device *dev)
 437{
 438        struct impd1_module *impd1 = lm_get_drvdata(dev);
 439        int i;
 440
 441        device_for_each_child(&dev->dev, NULL, impd1_remove_one);
 442
 443        for (i = 0; i < ARRAY_SIZE(impd1->clks); i++)
 444                clkdev_drop(impd1->clks[i]);
 445
 446        lm_set_drvdata(dev, NULL);
 447
 448        iounmap(impd1->base);
 449        kfree(impd1);
 450        release_mem_region(dev->resource.start, SZ_4K);
 451}
 452
 453static struct lm_driver impd1_driver = {
 454        .drv = {
 455                .name   = "impd1",
 456        },
 457        .probe          = impd1_probe,
 458        .remove         = impd1_remove,
 459};
 460
 461static int __init impd1_init(void)
 462{
 463        return lm_driver_register(&impd1_driver);
 464}
 465
 466static void __exit impd1_exit(void)
 467{
 468        lm_driver_unregister(&impd1_driver);
 469}
 470
 471module_init(impd1_init);
 472module_exit(impd1_exit);
 473
 474MODULE_LICENSE("GPL");
 475MODULE_DESCRIPTION("Integrator/IM-PD1 logic module core driver");
 476MODULE_AUTHOR("Deep Blue Solutions Ltd");
 477