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/slab.h> 25#include <linux/clkdev.h> 26 27#include <asm/hardware/icst.h> 28#include <mach/lm.h> 29#include <mach/impd1.h> 30#include <asm/sizes.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 struct clk vcos[2]; 40 struct clk_lookup *clks[3]; 41}; 42 43static const struct icst_params impd1_vco_params = { 44 .ref = 24000000, /* 24 MHz */ 45 .vco_max = ICST525_VCO_MAX_3V, 46 .vco_min = ICST525_VCO_MIN, 47 .vd_min = 12, 48 .vd_max = 519, 49 .rd_min = 3, 50 .rd_max = 120, 51 .s2div = icst525_s2div, 52 .idx2s = icst525_idx2s, 53}; 54 55static void impd1_setvco(struct clk *clk, struct icst_vco vco) 56{ 57 struct impd1_module *impd1 = clk->data; 58 u32 val = vco.v | (vco.r << 9) | (vco.s << 16); 59 60 writel(0xa05f, impd1->base + IMPD1_LOCK); 61 writel(val, clk->vcoreg); 62 writel(0, impd1->base + IMPD1_LOCK); 63 64#ifdef DEBUG 65 vco.v = val & 0x1ff; 66 vco.r = (val >> 9) & 0x7f; 67 vco.s = (val >> 16) & 7; 68 69 pr_debug("IM-PD1: VCO%d clock is %ld Hz\n", 70 vconr, icst525_hz(&impd1_vco_params, vco)); 71#endif 72} 73 74static const struct clk_ops impd1_clk_ops = { 75 .round = icst_clk_round, 76 .set = icst_clk_set, 77 .setvco = impd1_setvco, 78}; 79 80void impd1_tweak_control(struct device *dev, u32 mask, u32 val) 81{ 82 struct impd1_module *impd1 = dev_get_drvdata(dev); 83 u32 cur; 84 85 val &= mask; 86 cur = readl(impd1->base + IMPD1_CTRL) & ~mask; 87 writel(cur | val, impd1->base + IMPD1_CTRL); 88} 89 90EXPORT_SYMBOL(impd1_tweak_control); 91 92/* 93 * CLCD support 94 */ 95#define PANEL PROSPECTOR 96 97#define LTM10C209 1 98#define PROSPECTOR 2 99#define SVGA 3 100#define VGA 4 101 102#if PANEL == VGA 103#define PANELTYPE vga 104static struct clcd_panel vga = { 105 .mode = { 106 .name = "VGA", 107 .refresh = 60, 108 .xres = 640, 109 .yres = 480, 110 .pixclock = 39721, 111 .left_margin = 40, 112 .right_margin = 24, 113 .upper_margin = 32, 114 .lower_margin = 11, 115 .hsync_len = 96, 116 .vsync_len = 2, 117 .sync = 0, 118 .vmode = FB_VMODE_NONINTERLACED, 119 }, 120 .width = -1, 121 .height = -1, 122 .tim2 = TIM2_BCD | TIM2_IPC, 123 .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1), 124 .caps = CLCD_CAP_5551, 125 .connector = IMPD1_CTRL_DISP_VGA, 126 .bpp = 16, 127 .grayscale = 0, 128}; 129 130#elif PANEL == SVGA 131#define PANELTYPE svga 132static struct clcd_panel svga = { 133 .mode = { 134 .name = "SVGA", 135 .refresh = 0, 136 .xres = 800, 137 .yres = 600, 138 .pixclock = 27778, 139 .left_margin = 20, 140 .right_margin = 20, 141 .upper_margin = 5, 142 .lower_margin = 5, 143 .hsync_len = 164, 144 .vsync_len = 62, 145 .sync = 0, 146 .vmode = FB_VMODE_NONINTERLACED, 147 }, 148 .width = -1, 149 .height = -1, 150 .tim2 = TIM2_BCD, 151 .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1), 152 .connector = IMPD1_CTRL_DISP_VGA, 153 .caps = CLCD_CAP_5551, 154 .bpp = 16, 155 .grayscale = 0, 156}; 157 158#elif PANEL == PROSPECTOR 159#define PANELTYPE prospector 160static struct clcd_panel prospector = { 161 .mode = { 162 .name = "PROSPECTOR", 163 .refresh = 0, 164 .xres = 640, 165 .yres = 480, 166 .pixclock = 40000, 167 .left_margin = 33, 168 .right_margin = 64, 169 .upper_margin = 36, 170 .lower_margin = 7, 171 .hsync_len = 64, 172 .vsync_len = 25, 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 187#elif PANEL == LTM10C209 188#define PANELTYPE ltm10c209 189/* 190 * Untested. 191 */ 192static struct clcd_panel ltm10c209 = { 193 .mode = { 194 .name = "LTM10C209", 195 .refresh = 0, 196 .xres = 640, 197 .yres = 480, 198 .pixclock = 40000, 199 .left_margin = 20, 200 .right_margin = 20, 201 .upper_margin = 19, 202 .lower_margin = 19, 203 .hsync_len = 20, 204 .vsync_len = 10, 205 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 206 .vmode = FB_VMODE_NONINTERLACED, 207 }, 208 .width = -1, 209 .height = -1, 210 .tim2 = TIM2_BCD, 211 .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1), 212 .caps = CLCD_CAP_5551, 213 .fixedtimings = 1, 214 .connector = IMPD1_CTRL_DISP_LCD, 215 .bpp = 16, 216 .grayscale = 0, 217}; 218#endif 219 220/* 221 * Disable all display connectors on the interface module. 222 */ 223static void impd1fb_clcd_disable(struct clcd_fb *fb) 224{ 225 impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK, 0); 226} 227 228/* 229 * Enable the relevant connector on the interface module. 230 */ 231static void impd1fb_clcd_enable(struct clcd_fb *fb) 232{ 233 impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK, 234 fb->panel->connector | IMPD1_CTRL_DISP_ENABLE); 235} 236 237static int impd1fb_clcd_setup(struct clcd_fb *fb) 238{ 239 unsigned long framebase = fb->dev->res.start + 0x01000000; 240 unsigned long framesize = SZ_1M; 241 int ret = 0; 242 243 fb->panel = &PANELTYPE; 244 245 if (!request_mem_region(framebase, framesize, "clcd framebuffer")) { 246 printk(KERN_ERR "IM-PD1: unable to reserve framebuffer\n"); 247 return -EBUSY; 248 } 249 250 fb->fb.screen_base = ioremap(framebase, framesize); 251 if (!fb->fb.screen_base) { 252 printk(KERN_ERR "IM-PD1: unable to map framebuffer\n"); 253 ret = -ENOMEM; 254 goto free_buffer; 255 } 256 257 fb->fb.fix.smem_start = framebase; 258 fb->fb.fix.smem_len = framesize; 259 260 return 0; 261 262 free_buffer: 263 release_mem_region(framebase, framesize); 264 return ret; 265} 266 267static int impd1fb_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma) 268{ 269 unsigned long start, size; 270 271 start = vma->vm_pgoff + (fb->fb.fix.smem_start >> PAGE_SHIFT); 272 size = vma->vm_end - vma->vm_start; 273 274 return remap_pfn_range(vma, vma->vm_start, start, size, 275 vma->vm_page_prot); 276} 277 278static void impd1fb_clcd_remove(struct clcd_fb *fb) 279{ 280 iounmap(fb->fb.screen_base); 281 release_mem_region(fb->fb.fix.smem_start, fb->fb.fix.smem_len); 282} 283 284static struct clcd_board impd1_clcd_data = { 285 .name = "IM-PD/1", 286 .caps = CLCD_CAP_5551 | CLCD_CAP_888, 287 .check = clcdfb_check, 288 .decode = clcdfb_decode, 289 .disable = impd1fb_clcd_disable, 290 .enable = impd1fb_clcd_enable, 291 .setup = impd1fb_clcd_setup, 292 .mmap = impd1fb_clcd_mmap, 293 .remove = impd1fb_clcd_remove, 294}; 295 296struct impd1_device { 297 unsigned long offset; 298 unsigned int irq[2]; 299 unsigned int id; 300 void *platform_data; 301}; 302 303static struct impd1_device impd1_devs[] = { 304 { 305 .offset = 0x03000000, 306 .id = 0x00041190, 307 }, { 308 .offset = 0x00100000, 309 .irq = { 1 }, 310 .id = 0x00141011, 311 }, { 312 .offset = 0x00200000, 313 .irq = { 2 }, 314 .id = 0x00141011, 315 }, { 316 .offset = 0x00300000, 317 .irq = { 3 }, 318 .id = 0x00041022, 319 }, { 320 .offset = 0x00400000, 321 .irq = { 4 }, 322 .id = 0x00041061, 323 }, { 324 .offset = 0x00500000, 325 .irq = { 5 }, 326 .id = 0x00041061, 327 }, { 328 .offset = 0x00600000, 329 .irq = { 6 }, 330 .id = 0x00041130, 331 }, { 332 .offset = 0x00700000, 333 .irq = { 7, 8 }, 334 .id = 0x00041181, 335 }, { 336 .offset = 0x00800000, 337 .irq = { 9 }, 338 .id = 0x00041041, 339 }, { 340 .offset = 0x01000000, 341 .irq = { 11 }, 342 .id = 0x00041110, 343 .platform_data = &impd1_clcd_data, 344 } 345}; 346 347static struct clk fixed_14745600 = { 348 .rate = 14745600, 349}; 350 351static int impd1_probe(struct lm_device *dev) 352{ 353 struct impd1_module *impd1; 354 int i, ret; 355 356 if (dev->id != module_id) 357 return -EINVAL; 358 359 if (!request_mem_region(dev->resource.start, SZ_4K, "LM registers")) 360 return -EBUSY; 361 362 impd1 = kzalloc(sizeof(struct impd1_module), GFP_KERNEL); 363 if (!impd1) { 364 ret = -ENOMEM; 365 goto release_lm; 366 } 367 368 impd1->base = ioremap(dev->resource.start, SZ_4K); 369 if (!impd1->base) { 370 ret = -ENOMEM; 371 goto free_impd1; 372 } 373 374 lm_set_drvdata(dev, impd1); 375 376 printk("IM-PD1 found at 0x%08lx\n", 377 (unsigned long)dev->resource.start); 378 379 for (i = 0; i < ARRAY_SIZE(impd1->vcos); i++) { 380 impd1->vcos[i].ops = &impd1_clk_ops, 381 impd1->vcos[i].owner = THIS_MODULE, 382 impd1->vcos[i].params = &impd1_vco_params, 383 impd1->vcos[i].data = impd1; 384 } 385 impd1->vcos[0].vcoreg = impd1->base + IMPD1_OSC1; 386 impd1->vcos[1].vcoreg = impd1->base + IMPD1_OSC2; 387 388 impd1->clks[0] = clkdev_alloc(&impd1->vcos[0], NULL, "lm%x:01000", 389 dev->id); 390 impd1->clks[1] = clkdev_alloc(&fixed_14745600, NULL, "lm%x:00100", 391 dev->id); 392 impd1->clks[2] = clkdev_alloc(&fixed_14745600, NULL, "lm%x:00200", 393 dev->id); 394 for (i = 0; i < ARRAY_SIZE(impd1->clks); i++) 395 clkdev_add(impd1->clks[i]); 396 397 for (i = 0; i < ARRAY_SIZE(impd1_devs); i++) { 398 struct impd1_device *idev = impd1_devs + i; 399 struct amba_device *d; 400 unsigned long pc_base; 401 402 pc_base = dev->resource.start + idev->offset; 403 404 d = amba_device_alloc(NULL, pc_base, SZ_4K); 405 if (!d) 406 continue; 407 408 dev_set_name(&d->dev, "lm%x:%5.5lx", dev->id, idev->offset >> 12); 409 d->dev.parent = &dev->dev; 410 d->irq[0] = dev->irq; 411 d->irq[1] = dev->irq; 412 d->periphid = idev->id; 413 d->dev.platform_data = idev->platform_data; 414 415 ret = amba_device_add(d, &dev->resource); 416 if (ret) { 417 dev_err(&d->dev, "unable to register device: %d\n", ret); 418 amba_device_put(d); 419 } 420 } 421 422 return 0; 423 424 free_impd1: 425 if (impd1 && impd1->base) 426 iounmap(impd1->base); 427 kfree(impd1); 428 release_lm: 429 release_mem_region(dev->resource.start, SZ_4K); 430 return ret; 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 struct impd1_module *impd1 = lm_get_drvdata(dev); 442 int i; 443 444 device_for_each_child(&dev->dev, NULL, impd1_remove_one); 445 446 for (i = 0; i < ARRAY_SIZE(impd1->clks); i++) 447 clkdev_drop(impd1->clks[i]); 448 449 lm_set_drvdata(dev, NULL); 450 451 iounmap(impd1->base); 452 kfree(impd1); 453 release_mem_region(dev->resource.start, SZ_4K); 454} 455 456static struct lm_driver impd1_driver = { 457 .drv = { 458 .name = "impd1", 459 }, 460 .probe = impd1_probe, 461 .remove = impd1_remove, 462}; 463 464static int __init impd1_init(void) 465{ 466 return lm_driver_register(&impd1_driver); 467} 468 469static void __exit impd1_exit(void) 470{ 471 lm_driver_unregister(&impd1_driver); 472} 473 474module_init(impd1_init); 475module_exit(impd1_exit); 476 477MODULE_LICENSE("GPL"); 478MODULE_DESCRIPTION("Integrator/IM-PD1 logic module core driver"); 479MODULE_AUTHOR("Deep Blue Solutions Ltd"); 480