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/platform_data/clk-integrator.h>
  25#include <linux/slab.h>
  26#include <linux/irqchip/arm-vic.h>
  27
  28#include <asm/sizes.h>
  29#include "lm.h"
  30#include "impd1.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        void __iomem    *vic_base;
  40};
  41
  42void impd1_tweak_control(struct device *dev, u32 mask, u32 val)
  43{
  44        struct impd1_module *impd1 = dev_get_drvdata(dev);
  45        u32 cur;
  46
  47        val &= mask;
  48        cur = readl(impd1->base + IMPD1_CTRL) & ~mask;
  49        writel(cur | val, impd1->base + IMPD1_CTRL);
  50}
  51
  52EXPORT_SYMBOL(impd1_tweak_control);
  53
  54/*
  55 * CLCD support
  56 */
  57#define PANEL           PROSPECTOR
  58
  59#define LTM10C209               1
  60#define PROSPECTOR              2
  61#define SVGA                    3
  62#define VGA                     4
  63
  64#if PANEL == VGA
  65#define PANELTYPE       vga
  66static struct clcd_panel vga = {
  67        .mode           = {
  68                .name           = "VGA",
  69                .refresh        = 60,
  70                .xres           = 640,
  71                .yres           = 480,
  72                .pixclock       = 39721,
  73                .left_margin    = 40,
  74                .right_margin   = 24,
  75                .upper_margin   = 32,
  76                .lower_margin   = 11,
  77                .hsync_len      = 96,
  78                .vsync_len      = 2,
  79                .sync           = 0,
  80                .vmode          = FB_VMODE_NONINTERLACED,
  81        },
  82        .width          = -1,
  83        .height         = -1,
  84        .tim2           = TIM2_BCD | TIM2_IPC,
  85        .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
  86        .caps           = CLCD_CAP_5551,
  87        .connector      = IMPD1_CTRL_DISP_VGA,
  88        .bpp            = 16,
  89        .grayscale      = 0,
  90};
  91
  92#elif PANEL == SVGA
  93#define PANELTYPE       svga
  94static struct clcd_panel svga = {
  95        .mode           = {
  96                .name           = "SVGA",
  97                .refresh        = 0,
  98                .xres           = 800,
  99                .yres           = 600,
 100                .pixclock       = 27778,
 101                .left_margin    = 20,
 102                .right_margin   = 20,
 103                .upper_margin   = 5,
 104                .lower_margin   = 5,
 105                .hsync_len      = 164,
 106                .vsync_len      = 62,
 107                .sync           = 0,
 108                .vmode          = FB_VMODE_NONINTERLACED,
 109        },
 110        .width          = -1,
 111        .height         = -1,
 112        .tim2           = TIM2_BCD,
 113        .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
 114        .connector      = IMPD1_CTRL_DISP_VGA,
 115        .caps           = CLCD_CAP_5551,
 116        .bpp            = 16,
 117        .grayscale      = 0,
 118};
 119
 120#elif PANEL == PROSPECTOR
 121#define PANELTYPE       prospector
 122static struct clcd_panel prospector = {
 123        .mode           = {
 124                .name           = "PROSPECTOR",
 125                .refresh        = 0,
 126                .xres           = 640,
 127                .yres           = 480,
 128                .pixclock       = 40000,
 129                .left_margin    = 33,
 130                .right_margin   = 64,
 131                .upper_margin   = 36,
 132                .lower_margin   = 7,
 133                .hsync_len      = 64,
 134                .vsync_len      = 25,
 135                .sync           = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 136                .vmode          = FB_VMODE_NONINTERLACED,
 137        },
 138        .width          = -1,
 139        .height         = -1,
 140        .tim2           = TIM2_BCD,
 141        .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
 142        .caps           = CLCD_CAP_5551,
 143        .fixedtimings   = 1,
 144        .connector      = IMPD1_CTRL_DISP_LCD,
 145        .bpp            = 16,
 146        .grayscale      = 0,
 147};
 148
 149#elif PANEL == LTM10C209
 150#define PANELTYPE       ltm10c209
 151/*
 152 * Untested.
 153 */
 154static struct clcd_panel ltm10c209 = {
 155        .mode           = {
 156                .name           = "LTM10C209",
 157                .refresh        = 0,
 158                .xres           = 640,
 159                .yres           = 480,
 160                .pixclock       = 40000,
 161                .left_margin    = 20,
 162                .right_margin   = 20,
 163                .upper_margin   = 19,
 164                .lower_margin   = 19,
 165                .hsync_len      = 20,
 166                .vsync_len      = 10,
 167                .sync           = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 168                .vmode          = FB_VMODE_NONINTERLACED,
 169        },
 170        .width          = -1,
 171        .height         = -1,
 172        .tim2           = TIM2_BCD,
 173        .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
 174        .caps           = CLCD_CAP_5551,
 175        .fixedtimings   = 1,
 176        .connector      = IMPD1_CTRL_DISP_LCD,
 177        .bpp            = 16,
 178        .grayscale      = 0,
 179};
 180#endif
 181
 182/*
 183 * Disable all display connectors on the interface module.
 184 */
 185static void impd1fb_clcd_disable(struct clcd_fb *fb)
 186{
 187        impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK, 0);
 188}
 189
 190/*
 191 * Enable the relevant connector on the interface module.
 192 */
 193static void impd1fb_clcd_enable(struct clcd_fb *fb)
 194{
 195        impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK,
 196                        fb->panel->connector | IMPD1_CTRL_DISP_ENABLE);
 197}
 198
 199static int impd1fb_clcd_setup(struct clcd_fb *fb)
 200{
 201        unsigned long framebase = fb->dev->res.start + 0x01000000;
 202        unsigned long framesize = SZ_1M;
 203        int ret = 0;
 204
 205        fb->panel = &PANELTYPE;
 206
 207        if (!request_mem_region(framebase, framesize, "clcd framebuffer")) {
 208                printk(KERN_ERR "IM-PD1: unable to reserve framebuffer\n");
 209                return -EBUSY;
 210        }
 211
 212        fb->fb.screen_base = ioremap(framebase, framesize);
 213        if (!fb->fb.screen_base) {
 214                printk(KERN_ERR "IM-PD1: unable to map framebuffer\n");
 215                ret = -ENOMEM;
 216                goto free_buffer;
 217        }
 218
 219        fb->fb.fix.smem_start   = framebase;
 220        fb->fb.fix.smem_len     = framesize;
 221
 222        return 0;
 223
 224 free_buffer:
 225        release_mem_region(framebase, framesize);
 226        return ret;
 227}
 228
 229static int impd1fb_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
 230{
 231        unsigned long start, size;
 232
 233        start = vma->vm_pgoff + (fb->fb.fix.smem_start >> PAGE_SHIFT);
 234        size = vma->vm_end - vma->vm_start;
 235
 236        return remap_pfn_range(vma, vma->vm_start, start, size,
 237                               vma->vm_page_prot);
 238}
 239
 240static void impd1fb_clcd_remove(struct clcd_fb *fb)
 241{
 242        iounmap(fb->fb.screen_base);
 243        release_mem_region(fb->fb.fix.smem_start, fb->fb.fix.smem_len);
 244}
 245
 246static struct clcd_board impd1_clcd_data = {
 247        .name           = "IM-PD/1",
 248        .caps           = CLCD_CAP_5551 | CLCD_CAP_888,
 249        .check          = clcdfb_check,
 250        .decode         = clcdfb_decode,
 251        .disable        = impd1fb_clcd_disable,
 252        .enable         = impd1fb_clcd_enable,
 253        .setup          = impd1fb_clcd_setup,
 254        .mmap           = impd1fb_clcd_mmap,
 255        .remove         = impd1fb_clcd_remove,
 256};
 257
 258struct impd1_device {
 259        unsigned long   offset;
 260        unsigned int    irq[2];
 261        unsigned int    id;
 262        void            *platform_data;
 263};
 264
 265static struct impd1_device impd1_devs[] = {
 266        {
 267                .offset = 0x00100000,
 268                .irq    = { 1 },
 269                .id     = 0x00141011,
 270        }, {
 271                .offset = 0x00200000,
 272                .irq    = { 2 },
 273                .id     = 0x00141011,
 274        }, {
 275                .offset = 0x00300000,
 276                .irq    = { 3 },
 277                .id     = 0x00041022,
 278        }, {
 279                .offset = 0x00400000,
 280                .irq    = { 4 },
 281                .id     = 0x00041061,
 282        }, {
 283                .offset = 0x00500000,
 284                .irq    = { 5 },
 285                .id     = 0x00041061,
 286        }, {
 287                .offset = 0x00600000,
 288                .irq    = { 6 },
 289                .id     = 0x00041130,
 290        }, {
 291                .offset = 0x00700000,
 292                .irq    = { 7, 8 },
 293                .id     = 0x00041181,
 294        }, {
 295                .offset = 0x00800000,
 296                .irq    = { 9 },
 297                .id     = 0x00041041,
 298        }, {
 299                .offset = 0x01000000,
 300                .irq    = { 11 },
 301                .id     = 0x00041110,
 302                .platform_data = &impd1_clcd_data,
 303        }
 304};
 305
 306/*
 307 * Valid IRQs: 0 thru 9 and 11, 10 unused.
 308 */
 309#define IMPD1_VALID_IRQS 0x00000bffU
 310
 311/*
 312 * As this module is bool, it is OK to have this as __init_refok() - no
 313 * probe calls will be done after the initial system bootup, as devices
 314 * are discovered as part of the machine startup.
 315 */
 316static int __init_refok impd1_probe(struct lm_device *dev)
 317{
 318        struct impd1_module *impd1;
 319        int irq_base;
 320        int i;
 321
 322        if (dev->id != module_id)
 323                return -EINVAL;
 324
 325        if (!devm_request_mem_region(&dev->dev, dev->resource.start,
 326                                     SZ_4K, "LM registers"))
 327                return -EBUSY;
 328
 329        impd1 = devm_kzalloc(&dev->dev, sizeof(struct impd1_module),
 330                             GFP_KERNEL);
 331        if (!impd1)
 332                return -ENOMEM;
 333
 334        impd1->base = devm_ioremap(&dev->dev, dev->resource.start, SZ_4K);
 335        if (!impd1->base)
 336                return -ENOMEM;
 337
 338        integrator_impd1_clk_init(impd1->base, dev->id);
 339
 340        if (!devm_request_mem_region(&dev->dev,
 341                                     dev->resource.start + 0x03000000,
 342                                     SZ_4K, "VIC"))
 343                return -EBUSY;
 344
 345        impd1->vic_base = devm_ioremap(&dev->dev,
 346                                       dev->resource.start + 0x03000000,
 347                                       SZ_4K);
 348        if (!impd1->vic_base)
 349                return -ENOMEM;
 350
 351        irq_base = vic_init_cascaded(impd1->vic_base, dev->irq,
 352                                     IMPD1_VALID_IRQS, 0);
 353
 354        lm_set_drvdata(dev, impd1);
 355
 356        dev_info(&dev->dev, "IM-PD1 found at 0x%08lx\n",
 357                 (unsigned long)dev->resource.start);
 358
 359        for (i = 0; i < ARRAY_SIZE(impd1_devs); i++) {
 360                struct impd1_device *idev = impd1_devs + i;
 361                struct amba_device *d;
 362                unsigned long pc_base;
 363                char devname[32];
 364                int irq1 = idev->irq[0];
 365                int irq2 = idev->irq[1];
 366
 367                /* Translate IRQs to IM-PD1 local numberspace */
 368                if (irq1)
 369                        irq1 += irq_base;
 370                if (irq2)
 371                        irq2 += irq_base;
 372
 373                pc_base = dev->resource.start + idev->offset;
 374                snprintf(devname, 32, "lm%x:%5.5lx", dev->id, idev->offset >> 12);
 375                d = amba_ahb_device_add_res(&dev->dev, devname, pc_base, SZ_4K,
 376                                            irq1, irq2,
 377                                            idev->platform_data, idev->id,
 378                                            &dev->resource);
 379                if (IS_ERR(d)) {
 380                        dev_err(&dev->dev, "unable to register device: %ld\n", PTR_ERR(d));
 381                        continue;
 382                }
 383        }
 384
 385        return 0;
 386}
 387
 388static int impd1_remove_one(struct device *dev, void *data)
 389{
 390        device_unregister(dev);
 391        return 0;
 392}
 393
 394static void impd1_remove(struct lm_device *dev)
 395{
 396        device_for_each_child(&dev->dev, NULL, impd1_remove_one);
 397        integrator_impd1_clk_exit(dev->id);
 398
 399        lm_set_drvdata(dev, NULL);
 400}
 401
 402static struct lm_driver impd1_driver = {
 403        .drv = {
 404                .name   = "impd1",
 405                /*
 406                 * As we're dropping the probe() function, suppress driver
 407                 * binding from sysfs.
 408                 */
 409                .suppress_bind_attrs = true,
 410        },
 411        .probe          = impd1_probe,
 412        .remove         = impd1_remove,
 413};
 414
 415static int __init impd1_init(void)
 416{
 417        return lm_driver_register(&impd1_driver);
 418}
 419
 420static void __exit impd1_exit(void)
 421{
 422        lm_driver_unregister(&impd1_driver);
 423}
 424
 425module_init(impd1_init);
 426module_exit(impd1_exit);
 427
 428MODULE_LICENSE("GPL");
 429MODULE_DESCRIPTION("Integrator/IM-PD1 logic module core driver");
 430MODULE_AUTHOR("Deep Blue Solutions Ltd");
 431