Merge tag 'trace-v5.0-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt...
[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/io.h>
25 #include <linux/platform_data/clk-integrator.h>
26 #include <linux/slab.h>
27 #include <linux/irqchip/arm-vic.h>
28 #include <linux/gpio/machine.h>
29
30 #include <asm/sizes.h>
31 #include "lm.h"
32 #include "impd1.h"
33
34 static int module_id;
35
36 module_param_named(lmid, module_id, int, 0444);
37 MODULE_PARM_DESC(lmid, "logic module stack position");
38
39 struct impd1_module {
40         void __iomem    *base;
41         void __iomem    *vic_base;
42 };
43
44 void impd1_tweak_control(struct device *dev, u32 mask, u32 val)
45 {
46         struct impd1_module *impd1 = dev_get_drvdata(dev);
47         u32 cur;
48
49         val &= mask;
50         cur = readl(impd1->base + IMPD1_CTRL) & ~mask;
51         writel(cur | val, impd1->base + IMPD1_CTRL);
52 }
53
54 EXPORT_SYMBOL(impd1_tweak_control);
55
56 /*
57  * MMC support
58  */
59 static struct mmci_platform_data mmc_data = {
60         .ocr_mask       = MMC_VDD_32_33|MMC_VDD_33_34,
61 };
62
63 /*
64  * CLCD support
65  */
66 #define PANEL           PROSPECTOR
67
68 #define LTM10C209               1
69 #define PROSPECTOR              2
70 #define SVGA                    3
71 #define VGA                     4
72
73 #if PANEL == VGA
74 #define PANELTYPE       vga
75 static struct clcd_panel vga = {
76         .mode           = {
77                 .name           = "VGA",
78                 .refresh        = 60,
79                 .xres           = 640,
80                 .yres           = 480,
81                 .pixclock       = 39721,
82                 .left_margin    = 40,
83                 .right_margin   = 24,
84                 .upper_margin   = 32,
85                 .lower_margin   = 11,
86                 .hsync_len      = 96,
87                 .vsync_len      = 2,
88                 .sync           = 0,
89                 .vmode          = FB_VMODE_NONINTERLACED,
90         },
91         .width          = -1,
92         .height         = -1,
93         .tim2           = TIM2_BCD | TIM2_IPC,
94         .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
95         .caps           = CLCD_CAP_5551,
96         .connector      = IMPD1_CTRL_DISP_VGA,
97         .bpp            = 16,
98         .grayscale      = 0,
99 };
100
101 #elif PANEL == SVGA
102 #define PANELTYPE       svga
103 static struct clcd_panel svga = {
104         .mode           = {
105                 .name           = "SVGA",
106                 .refresh        = 0,
107                 .xres           = 800,
108                 .yres           = 600,
109                 .pixclock       = 27778,
110                 .left_margin    = 20,
111                 .right_margin   = 20,
112                 .upper_margin   = 5,
113                 .lower_margin   = 5,
114                 .hsync_len      = 164,
115                 .vsync_len      = 62,
116                 .sync           = 0,
117                 .vmode          = FB_VMODE_NONINTERLACED,
118         },
119         .width          = -1,
120         .height         = -1,
121         .tim2           = TIM2_BCD,
122         .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
123         .connector      = IMPD1_CTRL_DISP_VGA,
124         .caps           = CLCD_CAP_5551,
125         .bpp            = 16,
126         .grayscale      = 0,
127 };
128
129 #elif PANEL == PROSPECTOR
130 #define PANELTYPE       prospector
131 static struct clcd_panel prospector = {
132         .mode           = {
133                 .name           = "PROSPECTOR",
134                 .refresh        = 0,
135                 .xres           = 640,
136                 .yres           = 480,
137                 .pixclock       = 40000,
138                 .left_margin    = 33,
139                 .right_margin   = 64,
140                 .upper_margin   = 36,
141                 .lower_margin   = 7,
142                 .hsync_len      = 64,
143                 .vsync_len      = 25,
144                 .sync           = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
145                 .vmode          = FB_VMODE_NONINTERLACED,
146         },
147         .width          = -1,
148         .height         = -1,
149         .tim2           = TIM2_BCD,
150         .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
151         .caps           = CLCD_CAP_5551,
152         .fixedtimings   = 1,
153         .connector      = IMPD1_CTRL_DISP_LCD,
154         .bpp            = 16,
155         .grayscale      = 0,
156 };
157
158 #elif PANEL == LTM10C209
159 #define PANELTYPE       ltm10c209
160 /*
161  * Untested.
162  */
163 static struct clcd_panel ltm10c209 = {
164         .mode           = {
165                 .name           = "LTM10C209",
166                 .refresh        = 0,
167                 .xres           = 640,
168                 .yres           = 480,
169                 .pixclock       = 40000,
170                 .left_margin    = 20,
171                 .right_margin   = 20,
172                 .upper_margin   = 19,
173                 .lower_margin   = 19,
174                 .hsync_len      = 20,
175                 .vsync_len      = 10,
176                 .sync           = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
177                 .vmode          = FB_VMODE_NONINTERLACED,
178         },
179         .width          = -1,
180         .height         = -1,
181         .tim2           = TIM2_BCD,
182         .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
183         .caps           = CLCD_CAP_5551,
184         .fixedtimings   = 1,
185         .connector      = IMPD1_CTRL_DISP_LCD,
186         .bpp            = 16,
187         .grayscale      = 0,
188 };
189 #endif
190
191 /*
192  * Disable all display connectors on the interface module.
193  */
194 static void impd1fb_clcd_disable(struct clcd_fb *fb)
195 {
196         impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK, 0);
197 }
198
199 /*
200  * Enable the relevant connector on the interface module.
201  */
202 static void impd1fb_clcd_enable(struct clcd_fb *fb)
203 {
204         impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK,
205                         fb->panel->connector | IMPD1_CTRL_DISP_ENABLE);
206 }
207
208 static int impd1fb_clcd_setup(struct clcd_fb *fb)
209 {
210         unsigned long framebase = fb->dev->res.start + 0x01000000;
211         unsigned long framesize = SZ_1M;
212         int ret = 0;
213
214         fb->panel = &PANELTYPE;
215
216         if (!request_mem_region(framebase, framesize, "clcd framebuffer")) {
217                 printk(KERN_ERR "IM-PD1: unable to reserve framebuffer\n");
218                 return -EBUSY;
219         }
220
221         fb->fb.screen_base = ioremap(framebase, framesize);
222         if (!fb->fb.screen_base) {
223                 printk(KERN_ERR "IM-PD1: unable to map framebuffer\n");
224                 ret = -ENOMEM;
225                 goto free_buffer;
226         }
227
228         fb->fb.fix.smem_start   = framebase;
229         fb->fb.fix.smem_len     = framesize;
230
231         return 0;
232
233  free_buffer:
234         release_mem_region(framebase, framesize);
235         return ret;
236 }
237
238 static int impd1fb_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
239 {
240         unsigned long start, size;
241
242         start = vma->vm_pgoff + (fb->fb.fix.smem_start >> PAGE_SHIFT);
243         size = vma->vm_end - vma->vm_start;
244
245         return remap_pfn_range(vma, vma->vm_start, start, size,
246                                vma->vm_page_prot);
247 }
248
249 static void impd1fb_clcd_remove(struct clcd_fb *fb)
250 {
251         iounmap(fb->fb.screen_base);
252         release_mem_region(fb->fb.fix.smem_start, fb->fb.fix.smem_len);
253 }
254
255 static struct clcd_board impd1_clcd_data = {
256         .name           = "IM-PD/1",
257         .caps           = CLCD_CAP_5551 | CLCD_CAP_888,
258         .check          = clcdfb_check,
259         .decode         = clcdfb_decode,
260         .disable        = impd1fb_clcd_disable,
261         .enable         = impd1fb_clcd_enable,
262         .setup          = impd1fb_clcd_setup,
263         .mmap           = impd1fb_clcd_mmap,
264         .remove         = impd1fb_clcd_remove,
265 };
266
267 struct impd1_device {
268         unsigned long   offset;
269         unsigned int    irq[2];
270         unsigned int    id;
271         void            *platform_data;
272 };
273
274 static struct impd1_device impd1_devs[] = {
275         {
276                 .offset = 0x00100000,
277                 .irq    = { 1 },
278                 .id     = 0x00141011,
279         }, {
280                 .offset = 0x00200000,
281                 .irq    = { 2 },
282                 .id     = 0x00141011,
283         }, {
284                 .offset = 0x00300000,
285                 .irq    = { 3 },
286                 .id     = 0x00041022,
287         }, {
288                 .offset = 0x00400000,
289                 .irq    = { 4 },
290                 .id     = 0x00041061,
291         }, {
292                 .offset = 0x00500000,
293                 .irq    = { 5 },
294                 .id     = 0x00041061,
295         }, {
296                 .offset = 0x00600000,
297                 .irq    = { 6 },
298                 .id     = 0x00041130,
299         }, {
300                 .offset = 0x00700000,
301                 .irq    = { 7, 8 },
302                 .id     = 0x00041181,
303                 .platform_data = &mmc_data,
304         }, {
305                 .offset = 0x00800000,
306                 .irq    = { 9 },
307                 .id     = 0x00041041,
308         }, {
309                 .offset = 0x01000000,
310                 .irq    = { 11 },
311                 .id     = 0x00041110,
312                 .platform_data = &impd1_clcd_data,
313         }
314 };
315
316 /*
317  * Valid IRQs: 0 thru 9 and 11, 10 unused.
318  */
319 #define IMPD1_VALID_IRQS 0x00000bffU
320
321 /*
322  * As this module is bool, it is OK to have this as __ref() - no
323  * probe calls will be done after the initial system bootup, as devices
324  * are discovered as part of the machine startup.
325  */
326 static int __ref impd1_probe(struct lm_device *dev)
327 {
328         struct impd1_module *impd1;
329         int irq_base;
330         int i;
331
332         if (dev->id != module_id)
333                 return -EINVAL;
334
335         if (!devm_request_mem_region(&dev->dev, dev->resource.start,
336                                      SZ_4K, "LM registers"))
337                 return -EBUSY;
338
339         impd1 = devm_kzalloc(&dev->dev, sizeof(struct impd1_module),
340                              GFP_KERNEL);
341         if (!impd1)
342                 return -ENOMEM;
343
344         impd1->base = devm_ioremap(&dev->dev, dev->resource.start, SZ_4K);
345         if (!impd1->base)
346                 return -ENOMEM;
347
348         integrator_impd1_clk_init(impd1->base, dev->id);
349
350         if (!devm_request_mem_region(&dev->dev,
351                                      dev->resource.start + 0x03000000,
352                                      SZ_4K, "VIC"))
353                 return -EBUSY;
354
355         impd1->vic_base = devm_ioremap(&dev->dev,
356                                        dev->resource.start + 0x03000000,
357                                        SZ_4K);
358         if (!impd1->vic_base)
359                 return -ENOMEM;
360
361         irq_base = vic_init_cascaded(impd1->vic_base, dev->irq,
362                                      IMPD1_VALID_IRQS, 0);
363
364         lm_set_drvdata(dev, impd1);
365
366         dev_info(&dev->dev, "IM-PD1 found at 0x%08lx\n",
367                  (unsigned long)dev->resource.start);
368
369         for (i = 0; i < ARRAY_SIZE(impd1_devs); i++) {
370                 struct impd1_device *idev = impd1_devs + i;
371                 struct amba_device *d;
372                 unsigned long pc_base;
373                 char devname[32];
374                 int irq1 = idev->irq[0];
375                 int irq2 = idev->irq[1];
376
377                 /* Translate IRQs to IM-PD1 local numberspace */
378                 if (irq1)
379                         irq1 += irq_base;
380                 if (irq2)
381                         irq2 += irq_base;
382
383                 pc_base = dev->resource.start + idev->offset;
384                 snprintf(devname, 32, "lm%x:%5.5lx", dev->id, idev->offset >> 12);
385
386                 /* Add GPIO descriptor lookup table for the PL061 block */
387                 if (idev->offset == 0x00400000) {
388                         struct gpiod_lookup_table *lookup;
389                         char *chipname;
390                         char *mmciname;
391
392                         lookup = devm_kzalloc(&dev->dev,
393                                               struct_size(lookup, table, 3),
394                                               GFP_KERNEL);
395                         chipname = devm_kstrdup(&dev->dev, devname, GFP_KERNEL);
396                         mmciname = devm_kasprintf(&dev->dev, GFP_KERNEL,
397                                                   "lm%x:00700", dev->id);
398                         if (!lookup || !chipname || !mmciname)
399                                 return -ENOMEM;
400
401                         lookup->dev_id = mmciname;
402                         /*
403                          * Offsets on GPIO block 1:
404                          * 3 = MMC WP (write protect)
405                          * 4 = MMC CD (card detect)
406                          *
407                          * Offsets on GPIO block 2:
408                          * 0 = Up key
409                          * 1 = Down key
410                          * 2 = Left key
411                          * 3 = Right key
412                          * 4 = Key lower left
413                          * 5 = Key lower right
414                          */
415                         /* We need the two MMCI GPIO entries */
416                         lookup->table[0].chip_label = chipname;
417                         lookup->table[0].chip_hwnum = 3;
418                         lookup->table[0].con_id = "wp";
419                         lookup->table[1].chip_label = chipname;
420                         lookup->table[1].chip_hwnum = 4;
421                         lookup->table[1].con_id = "cd";
422                         lookup->table[1].flags = GPIO_ACTIVE_LOW;
423                         gpiod_add_lookup_table(lookup);
424                 }
425
426                 d = amba_ahb_device_add_res(&dev->dev, devname, pc_base, SZ_4K,
427                                             irq1, irq2,
428                                             idev->platform_data, idev->id,
429                                             &dev->resource);
430                 if (IS_ERR(d)) {
431                         dev_err(&dev->dev, "unable to register device: %ld\n", PTR_ERR(d));
432                         continue;
433                 }
434         }
435
436         return 0;
437 }
438
439 static int impd1_remove_one(struct device *dev, void *data)
440 {
441         device_unregister(dev);
442         return 0;
443 }
444
445 static void impd1_remove(struct lm_device *dev)
446 {
447         device_for_each_child(&dev->dev, NULL, impd1_remove_one);
448         integrator_impd1_clk_exit(dev->id);
449
450         lm_set_drvdata(dev, NULL);
451 }
452
453 static struct lm_driver impd1_driver = {
454         .drv = {
455                 .name   = "impd1",
456                 /*
457                  * As we're dropping the probe() function, suppress driver
458                  * binding from sysfs.
459                  */
460                 .suppress_bind_attrs = true,
461         },
462         .probe          = impd1_probe,
463         .remove         = impd1_remove,
464 };
465
466 static int __init impd1_init(void)
467 {
468         return lm_driver_register(&impd1_driver);
469 }
470
471 static void __exit impd1_exit(void)
472 {
473         lm_driver_unregister(&impd1_driver);
474 }
475
476 module_init(impd1_init);
477 module_exit(impd1_exit);
478
479 MODULE_LICENSE("GPL");
480 MODULE_DESCRIPTION("Integrator/IM-PD1 logic module core driver");
481 MODULE_AUTHOR("Deep Blue Solutions Ltd");