Merge branch 'x86-cpu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[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/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
35 static int module_id;
36
37 module_param_named(lmid, module_id, int, 0444);
38 MODULE_PARM_DESC(lmid, "logic module stack position");
39
40 struct impd1_module {
41         void __iomem    *base;
42         void __iomem    *vic_base;
43 };
44
45 void 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
55 EXPORT_SYMBOL(impd1_tweak_control);
56
57 /*
58  * MMC support
59  */
60 static 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
76 static 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
104 static 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
132 static 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  */
164 static 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  */
195 static 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  */
203 static 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
209 static 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
239 static 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
250 static 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
256 static 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
268 struct impd1_device {
269         unsigned long   offset;
270         unsigned int    irq[2];
271         unsigned int    id;
272         void            *platform_data;
273 };
274
275 static 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  */
327 static 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
436 static int impd1_remove_one(struct device *dev, void *data)
437 {
438         device_unregister(dev);
439         return 0;
440 }
441
442 static 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
450 static 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
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");