Merge branch 'drm-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied...
[sfrench/cifs-2.6.git] / 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 #include <linux/slab.h>
25
26 #include <asm/clkdev.h>
27 #include <mach/clkdev.h>
28 #include <asm/hardware/icst.h>
29 #include <mach/lm.h>
30 #include <mach/impd1.h>
31 #include <asm/sizes.h>
32
33 static int module_id;
34
35 module_param_named(lmid, module_id, int, 0444);
36 MODULE_PARM_DESC(lmid, "logic module stack position");
37
38 struct impd1_module {
39         void __iomem    *base;
40         struct clk      vcos[2];
41         struct clk_lookup *clks[3];
42 };
43
44 static const struct icst_params impd1_vco_params = {
45         .ref            = 24000000,     /* 24 MHz */
46         .vco_max        = ICST525_VCO_MAX_3V,
47         .vco_min        = ICST525_VCO_MIN,
48         .vd_min         = 12,
49         .vd_max         = 519,
50         .rd_min         = 3,
51         .rd_max         = 120,
52         .s2div          = icst525_s2div,
53         .idx2s          = icst525_idx2s,
54 };
55
56 static void impd1_setvco(struct clk *clk, struct icst_vco vco)
57 {
58         struct impd1_module *impd1 = clk->data;
59         u32 val = vco.v | (vco.r << 9) | (vco.s << 16);
60
61         writel(0xa05f, impd1->base + IMPD1_LOCK);
62         writel(val, clk->vcoreg);
63         writel(0, impd1->base + IMPD1_LOCK);
64
65 #ifdef DEBUG
66         vco.v = val & 0x1ff;
67         vco.r = (val >> 9) & 0x7f;
68         vco.s = (val >> 16) & 7;
69
70         pr_debug("IM-PD1: VCO%d clock is %ld Hz\n",
71                  vconr, icst525_hz(&impd1_vco_params, vco));
72 #endif
73 }
74
75 static const struct clk_ops impd1_clk_ops = {
76         .round  = icst_clk_round,
77         .set    = icst_clk_set,
78         .setvco = impd1_setvco,
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].ops = &impd1_clk_ops,
377                 impd1->vcos[i].owner = THIS_MODULE,
378                 impd1->vcos[i].params = &impd1_vco_params,
379                 impd1->vcos[i].data = impd1;
380         }
381         impd1->vcos[0].vcoreg = impd1->base + IMPD1_OSC1;
382         impd1->vcos[1].vcoreg = impd1->base + IMPD1_OSC2;
383
384         impd1->clks[0] = clkdev_alloc(&impd1->vcos[0], NULL, "lm%x:01000",
385                                         dev->id);
386         impd1->clks[1] = clkdev_alloc(&fixed_14745600, NULL, "lm%x:00100",
387                                         dev->id);
388         impd1->clks[2] = clkdev_alloc(&fixed_14745600, NULL, "lm%x:00200",
389                                         dev->id);
390         for (i = 0; i < ARRAY_SIZE(impd1->clks); i++)
391                 clkdev_add(impd1->clks[i]);
392
393         for (i = 0; i < ARRAY_SIZE(impd1_devs); i++) {
394                 struct impd1_device *idev = impd1_devs + i;
395                 struct amba_device *d;
396                 unsigned long pc_base;
397
398                 pc_base = dev->resource.start + idev->offset;
399
400                 d = kzalloc(sizeof(struct amba_device), GFP_KERNEL);
401                 if (!d)
402                         continue;
403
404                 dev_set_name(&d->dev, "lm%x:%5.5lx", dev->id, idev->offset >> 12);
405                 d->dev.parent   = &dev->dev;
406                 d->res.start    = dev->resource.start + idev->offset;
407                 d->res.end      = d->res.start + SZ_4K - 1;
408                 d->res.flags    = IORESOURCE_MEM;
409                 d->irq[0]       = dev->irq;
410                 d->irq[1]       = dev->irq;
411                 d->periphid     = idev->id;
412                 d->dev.platform_data = idev->platform_data;
413
414                 ret = amba_device_register(d, &dev->resource);
415                 if (ret) {
416                         dev_err(&d->dev, "unable to register device: %d\n", ret);
417                         kfree(d);
418                 }
419         }
420
421         return 0;
422
423  free_impd1:
424         if (impd1 && impd1->base)
425                 iounmap(impd1->base);
426         kfree(impd1);
427  release_lm:
428         release_mem_region(dev->resource.start, SZ_4K);
429         return ret;
430 }
431
432 static int impd1_remove_one(struct device *dev, void *data)
433 {
434         device_unregister(dev);
435         return 0;
436 }
437
438 static void impd1_remove(struct lm_device *dev)
439 {
440         struct impd1_module *impd1 = lm_get_drvdata(dev);
441         int i;
442
443         device_for_each_child(&dev->dev, NULL, impd1_remove_one);
444
445         for (i = 0; i < ARRAY_SIZE(impd1->clks); i++)
446                 clkdev_drop(impd1->clks[i]);
447
448         lm_set_drvdata(dev, NULL);
449
450         iounmap(impd1->base);
451         kfree(impd1);
452         release_mem_region(dev->resource.start, SZ_4K);
453 }
454
455 static struct lm_driver impd1_driver = {
456         .drv = {
457                 .name   = "impd1",
458         },
459         .probe          = impd1_probe,
460         .remove         = impd1_remove,
461 };
462
463 static int __init impd1_init(void)
464 {
465         return lm_driver_register(&impd1_driver);
466 }
467
468 static void __exit impd1_exit(void)
469 {
470         lm_driver_unregister(&impd1_driver);
471 }
472
473 module_init(impd1_init);
474 module_exit(impd1_exit);
475
476 MODULE_LICENSE("GPL");
477 MODULE_DESCRIPTION("Integrator/IM-PD1 logic module core driver");
478 MODULE_AUTHOR("Deep Blue Solutions Ltd");