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