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