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