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 __init_refok() - 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 __init_refok 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