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