Merge branch 'for-linus' of git://git.linaro.org/people/rmk/linux-arm
[linux-3.10.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/platform_data/clk-integrator.h>
25 #include <linux/slab.h>
26
27 #include <mach/lm.h>
28 #include <mach/impd1.h>
29 #include <asm/sizes.h>
30
31 static int module_id;
32
33 module_param_named(lmid, module_id, int, 0444);
34 MODULE_PARM_DESC(lmid, "logic module stack position");
35
36 struct impd1_module {
37         void __iomem    *base;
38 };
39
40 void impd1_tweak_control(struct device *dev, u32 mask, u32 val)
41 {
42         struct impd1_module *impd1 = dev_get_drvdata(dev);
43         u32 cur;
44
45         val &= mask;
46         cur = readl(impd1->base + IMPD1_CTRL) & ~mask;
47         writel(cur | val, impd1->base + IMPD1_CTRL);
48 }
49
50 EXPORT_SYMBOL(impd1_tweak_control);
51
52 /*
53  * CLCD support
54  */
55 #define PANEL           PROSPECTOR
56
57 #define LTM10C209               1
58 #define PROSPECTOR              2
59 #define SVGA                    3
60 #define VGA                     4
61
62 #if PANEL == VGA
63 #define PANELTYPE       vga
64 static struct clcd_panel vga = {
65         .mode           = {
66                 .name           = "VGA",
67                 .refresh        = 60,
68                 .xres           = 640,
69                 .yres           = 480,
70                 .pixclock       = 39721,
71                 .left_margin    = 40,
72                 .right_margin   = 24,
73                 .upper_margin   = 32,
74                 .lower_margin   = 11,
75                 .hsync_len      = 96,
76                 .vsync_len      = 2,
77                 .sync           = 0,
78                 .vmode          = FB_VMODE_NONINTERLACED,
79         },
80         .width          = -1,
81         .height         = -1,
82         .tim2           = TIM2_BCD | TIM2_IPC,
83         .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
84         .caps           = CLCD_CAP_5551,
85         .connector      = IMPD1_CTRL_DISP_VGA,
86         .bpp            = 16,
87         .grayscale      = 0,
88 };
89
90 #elif PANEL == SVGA
91 #define PANELTYPE       svga
92 static struct clcd_panel svga = {
93         .mode           = {
94                 .name           = "SVGA",
95                 .refresh        = 0,
96                 .xres           = 800,
97                 .yres           = 600,
98                 .pixclock       = 27778,
99                 .left_margin    = 20,
100                 .right_margin   = 20,
101                 .upper_margin   = 5,
102                 .lower_margin   = 5,
103                 .hsync_len      = 164,
104                 .vsync_len      = 62,
105                 .sync           = 0,
106                 .vmode          = FB_VMODE_NONINTERLACED,
107         },
108         .width          = -1,
109         .height         = -1,
110         .tim2           = TIM2_BCD,
111         .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
112         .connector      = IMPD1_CTRL_DISP_VGA,
113         .caps           = CLCD_CAP_5551,
114         .bpp            = 16,
115         .grayscale      = 0,
116 };
117
118 #elif PANEL == PROSPECTOR
119 #define PANELTYPE       prospector
120 static struct clcd_panel prospector = {
121         .mode           = {
122                 .name           = "PROSPECTOR",
123                 .refresh        = 0,
124                 .xres           = 640,
125                 .yres           = 480,
126                 .pixclock       = 40000,
127                 .left_margin    = 33,
128                 .right_margin   = 64,
129                 .upper_margin   = 36,
130                 .lower_margin   = 7,
131                 .hsync_len      = 64,
132                 .vsync_len      = 25,
133                 .sync           = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
134                 .vmode          = FB_VMODE_NONINTERLACED,
135         },
136         .width          = -1,
137         .height         = -1,
138         .tim2           = TIM2_BCD,
139         .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
140         .caps           = CLCD_CAP_5551,
141         .fixedtimings   = 1,
142         .connector      = IMPD1_CTRL_DISP_LCD,
143         .bpp            = 16,
144         .grayscale      = 0,
145 };
146
147 #elif PANEL == LTM10C209
148 #define PANELTYPE       ltm10c209
149 /*
150  * Untested.
151  */
152 static struct clcd_panel ltm10c209 = {
153         .mode           = {
154                 .name           = "LTM10C209",
155                 .refresh        = 0,
156                 .xres           = 640,
157                 .yres           = 480,
158                 .pixclock       = 40000,
159                 .left_margin    = 20,
160                 .right_margin   = 20,
161                 .upper_margin   = 19,
162                 .lower_margin   = 19,
163                 .hsync_len      = 20,
164                 .vsync_len      = 10,
165                 .sync           = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
166                 .vmode          = FB_VMODE_NONINTERLACED,
167         },
168         .width          = -1,
169         .height         = -1,
170         .tim2           = TIM2_BCD,
171         .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
172         .caps           = CLCD_CAP_5551,
173         .fixedtimings   = 1,
174         .connector      = IMPD1_CTRL_DISP_LCD,
175         .bpp            = 16,
176         .grayscale      = 0,
177 };
178 #endif
179
180 /*
181  * Disable all display connectors on the interface module.
182  */
183 static void impd1fb_clcd_disable(struct clcd_fb *fb)
184 {
185         impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK, 0);
186 }
187
188 /*
189  * Enable the relevant connector on the interface module.
190  */
191 static void impd1fb_clcd_enable(struct clcd_fb *fb)
192 {
193         impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK,
194                         fb->panel->connector | IMPD1_CTRL_DISP_ENABLE);
195 }
196
197 static int impd1fb_clcd_setup(struct clcd_fb *fb)
198 {
199         unsigned long framebase = fb->dev->res.start + 0x01000000;
200         unsigned long framesize = SZ_1M;
201         int ret = 0;
202
203         fb->panel = &PANELTYPE;
204
205         if (!request_mem_region(framebase, framesize, "clcd framebuffer")) {
206                 printk(KERN_ERR "IM-PD1: unable to reserve framebuffer\n");
207                 return -EBUSY;
208         }
209
210         fb->fb.screen_base = ioremap(framebase, framesize);
211         if (!fb->fb.screen_base) {
212                 printk(KERN_ERR "IM-PD1: unable to map framebuffer\n");
213                 ret = -ENOMEM;
214                 goto free_buffer;
215         }
216
217         fb->fb.fix.smem_start   = framebase;
218         fb->fb.fix.smem_len     = framesize;
219
220         return 0;
221
222  free_buffer:
223         release_mem_region(framebase, framesize);
224         return ret;
225 }
226
227 static int impd1fb_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
228 {
229         unsigned long start, size;
230
231         start = vma->vm_pgoff + (fb->fb.fix.smem_start >> PAGE_SHIFT);
232         size = vma->vm_end - vma->vm_start;
233
234         return remap_pfn_range(vma, vma->vm_start, start, size,
235                                vma->vm_page_prot);
236 }
237
238 static void impd1fb_clcd_remove(struct clcd_fb *fb)
239 {
240         iounmap(fb->fb.screen_base);
241         release_mem_region(fb->fb.fix.smem_start, fb->fb.fix.smem_len);
242 }
243
244 static struct clcd_board impd1_clcd_data = {
245         .name           = "IM-PD/1",
246         .caps           = CLCD_CAP_5551 | CLCD_CAP_888,
247         .check          = clcdfb_check,
248         .decode         = clcdfb_decode,
249         .disable        = impd1fb_clcd_disable,
250         .enable         = impd1fb_clcd_enable,
251         .setup          = impd1fb_clcd_setup,
252         .mmap           = impd1fb_clcd_mmap,
253         .remove         = impd1fb_clcd_remove,
254 };
255
256 struct impd1_device {
257         unsigned long   offset;
258         unsigned int    irq[2];
259         unsigned int    id;
260         void            *platform_data;
261 };
262
263 static struct impd1_device impd1_devs[] = {
264         {
265                 .offset = 0x03000000,
266                 .id     = 0x00041190,
267         }, {
268                 .offset = 0x00100000,
269                 .irq    = { 1 },
270                 .id     = 0x00141011,
271         }, {
272                 .offset = 0x00200000,
273                 .irq    = { 2 },
274                 .id     = 0x00141011,
275         }, {
276                 .offset = 0x00300000,
277                 .irq    = { 3 },
278                 .id     = 0x00041022,
279         }, {
280                 .offset = 0x00400000,
281                 .irq    = { 4 },
282                 .id     = 0x00041061,
283         }, {
284                 .offset = 0x00500000,
285                 .irq    = { 5 },
286                 .id     = 0x00041061,
287         }, {
288                 .offset = 0x00600000,
289                 .irq    = { 6 },
290                 .id     = 0x00041130,
291         }, {
292                 .offset = 0x00700000,
293                 .irq    = { 7, 8 },
294                 .id     = 0x00041181,
295         }, {
296                 .offset = 0x00800000,
297                 .irq    = { 9 },
298                 .id     = 0x00041041,
299         }, {
300                 .offset = 0x01000000,
301                 .irq    = { 11 },
302                 .id     = 0x00041110,
303                 .platform_data = &impd1_clcd_data,
304         }
305 };
306
307 static int impd1_probe(struct lm_device *dev)
308 {
309         struct impd1_module *impd1;
310         int i, ret;
311
312         if (dev->id != module_id)
313                 return -EINVAL;
314
315         if (!request_mem_region(dev->resource.start, SZ_4K, "LM registers"))
316                 return -EBUSY;
317
318         impd1 = kzalloc(sizeof(struct impd1_module), GFP_KERNEL);
319         if (!impd1) {
320                 ret = -ENOMEM;
321                 goto release_lm;
322         }
323
324         impd1->base = ioremap(dev->resource.start, SZ_4K);
325         if (!impd1->base) {
326                 ret = -ENOMEM;
327                 goto free_impd1;
328         }
329
330         lm_set_drvdata(dev, impd1);
331
332         printk("IM-PD1 found at 0x%08lx\n",
333                 (unsigned long)dev->resource.start);
334
335         integrator_impd1_clk_init(impd1->base, dev->id);
336
337         for (i = 0; i < ARRAY_SIZE(impd1_devs); i++) {
338                 struct impd1_device *idev = impd1_devs + i;
339                 struct amba_device *d;
340                 unsigned long pc_base;
341                 char devname[32];
342
343                 pc_base = dev->resource.start + idev->offset;
344                 snprintf(devname, 32, "lm%x:%5.5lx", dev->id, idev->offset >> 12);
345                 d = amba_ahb_device_add_res(&dev->dev, devname, pc_base, SZ_4K,
346                                             dev->irq, dev->irq,
347                                             idev->platform_data, idev->id,
348                                             &dev->resource);
349                 if (IS_ERR(d)) {
350                         dev_err(&dev->dev, "unable to register device: %ld\n", PTR_ERR(d));
351                         continue;
352                 }
353         }
354
355         return 0;
356
357  free_impd1:
358         if (impd1 && impd1->base)
359                 iounmap(impd1->base);
360         kfree(impd1);
361  release_lm:
362         release_mem_region(dev->resource.start, SZ_4K);
363         return ret;
364 }
365
366 static int impd1_remove_one(struct device *dev, void *data)
367 {
368         device_unregister(dev);
369         return 0;
370 }
371
372 static void impd1_remove(struct lm_device *dev)
373 {
374         struct impd1_module *impd1 = lm_get_drvdata(dev);
375
376         device_for_each_child(&dev->dev, NULL, impd1_remove_one);
377         integrator_impd1_clk_exit(dev->id);
378
379         lm_set_drvdata(dev, NULL);
380
381         iounmap(impd1->base);
382         kfree(impd1);
383         release_mem_region(dev->resource.start, SZ_4K);
384 }
385
386 static struct lm_driver impd1_driver = {
387         .drv = {
388                 .name   = "impd1",
389         },
390         .probe          = impd1_probe,
391         .remove         = impd1_remove,
392 };
393
394 static int __init impd1_init(void)
395 {
396         return lm_driver_register(&impd1_driver);
397 }
398
399 static void __exit impd1_exit(void)
400 {
401         lm_driver_unregister(&impd1_driver);
402 }
403
404 module_init(impd1_init);
405 module_exit(impd1_exit);
406
407 MODULE_LICENSE("GPL");
408 MODULE_DESCRIPTION("Integrator/IM-PD1 logic module core driver");
409 MODULE_AUTHOR("Deep Blue Solutions Ltd");