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/slab.h>
25 #include <linux/clkdev.h>
26 
27 #include <asm/hardware/icst.h>
28 #include <mach/lm.h>
29 #include <mach/impd1.h>
30 #include <asm/sizes.h>
31 
32 static int module_id;
33 
34 module_param_named(lmid, module_id, int, 0444);
35 MODULE_PARM_DESC(lmid, "logic module stack position");
36 
37 struct impd1_module {
38 	void __iomem	*base;
39 	struct clk	vcos[2];
40 	struct clk_lookup *clks[3];
41 };
42 
43 static const struct icst_params impd1_vco_params = {
44 	.ref		= 24000000,	/* 24 MHz */
45 	.vco_max	= ICST525_VCO_MAX_3V,
46 	.vco_min	= ICST525_VCO_MIN,
47 	.vd_min		= 12,
48 	.vd_max		= 519,
49 	.rd_min		= 3,
50 	.rd_max		= 120,
51 	.s2div		= icst525_s2div,
52 	.idx2s		= icst525_idx2s,
53 };
54 
impd1_setvco(struct clk * clk,struct icst_vco vco)55 static void impd1_setvco(struct clk *clk, struct icst_vco vco)
56 {
57 	struct impd1_module *impd1 = clk->data;
58 	u32 val = vco.v | (vco.r << 9) | (vco.s << 16);
59 
60 	writel(0xa05f, impd1->base + IMPD1_LOCK);
61 	writel(val, clk->vcoreg);
62 	writel(0, impd1->base + IMPD1_LOCK);
63 
64 #ifdef DEBUG
65 	vco.v = val & 0x1ff;
66 	vco.r = (val >> 9) & 0x7f;
67 	vco.s = (val >> 16) & 7;
68 
69 	pr_debug("IM-PD1: VCO%d clock is %ld Hz\n",
70 		 vconr, icst525_hz(&impd1_vco_params, vco));
71 #endif
72 }
73 
74 static const struct clk_ops impd1_clk_ops = {
75 	.round	= icst_clk_round,
76 	.set	= icst_clk_set,
77 	.setvco	= impd1_setvco,
78 };
79 
impd1_tweak_control(struct device * dev,u32 mask,u32 val)80 void impd1_tweak_control(struct device *dev, u32 mask, u32 val)
81 {
82 	struct impd1_module *impd1 = dev_get_drvdata(dev);
83 	u32 cur;
84 
85 	val &= mask;
86 	cur = readl(impd1->base + IMPD1_CTRL) & ~mask;
87 	writel(cur | val, impd1->base + IMPD1_CTRL);
88 }
89 
90 EXPORT_SYMBOL(impd1_tweak_control);
91 
92 /*
93  * CLCD support
94  */
95 #define PANEL		PROSPECTOR
96 
97 #define LTM10C209		1
98 #define PROSPECTOR		2
99 #define SVGA			3
100 #define VGA			4
101 
102 #if PANEL == VGA
103 #define PANELTYPE	vga
104 static struct clcd_panel vga = {
105 	.mode		= {
106 		.name		= "VGA",
107 		.refresh	= 60,
108 		.xres		= 640,
109 		.yres		= 480,
110 		.pixclock	= 39721,
111 		.left_margin	= 40,
112 		.right_margin	= 24,
113 		.upper_margin	= 32,
114 		.lower_margin	= 11,
115 		.hsync_len	= 96,
116 		.vsync_len	= 2,
117 		.sync		= 0,
118 		.vmode		= FB_VMODE_NONINTERLACED,
119 	},
120 	.width		= -1,
121 	.height		= -1,
122 	.tim2		= TIM2_BCD | TIM2_IPC,
123 	.cntl		= CNTL_LCDTFT | CNTL_LCDVCOMP(1),
124 	.caps		= CLCD_CAP_5551,
125 	.connector	= IMPD1_CTRL_DISP_VGA,
126 	.bpp		= 16,
127 	.grayscale	= 0,
128 };
129 
130 #elif PANEL == SVGA
131 #define PANELTYPE	svga
132 static struct clcd_panel svga = {
133 	.mode		= {
134 		.name		= "SVGA",
135 		.refresh	= 0,
136 		.xres		= 800,
137 		.yres		= 600,
138 		.pixclock	= 27778,
139 		.left_margin	= 20,
140 		.right_margin	= 20,
141 		.upper_margin	= 5,
142 		.lower_margin	= 5,
143 		.hsync_len	= 164,
144 		.vsync_len	= 62,
145 		.sync		= 0,
146 		.vmode		= FB_VMODE_NONINTERLACED,
147 	},
148 	.width		= -1,
149 	.height		= -1,
150 	.tim2		= TIM2_BCD,
151 	.cntl		= CNTL_LCDTFT | CNTL_LCDVCOMP(1),
152 	.connector	= IMPD1_CTRL_DISP_VGA,
153 	.caps		= CLCD_CAP_5551,
154 	.bpp		= 16,
155 	.grayscale	= 0,
156 };
157 
158 #elif PANEL == PROSPECTOR
159 #define PANELTYPE	prospector
160 static struct clcd_panel prospector = {
161 	.mode		= {
162 		.name		= "PROSPECTOR",
163 		.refresh	= 0,
164 		.xres		= 640,
165 		.yres		= 480,
166 		.pixclock	= 40000,
167 		.left_margin	= 33,
168 		.right_margin	= 64,
169 		.upper_margin	= 36,
170 		.lower_margin	= 7,
171 		.hsync_len	= 64,
172 		.vsync_len	= 25,
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 
187 #elif PANEL == LTM10C209
188 #define PANELTYPE	ltm10c209
189 /*
190  * Untested.
191  */
192 static struct clcd_panel ltm10c209 = {
193 	.mode		= {
194 		.name		= "LTM10C209",
195 		.refresh	= 0,
196 		.xres		= 640,
197 		.yres		= 480,
198 		.pixclock	= 40000,
199 		.left_margin	= 20,
200 		.right_margin	= 20,
201 		.upper_margin	= 19,
202 		.lower_margin	= 19,
203 		.hsync_len	= 20,
204 		.vsync_len	= 10,
205 		.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
206 		.vmode		= FB_VMODE_NONINTERLACED,
207 	},
208 	.width		= -1,
209 	.height		= -1,
210 	.tim2		= TIM2_BCD,
211 	.cntl		= CNTL_LCDTFT | CNTL_LCDVCOMP(1),
212 	.caps		= CLCD_CAP_5551,
213 	.fixedtimings	= 1,
214 	.connector	= IMPD1_CTRL_DISP_LCD,
215 	.bpp		= 16,
216 	.grayscale	= 0,
217 };
218 #endif
219 
220 /*
221  * Disable all display connectors on the interface module.
222  */
impd1fb_clcd_disable(struct clcd_fb * fb)223 static void impd1fb_clcd_disable(struct clcd_fb *fb)
224 {
225 	impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK, 0);
226 }
227 
228 /*
229  * Enable the relevant connector on the interface module.
230  */
impd1fb_clcd_enable(struct clcd_fb * fb)231 static void impd1fb_clcd_enable(struct clcd_fb *fb)
232 {
233 	impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK,
234 			fb->panel->connector | IMPD1_CTRL_DISP_ENABLE);
235 }
236 
impd1fb_clcd_setup(struct clcd_fb * fb)237 static int impd1fb_clcd_setup(struct clcd_fb *fb)
238 {
239 	unsigned long framebase = fb->dev->res.start + 0x01000000;
240 	unsigned long framesize = SZ_1M;
241 	int ret = 0;
242 
243 	fb->panel = &PANELTYPE;
244 
245 	if (!request_mem_region(framebase, framesize, "clcd framebuffer")) {
246 		printk(KERN_ERR "IM-PD1: unable to reserve framebuffer\n");
247 		return -EBUSY;
248 	}
249 
250 	fb->fb.screen_base = ioremap(framebase, framesize);
251 	if (!fb->fb.screen_base) {
252 		printk(KERN_ERR "IM-PD1: unable to map framebuffer\n");
253 		ret = -ENOMEM;
254 		goto free_buffer;
255 	}
256 
257 	fb->fb.fix.smem_start	= framebase;
258 	fb->fb.fix.smem_len	= framesize;
259 
260 	return 0;
261 
262  free_buffer:
263 	release_mem_region(framebase, framesize);
264 	return ret;
265 }
266 
impd1fb_clcd_mmap(struct clcd_fb * fb,struct vm_area_struct * vma)267 static int impd1fb_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
268 {
269 	unsigned long start, size;
270 
271 	start = vma->vm_pgoff + (fb->fb.fix.smem_start >> PAGE_SHIFT);
272 	size = vma->vm_end - vma->vm_start;
273 
274 	return remap_pfn_range(vma, vma->vm_start, start, size,
275 			       vma->vm_page_prot);
276 }
277 
impd1fb_clcd_remove(struct clcd_fb * fb)278 static void impd1fb_clcd_remove(struct clcd_fb *fb)
279 {
280 	iounmap(fb->fb.screen_base);
281 	release_mem_region(fb->fb.fix.smem_start, fb->fb.fix.smem_len);
282 }
283 
284 static struct clcd_board impd1_clcd_data = {
285 	.name		= "IM-PD/1",
286 	.caps		= CLCD_CAP_5551 | CLCD_CAP_888,
287 	.check		= clcdfb_check,
288 	.decode		= clcdfb_decode,
289 	.disable	= impd1fb_clcd_disable,
290 	.enable		= impd1fb_clcd_enable,
291 	.setup		= impd1fb_clcd_setup,
292 	.mmap		= impd1fb_clcd_mmap,
293 	.remove		= impd1fb_clcd_remove,
294 };
295 
296 struct impd1_device {
297 	unsigned long	offset;
298 	unsigned int	irq[2];
299 	unsigned int	id;
300 	void		*platform_data;
301 };
302 
303 static struct impd1_device impd1_devs[] = {
304 	{
305 		.offset	= 0x03000000,
306 		.id	= 0x00041190,
307 	}, {
308 		.offset	= 0x00100000,
309 		.irq	= { 1 },
310 		.id	= 0x00141011,
311 	}, {
312 		.offset	= 0x00200000,
313 		.irq	= { 2 },
314 		.id	= 0x00141011,
315 	}, {
316 		.offset	= 0x00300000,
317 		.irq	= { 3 },
318 		.id	= 0x00041022,
319 	}, {
320 		.offset	= 0x00400000,
321 		.irq	= { 4 },
322 		.id	= 0x00041061,
323 	}, {
324 		.offset	= 0x00500000,
325 		.irq	= { 5 },
326 		.id	= 0x00041061,
327 	}, {
328 		.offset	= 0x00600000,
329 		.irq	= { 6 },
330 		.id	= 0x00041130,
331 	}, {
332 		.offset	= 0x00700000,
333 		.irq	= { 7, 8 },
334 		.id	= 0x00041181,
335 	}, {
336 		.offset	= 0x00800000,
337 		.irq	= { 9 },
338 		.id	= 0x00041041,
339 	}, {
340 		.offset	= 0x01000000,
341 		.irq	= { 11 },
342 		.id	= 0x00041110,
343 		.platform_data = &impd1_clcd_data,
344 	}
345 };
346 
347 static struct clk fixed_14745600 = {
348 	.rate = 14745600,
349 };
350 
impd1_probe(struct lm_device * dev)351 static int impd1_probe(struct lm_device *dev)
352 {
353 	struct impd1_module *impd1;
354 	int i, ret;
355 
356 	if (dev->id != module_id)
357 		return -EINVAL;
358 
359 	if (!request_mem_region(dev->resource.start, SZ_4K, "LM registers"))
360 		return -EBUSY;
361 
362 	impd1 = kzalloc(sizeof(struct impd1_module), GFP_KERNEL);
363 	if (!impd1) {
364 		ret = -ENOMEM;
365 		goto release_lm;
366 	}
367 
368 	impd1->base = ioremap(dev->resource.start, SZ_4K);
369 	if (!impd1->base) {
370 		ret = -ENOMEM;
371 		goto free_impd1;
372 	}
373 
374 	lm_set_drvdata(dev, impd1);
375 
376 	printk("IM-PD1 found at 0x%08lx\n",
377 		(unsigned long)dev->resource.start);
378 
379 	for (i = 0; i < ARRAY_SIZE(impd1->vcos); i++) {
380 		impd1->vcos[i].ops = &impd1_clk_ops,
381 		impd1->vcos[i].owner = THIS_MODULE,
382 		impd1->vcos[i].params = &impd1_vco_params,
383 		impd1->vcos[i].data = impd1;
384 	}
385 	impd1->vcos[0].vcoreg = impd1->base + IMPD1_OSC1;
386 	impd1->vcos[1].vcoreg = impd1->base + IMPD1_OSC2;
387 
388 	impd1->clks[0] = clkdev_alloc(&impd1->vcos[0], NULL, "lm%x:01000",
389 					dev->id);
390 	impd1->clks[1] = clkdev_alloc(&fixed_14745600, NULL, "lm%x:00100",
391 					dev->id);
392 	impd1->clks[2] = clkdev_alloc(&fixed_14745600, NULL, "lm%x:00200",
393 					dev->id);
394 	for (i = 0; i < ARRAY_SIZE(impd1->clks); i++)
395 		clkdev_add(impd1->clks[i]);
396 
397 	for (i = 0; i < ARRAY_SIZE(impd1_devs); i++) {
398 		struct impd1_device *idev = impd1_devs + i;
399 		struct amba_device *d;
400 		unsigned long pc_base;
401 
402 		pc_base = dev->resource.start + idev->offset;
403 
404 		d = kzalloc(sizeof(struct amba_device), GFP_KERNEL);
405 		if (!d)
406 			continue;
407 
408 		dev_set_name(&d->dev, "lm%x:%5.5lx", dev->id, idev->offset >> 12);
409 		d->dev.parent	= &dev->dev;
410 		d->res.start	= dev->resource.start + idev->offset;
411 		d->res.end	= d->res.start + SZ_4K - 1;
412 		d->res.flags	= IORESOURCE_MEM;
413 		d->irq[0]	= dev->irq;
414 		d->irq[1]	= dev->irq;
415 		d->periphid	= idev->id;
416 		d->dev.platform_data = idev->platform_data;
417 
418 		ret = amba_device_register(d, &dev->resource);
419 		if (ret) {
420 			dev_err(&d->dev, "unable to register device: %d\n", ret);
421 			kfree(d);
422 		}
423 	}
424 
425 	return 0;
426 
427  free_impd1:
428 	if (impd1 && impd1->base)
429 		iounmap(impd1->base);
430 	kfree(impd1);
431  release_lm:
432 	release_mem_region(dev->resource.start, SZ_4K);
433 	return ret;
434 }
435 
impd1_remove_one(struct device * dev,void * data)436 static int impd1_remove_one(struct device *dev, void *data)
437 {
438 	device_unregister(dev);
439 	return 0;
440 }
441 
impd1_remove(struct lm_device * dev)442 static void impd1_remove(struct lm_device *dev)
443 {
444 	struct impd1_module *impd1 = lm_get_drvdata(dev);
445 	int i;
446 
447 	device_for_each_child(&dev->dev, NULL, impd1_remove_one);
448 
449 	for (i = 0; i < ARRAY_SIZE(impd1->clks); i++)
450 		clkdev_drop(impd1->clks[i]);
451 
452 	lm_set_drvdata(dev, NULL);
453 
454 	iounmap(impd1->base);
455 	kfree(impd1);
456 	release_mem_region(dev->resource.start, SZ_4K);
457 }
458 
459 static struct lm_driver impd1_driver = {
460 	.drv = {
461 		.name	= "impd1",
462 	},
463 	.probe		= impd1_probe,
464 	.remove		= impd1_remove,
465 };
466 
impd1_init(void)467 static int __init impd1_init(void)
468 {
469 	return lm_driver_register(&impd1_driver);
470 }
471 
impd1_exit(void)472 static void __exit impd1_exit(void)
473 {
474 	lm_driver_unregister(&impd1_driver);
475 }
476 
477 module_init(impd1_init);
478 module_exit(impd1_exit);
479 
480 MODULE_LICENSE("GPL");
481 MODULE_DESCRIPTION("Integrator/IM-PD1 logic module core driver");
482 MODULE_AUTHOR("Deep Blue Solutions Ltd");
483