• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
impd1_tweak_control(struct device * dev,u32 mask,u32 val)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  */
impd1fb_clcd_disable(struct clcd_fb * fb)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  */
impd1fb_clcd_enable(struct clcd_fb * fb)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 
impd1fb_clcd_setup(struct clcd_fb * fb)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 
impd1fb_clcd_mmap(struct clcd_fb * fb,struct vm_area_struct * vma)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 
impd1fb_clcd_remove(struct clcd_fb * fb)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  */
impd1_probe(struct lm_device * dev)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 					      sizeof(*lookup) + 3 * sizeof(struct gpiod_lookup),
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 
impd1_remove_one(struct device * dev,void * data)439 static int impd1_remove_one(struct device *dev, void *data)
440 {
441 	device_unregister(dev);
442 	return 0;
443 }
444 
impd1_remove(struct lm_device * dev)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 
impd1_init(void)466 static int __init impd1_init(void)
467 {
468 	return lm_driver_register(&impd1_driver);
469 }
470 
impd1_exit(void)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");
482