Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
impd1.c
Go to the documentation of this file.
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 
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 
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 
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,
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,
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  */
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  */
231 static void impd1fb_clcd_enable(struct clcd_fb *fb)
232 {
234  fb->panel->connector | IMPD1_CTRL_DISP_ENABLE);
235 }
236 
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 
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 
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;
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 
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  char devname[32];
402 
403  pc_base = dev->resource.start + idev->offset;
404  snprintf(devname, 32, "lm%x:%5.5lx", dev->id, idev->offset >> 12);
405  d = amba_ahb_device_add(&dev->dev, devname, pc_base, SZ_4K,
406  dev->irq, dev->irq,
407  idev->platform_data, idev->id);
408  if (IS_ERR(d)) {
409  dev_err(&dev->dev, "unable to register device: %ld\n", PTR_ERR(d));
410  continue;
411  }
412  }
413 
414  return 0;
415 
416  free_impd1:
417  if (impd1 && impd1->base)
418  iounmap(impd1->base);
419  kfree(impd1);
420  release_lm:
421  release_mem_region(dev->resource.start, SZ_4K);
422  return ret;
423 }
424 
425 static int impd1_remove_one(struct device *dev, void *data)
426 {
427  device_unregister(dev);
428  return 0;
429 }
430 
431 static void impd1_remove(struct lm_device *dev)
432 {
433  struct impd1_module *impd1 = lm_get_drvdata(dev);
434  int i;
435 
436  device_for_each_child(&dev->dev, NULL, impd1_remove_one);
437 
438  for (i = 0; i < ARRAY_SIZE(impd1->clks); i++)
439  clkdev_drop(impd1->clks[i]);
440 
441  lm_set_drvdata(dev, NULL);
442 
443  iounmap(impd1->base);
444  kfree(impd1);
445  release_mem_region(dev->resource.start, SZ_4K);
446 }
447 
448 static struct lm_driver impd1_driver = {
449  .drv = {
450  .name = "impd1",
451  },
452  .probe = impd1_probe,
453  .remove = impd1_remove,
454 };
455 
456 static int __init impd1_init(void)
457 {
458  return lm_driver_register(&impd1_driver);
459 }
460 
461 static void __exit impd1_exit(void)
462 {
463  lm_driver_unregister(&impd1_driver);
464 }
465 
466 module_init(impd1_init);
467 module_exit(impd1_exit);
468 
469 MODULE_LICENSE("GPL");
470 MODULE_DESCRIPTION("Integrator/IM-PD1 logic module core driver");
471 MODULE_AUTHOR("Deep Blue Solutions Ltd");