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