GNU Linux-libre 5.4.274-gnu1
[releases.git] / arch / arm / mach-integrator / impd1.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *  linux/arch/arm/mach-integrator/impd1.c
4  *
5  *  Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.
6  *
7  *  This file provides the core support for the IM-PD1 module.
8  *
9  * Module / boot parameters.
10  *   lmid=n   impd1.lmid=n - set the logic module position in stack to 'n'
11  */
12 #include <linux/module.h>
13 #include <linux/moduleparam.h>
14 #include <linux/init.h>
15 #include <linux/device.h>
16 #include <linux/errno.h>
17 #include <linux/mm.h>
18 #include <linux/amba/bus.h>
19 #include <linux/amba/clcd.h>
20 #include <linux/amba/mmci.h>
21 #include <linux/io.h>
22 #include <linux/platform_data/clk-integrator.h>
23 #include <linux/slab.h>
24 #include <linux/irqchip/arm-vic.h>
25 #include <linux/gpio/machine.h>
26
27 #include <linux/sizes.h>
28 #include "lm.h"
29 #include "impd1.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         void __iomem    *vic_base;
39 };
40
41 void impd1_tweak_control(struct device *dev, u32 mask, u32 val)
42 {
43         struct impd1_module *impd1 = dev_get_drvdata(dev);
44         u32 cur;
45
46         val &= mask;
47         cur = readl(impd1->base + IMPD1_CTRL) & ~mask;
48         writel(cur | val, impd1->base + IMPD1_CTRL);
49 }
50
51 EXPORT_SYMBOL(impd1_tweak_control);
52
53 /*
54  * MMC support
55  */
56 static struct mmci_platform_data mmc_data = {
57         .ocr_mask       = MMC_VDD_32_33|MMC_VDD_33_34,
58 };
59
60 /*
61  * CLCD support
62  */
63 #define PANEL           PROSPECTOR
64
65 #define LTM10C209               1
66 #define PROSPECTOR              2
67 #define SVGA                    3
68 #define VGA                     4
69
70 #if PANEL == VGA
71 #define PANELTYPE       vga
72 static struct clcd_panel vga = {
73         .mode           = {
74                 .name           = "VGA",
75                 .refresh        = 60,
76                 .xres           = 640,
77                 .yres           = 480,
78                 .pixclock       = 39721,
79                 .left_margin    = 40,
80                 .right_margin   = 24,
81                 .upper_margin   = 32,
82                 .lower_margin   = 11,
83                 .hsync_len      = 96,
84                 .vsync_len      = 2,
85                 .sync           = 0,
86                 .vmode          = FB_VMODE_NONINTERLACED,
87         },
88         .width          = -1,
89         .height         = -1,
90         .tim2           = TIM2_BCD | TIM2_IPC,
91         .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
92         .caps           = CLCD_CAP_5551,
93         .connector      = IMPD1_CTRL_DISP_VGA,
94         .bpp            = 16,
95         .grayscale      = 0,
96 };
97
98 #elif PANEL == SVGA
99 #define PANELTYPE       svga
100 static struct clcd_panel svga = {
101         .mode           = {
102                 .name           = "SVGA",
103                 .refresh        = 0,
104                 .xres           = 800,
105                 .yres           = 600,
106                 .pixclock       = 27778,
107                 .left_margin    = 20,
108                 .right_margin   = 20,
109                 .upper_margin   = 5,
110                 .lower_margin   = 5,
111                 .hsync_len      = 164,
112                 .vsync_len      = 62,
113                 .sync           = 0,
114                 .vmode          = FB_VMODE_NONINTERLACED,
115         },
116         .width          = -1,
117         .height         = -1,
118         .tim2           = TIM2_BCD,
119         .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
120         .connector      = IMPD1_CTRL_DISP_VGA,
121         .caps           = CLCD_CAP_5551,
122         .bpp            = 16,
123         .grayscale      = 0,
124 };
125
126 #elif PANEL == PROSPECTOR
127 #define PANELTYPE       prospector
128 static struct clcd_panel prospector = {
129         .mode           = {
130                 .name           = "PROSPECTOR",
131                 .refresh        = 0,
132                 .xres           = 640,
133                 .yres           = 480,
134                 .pixclock       = 40000,
135                 .left_margin    = 33,
136                 .right_margin   = 64,
137                 .upper_margin   = 36,
138                 .lower_margin   = 7,
139                 .hsync_len      = 64,
140                 .vsync_len      = 25,
141                 .sync           = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
142                 .vmode          = FB_VMODE_NONINTERLACED,
143         },
144         .width          = -1,
145         .height         = -1,
146         .tim2           = TIM2_BCD,
147         .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
148         .caps           = CLCD_CAP_5551,
149         .fixedtimings   = 1,
150         .connector      = IMPD1_CTRL_DISP_LCD,
151         .bpp            = 16,
152         .grayscale      = 0,
153 };
154
155 #elif PANEL == LTM10C209
156 #define PANELTYPE       ltm10c209
157 /*
158  * Untested.
159  */
160 static struct clcd_panel ltm10c209 = {
161         .mode           = {
162                 .name           = "LTM10C209",
163                 .refresh        = 0,
164                 .xres           = 640,
165                 .yres           = 480,
166                 .pixclock       = 40000,
167                 .left_margin    = 20,
168                 .right_margin   = 20,
169                 .upper_margin   = 19,
170                 .lower_margin   = 19,
171                 .hsync_len      = 20,
172                 .vsync_len      = 10,
173                 .sync           = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
174                 .vmode          = FB_VMODE_NONINTERLACED,
175         },
176         .width          = -1,
177         .height         = -1,
178         .tim2           = TIM2_BCD,
179         .cntl           = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
180         .caps           = CLCD_CAP_5551,
181         .fixedtimings   = 1,
182         .connector      = IMPD1_CTRL_DISP_LCD,
183         .bpp            = 16,
184         .grayscale      = 0,
185 };
186 #endif
187
188 /*
189  * Disable all display connectors on the interface module.
190  */
191 static void impd1fb_clcd_disable(struct clcd_fb *fb)
192 {
193         impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK, 0);
194 }
195
196 /*
197  * Enable the relevant connector on the interface module.
198  */
199 static void impd1fb_clcd_enable(struct clcd_fb *fb)
200 {
201         impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK,
202                         fb->panel->connector | IMPD1_CTRL_DISP_ENABLE);
203 }
204
205 static int impd1fb_clcd_setup(struct clcd_fb *fb)
206 {
207         unsigned long framebase = fb->dev->res.start + 0x01000000;
208         unsigned long framesize = SZ_1M;
209         int ret = 0;
210
211         fb->panel = &PANELTYPE;
212
213         if (!request_mem_region(framebase, framesize, "clcd framebuffer")) {
214                 printk(KERN_ERR "IM-PD1: unable to reserve framebuffer\n");
215                 return -EBUSY;
216         }
217
218         fb->fb.screen_base = ioremap(framebase, framesize);
219         if (!fb->fb.screen_base) {
220                 printk(KERN_ERR "IM-PD1: unable to map framebuffer\n");
221                 ret = -ENOMEM;
222                 goto free_buffer;
223         }
224
225         fb->fb.fix.smem_start   = framebase;
226         fb->fb.fix.smem_len     = framesize;
227
228         return 0;
229
230  free_buffer:
231         release_mem_region(framebase, framesize);
232         return ret;
233 }
234
235 static int impd1fb_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
236 {
237         unsigned long start, size;
238
239         start = vma->vm_pgoff + (fb->fb.fix.smem_start >> PAGE_SHIFT);
240         size = vma->vm_end - vma->vm_start;
241
242         return remap_pfn_range(vma, vma->vm_start, start, size,
243                                vma->vm_page_prot);
244 }
245
246 static void impd1fb_clcd_remove(struct clcd_fb *fb)
247 {
248         iounmap(fb->fb.screen_base);
249         release_mem_region(fb->fb.fix.smem_start, fb->fb.fix.smem_len);
250 }
251
252 static struct clcd_board impd1_clcd_data = {
253         .name           = "IM-PD/1",
254         .caps           = CLCD_CAP_5551 | CLCD_CAP_888,
255         .check          = clcdfb_check,
256         .decode         = clcdfb_decode,
257         .disable        = impd1fb_clcd_disable,
258         .enable         = impd1fb_clcd_enable,
259         .setup          = impd1fb_clcd_setup,
260         .mmap           = impd1fb_clcd_mmap,
261         .remove         = impd1fb_clcd_remove,
262 };
263
264 struct impd1_device {
265         unsigned long   offset;
266         unsigned int    irq[2];
267         unsigned int    id;
268         void            *platform_data;
269 };
270
271 static struct impd1_device impd1_devs[] = {
272         {
273                 .offset = 0x00100000,
274                 .irq    = { 1 },
275                 .id     = 0x00141011,
276         }, {
277                 .offset = 0x00200000,
278                 .irq    = { 2 },
279                 .id     = 0x00141011,
280         }, {
281                 .offset = 0x00300000,
282                 .irq    = { 3 },
283                 .id     = 0x00041022,
284         }, {
285                 .offset = 0x00400000,
286                 .irq    = { 4 },
287                 .id     = 0x00041061,
288         }, {
289                 .offset = 0x00500000,
290                 .irq    = { 5 },
291                 .id     = 0x00041061,
292         }, {
293                 .offset = 0x00600000,
294                 .irq    = { 6 },
295                 .id     = 0x00041130,
296         }, {
297                 .offset = 0x00700000,
298                 .irq    = { 7, 8 },
299                 .id     = 0x00041181,
300                 .platform_data = &mmc_data,
301         }, {
302                 .offset = 0x00800000,
303                 .irq    = { 9 },
304                 .id     = 0x00041041,
305         }, {
306                 .offset = 0x01000000,
307                 .irq    = { 11 },
308                 .id     = 0x00041110,
309                 .platform_data = &impd1_clcd_data,
310         }
311 };
312
313 /*
314  * Valid IRQs: 0 thru 9 and 11, 10 unused.
315  */
316 #define IMPD1_VALID_IRQS 0x00000bffU
317
318 /*
319  * As this module is bool, it is OK to have this as __ref() - no
320  * probe calls will be done after the initial system bootup, as devices
321  * are discovered as part of the machine startup.
322  */
323 static int __ref impd1_probe(struct lm_device *dev)
324 {
325         struct impd1_module *impd1;
326         int irq_base;
327         int i;
328
329         if (dev->id != module_id)
330                 return -EINVAL;
331
332         if (!devm_request_mem_region(&dev->dev, dev->resource.start,
333                                      SZ_4K, "LM registers"))
334                 return -EBUSY;
335
336         impd1 = devm_kzalloc(&dev->dev, sizeof(struct impd1_module),
337                              GFP_KERNEL);
338         if (!impd1)
339                 return -ENOMEM;
340
341         impd1->base = devm_ioremap(&dev->dev, dev->resource.start, SZ_4K);
342         if (!impd1->base)
343                 return -ENOMEM;
344
345         integrator_impd1_clk_init(impd1->base, dev->id);
346
347         if (!devm_request_mem_region(&dev->dev,
348                                      dev->resource.start + 0x03000000,
349                                      SZ_4K, "VIC"))
350                 return -EBUSY;
351
352         impd1->vic_base = devm_ioremap(&dev->dev,
353                                        dev->resource.start + 0x03000000,
354                                        SZ_4K);
355         if (!impd1->vic_base)
356                 return -ENOMEM;
357
358         irq_base = vic_init_cascaded(impd1->vic_base, dev->irq,
359                                      IMPD1_VALID_IRQS, 0);
360
361         lm_set_drvdata(dev, impd1);
362
363         dev_info(&dev->dev, "IM-PD1 found at 0x%08lx\n",
364                  (unsigned long)dev->resource.start);
365
366         for (i = 0; i < ARRAY_SIZE(impd1_devs); i++) {
367                 struct impd1_device *idev = impd1_devs + i;
368                 struct amba_device *d;
369                 unsigned long pc_base;
370                 char devname[32];
371                 int irq1 = idev->irq[0];
372                 int irq2 = idev->irq[1];
373
374                 /* Translate IRQs to IM-PD1 local numberspace */
375                 if (irq1)
376                         irq1 += irq_base;
377                 if (irq2)
378                         irq2 += irq_base;
379
380                 pc_base = dev->resource.start + idev->offset;
381                 snprintf(devname, 32, "lm%x:%5.5lx", dev->id, idev->offset >> 12);
382
383                 /* Add GPIO descriptor lookup table for the PL061 block */
384                 if (idev->offset == 0x00400000) {
385                         struct gpiod_lookup_table *lookup;
386                         char *chipname;
387                         char *mmciname;
388
389                         lookup = devm_kzalloc(&dev->dev,
390                                               struct_size(lookup, table, 3),
391                                               GFP_KERNEL);
392                         chipname = devm_kstrdup(&dev->dev, devname, GFP_KERNEL);
393                         mmciname = devm_kasprintf(&dev->dev, GFP_KERNEL,
394                                                   "lm%x:00700", dev->id);
395                         if (!lookup || !chipname || !mmciname)
396                                 return -ENOMEM;
397
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");