aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/512x
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/512x')
-rw-r--r--arch/powerpc/platforms/512x/Kconfig17
-rw-r--r--arch/powerpc/platforms/512x/Makefile4
-rw-r--r--arch/powerpc/platforms/512x/clock.c729
-rw-r--r--arch/powerpc/platforms/512x/mpc5121_ads.c69
-rw-r--r--arch/powerpc/platforms/512x/mpc5121_ads.h16
-rw-r--r--arch/powerpc/platforms/512x/mpc5121_ads_cpld.c204
-rw-r--r--arch/powerpc/platforms/512x/mpc5121_generic.c58
-rw-r--r--arch/powerpc/platforms/512x/mpc512x.h17
-rw-r--r--arch/powerpc/platforms/512x/mpc512x_shared.c83
9 files changed, 1137 insertions, 60 deletions
diff --git a/arch/powerpc/platforms/512x/Kconfig b/arch/powerpc/platforms/512x/Kconfig
index 4c0da0c079e..c62f893ede1 100644
--- a/arch/powerpc/platforms/512x/Kconfig
+++ b/arch/powerpc/platforms/512x/Kconfig
@@ -2,18 +2,29 @@ config PPC_MPC512x
2 bool 2 bool
3 select FSL_SOC 3 select FSL_SOC
4 select IPIC 4 select IPIC
5 default n 5 select PPC_CLOCK
6 6
7config PPC_MPC5121 7config PPC_MPC5121
8 bool 8 bool
9 select PPC_MPC512x 9 select PPC_MPC512x
10 default n
11 10
12config MPC5121_ADS 11config MPC5121_ADS
13 bool "Freescale MPC5121E ADS" 12 bool "Freescale MPC5121E ADS"
14 depends on PPC_MULTIPLATFORM && PPC32 13 depends on PPC_MULTIPLATFORM && PPC32
15 select DEFAULT_UIMAGE 14 select DEFAULT_UIMAGE
16 select PPC_MPC5121 15 select PPC_MPC5121
16 select MPC5121_ADS_CPLD
17 help 17 help
18 This option enables support for the MPC5121E ADS board. 18 This option enables support for the MPC5121E ADS board.
19 default n 19
20config MPC5121_GENERIC
21 bool "Generic support for simple MPC5121 based boards"
22 depends on PPC_MULTIPLATFORM && PPC32
23 select DEFAULT_UIMAGE
24 select PPC_MPC5121
25 help
26 This option enables support for simple MPC5121 based boards
27 which do not need custom platform specific setup.
28
29 Compatible boards include: Protonic LVT base boards (ZANMCU
30 and VICVT2).
diff --git a/arch/powerpc/platforms/512x/Makefile b/arch/powerpc/platforms/512x/Makefile
index 232c89f2039..90be2f5717e 100644
--- a/arch/powerpc/platforms/512x/Makefile
+++ b/arch/powerpc/platforms/512x/Makefile
@@ -1,4 +1,6 @@
1# 1#
2# Makefile for the Freescale PowerPC 512x linux kernel. 2# Makefile for the Freescale PowerPC 512x linux kernel.
3# 3#
4obj-$(CONFIG_MPC5121_ADS) += mpc5121_ads.o 4obj-y += clock.o mpc512x_shared.o
5obj-$(CONFIG_MPC5121_ADS) += mpc5121_ads.o mpc5121_ads_cpld.o
6obj-$(CONFIG_MPC5121_GENERIC) += mpc5121_generic.o
diff --git a/arch/powerpc/platforms/512x/clock.c b/arch/powerpc/platforms/512x/clock.c
new file mode 100644
index 00000000000..f416014ee72
--- /dev/null
+++ b/arch/powerpc/platforms/512x/clock.c
@@ -0,0 +1,729 @@
1/*
2 * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. All rights reserved.
3 *
4 * Author: John Rigby <jrigby@freescale.com>
5 *
6 * Implements the clk api defined in include/linux/clk.h
7 *
8 * Original based on linux/arch/arm/mach-integrator/clock.c
9 *
10 * Copyright (C) 2004 ARM Limited.
11 * Written by Deep Blue Solutions Limited.
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2 as
15 * published by the Free Software Foundation.
16 */
17#include <linux/kernel.h>
18#include <linux/list.h>
19#include <linux/errno.h>
20#include <linux/err.h>
21#include <linux/string.h>
22#include <linux/clk.h>
23#include <linux/mutex.h>
24#include <linux/io.h>
25
26#include <linux/of_platform.h>
27#include <asm/mpc512x.h>
28#include <asm/clk_interface.h>
29
30#undef CLK_DEBUG
31
32static int clocks_initialized;
33
34#define CLK_HAS_RATE 0x1 /* has rate in MHz */
35#define CLK_HAS_CTRL 0x2 /* has control reg and bit */
36
37struct clk {
38 struct list_head node;
39 char name[32];
40 int flags;
41 struct device *dev;
42 unsigned long rate;
43 struct module *owner;
44 void (*calc) (struct clk *);
45 struct clk *parent;
46 int reg, bit; /* CLK_HAS_CTRL */
47 int div_shift; /* only used by generic_div_clk_calc */
48};
49
50static LIST_HEAD(clocks);
51static DEFINE_MUTEX(clocks_mutex);
52
53static struct clk *mpc5121_clk_get(struct device *dev, const char *id)
54{
55 struct clk *p, *clk = ERR_PTR(-ENOENT);
56 int dev_match = 0;
57 int id_match = 0;
58
59 if (dev == NULL && id == NULL)
60 return NULL;
61
62 mutex_lock(&clocks_mutex);
63 list_for_each_entry(p, &clocks, node) {
64 if (dev && dev == p->dev)
65 dev_match++;
66 if (strcmp(id, p->name) == 0)
67 id_match++;
68 if ((dev_match || id_match) && try_module_get(p->owner)) {
69 clk = p;
70 break;
71 }
72 }
73 mutex_unlock(&clocks_mutex);
74
75 return clk;
76}
77
78#ifdef CLK_DEBUG
79static void dump_clocks(void)
80{
81 struct clk *p;
82
83 mutex_lock(&clocks_mutex);
84 printk(KERN_INFO "CLOCKS:\n");
85 list_for_each_entry(p, &clocks, node) {
86 printk(KERN_INFO " %s %ld", p->name, p->rate);
87 if (p->parent)
88 printk(KERN_INFO " %s %ld", p->parent->name,
89 p->parent->rate);
90 if (p->flags & CLK_HAS_CTRL)
91 printk(KERN_INFO " reg/bit %d/%d", p->reg, p->bit);
92 printk("\n");
93 }
94 mutex_unlock(&clocks_mutex);
95}
96#define DEBUG_CLK_DUMP() dump_clocks()
97#else
98#define DEBUG_CLK_DUMP()
99#endif
100
101
102static void mpc5121_clk_put(struct clk *clk)
103{
104 module_put(clk->owner);
105}
106
107#define NRPSC 12
108
109struct mpc512x_clockctl {
110 u32 spmr; /* System PLL Mode Reg */
111 u32 sccr[2]; /* System Clk Ctrl Reg 1 & 2 */
112 u32 scfr1; /* System Clk Freq Reg 1 */
113 u32 scfr2; /* System Clk Freq Reg 2 */
114 u32 reserved;
115 u32 bcr; /* Bread Crumb Reg */
116 u32 pccr[NRPSC]; /* PSC Clk Ctrl Reg 0-11 */
117 u32 spccr; /* SPDIF Clk Ctrl Reg */
118 u32 cccr; /* CFM Clk Ctrl Reg */
119 u32 dccr; /* DIU Clk Cnfg Reg */
120};
121
122struct mpc512x_clockctl __iomem *clockctl;
123
124static int mpc5121_clk_enable(struct clk *clk)
125{
126 unsigned int mask;
127
128 if (clk->flags & CLK_HAS_CTRL) {
129 mask = in_be32(&clockctl->sccr[clk->reg]);
130 mask |= 1 << clk->bit;
131 out_be32(&clockctl->sccr[clk->reg], mask);
132 }
133 return 0;
134}
135
136static void mpc5121_clk_disable(struct clk *clk)
137{
138 unsigned int mask;
139
140 if (clk->flags & CLK_HAS_CTRL) {
141 mask = in_be32(&clockctl->sccr[clk->reg]);
142 mask &= ~(1 << clk->bit);
143 out_be32(&clockctl->sccr[clk->reg], mask);
144 }
145}
146
147static unsigned long mpc5121_clk_get_rate(struct clk *clk)
148{
149 if (clk->flags & CLK_HAS_RATE)
150 return clk->rate;
151 else
152 return 0;
153}
154
155static long mpc5121_clk_round_rate(struct clk *clk, unsigned long rate)
156{
157 return rate;
158}
159
160static int mpc5121_clk_set_rate(struct clk *clk, unsigned long rate)
161{
162 return 0;
163}
164
165static int clk_register(struct clk *clk)
166{
167 mutex_lock(&clocks_mutex);
168 list_add(&clk->node, &clocks);
169 mutex_unlock(&clocks_mutex);
170 return 0;
171}
172
173static unsigned long spmf_mult(void)
174{
175 /*
176 * Convert spmf to multiplier
177 */
178 static int spmf_to_mult[] = {
179 68, 1, 12, 16,
180 20, 24, 28, 32,
181 36, 40, 44, 48,
182 52, 56, 60, 64
183 };
184 int spmf = (clockctl->spmr >> 24) & 0xf;
185 return spmf_to_mult[spmf];
186}
187
188static unsigned long sysdiv_div_x_2(void)
189{
190 /*
191 * Convert sysdiv to divisor x 2
192 * Some divisors have fractional parts so
193 * multiply by 2 then divide by this value
194 */
195 static int sysdiv_to_div_x_2[] = {
196 4, 5, 6, 7,
197 8, 9, 10, 14,
198 12, 16, 18, 22,
199 20, 24, 26, 30,
200 28, 32, 34, 38,
201 36, 40, 42, 46,
202 44, 48, 50, 54,
203 52, 56, 58, 62,
204 60, 64, 66,
205 };
206 int sysdiv = (clockctl->scfr2 >> 26) & 0x3f;
207 return sysdiv_to_div_x_2[sysdiv];
208}
209
210static unsigned long ref_to_sys(unsigned long rate)
211{
212 rate *= spmf_mult();
213 rate *= 2;
214 rate /= sysdiv_div_x_2();
215
216 return rate;
217}
218
219static unsigned long sys_to_ref(unsigned long rate)
220{
221 rate *= sysdiv_div_x_2();
222 rate /= 2;
223 rate /= spmf_mult();
224
225 return rate;
226}
227
228static long ips_to_ref(unsigned long rate)
229{
230 int ips_div = (clockctl->scfr1 >> 23) & 0x7;
231
232 rate *= ips_div; /* csb_clk = ips_clk * ips_div */
233 rate *= 2; /* sys_clk = csb_clk * 2 */
234 return sys_to_ref(rate);
235}
236
237static unsigned long devtree_getfreq(char *clockname)
238{
239 struct device_node *np;
240 const unsigned int *prop;
241 unsigned int val = 0;
242
243 np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-immr");
244 if (np) {
245 prop = of_get_property(np, clockname, NULL);
246 if (prop)
247 val = *prop;
248 of_node_put(np);
249 }
250 return val;
251}
252
253static void ref_clk_calc(struct clk *clk)
254{
255 unsigned long rate;
256
257 rate = devtree_getfreq("bus-frequency");
258 if (rate == 0) {
259 printk(KERN_ERR "No bus-frequency in dev tree\n");
260 clk->rate = 0;
261 return;
262 }
263 clk->rate = ips_to_ref(rate);
264}
265
266static struct clk ref_clk = {
267 .name = "ref_clk",
268 .calc = ref_clk_calc,
269};
270
271
272static void sys_clk_calc(struct clk *clk)
273{
274 clk->rate = ref_to_sys(ref_clk.rate);
275}
276
277static struct clk sys_clk = {
278 .name = "sys_clk",
279 .calc = sys_clk_calc,
280};
281
282static void diu_clk_calc(struct clk *clk)
283{
284 int diudiv_x_2 = clockctl->scfr1 & 0xff;
285 unsigned long rate;
286
287 rate = sys_clk.rate;
288
289 rate *= 2;
290 rate /= diudiv_x_2;
291
292 clk->rate = rate;
293}
294
295static void half_clk_calc(struct clk *clk)
296{
297 clk->rate = clk->parent->rate / 2;
298}
299
300static void generic_div_clk_calc(struct clk *clk)
301{
302 int div = (clockctl->scfr1 >> clk->div_shift) & 0x7;
303
304 clk->rate = clk->parent->rate / div;
305}
306
307static void unity_clk_calc(struct clk *clk)
308{
309 clk->rate = clk->parent->rate;
310}
311
312static struct clk csb_clk = {
313 .name = "csb_clk",
314 .calc = half_clk_calc,
315 .parent = &sys_clk,
316};
317
318static void e300_clk_calc(struct clk *clk)
319{
320 int spmf = (clockctl->spmr >> 16) & 0xf;
321 int ratex2 = clk->parent->rate * spmf;
322
323 clk->rate = ratex2 / 2;
324}
325
326static struct clk e300_clk = {
327 .name = "e300_clk",
328 .calc = e300_clk_calc,
329 .parent = &csb_clk,
330};
331
332static struct clk ips_clk = {
333 .name = "ips_clk",
334 .calc = generic_div_clk_calc,
335 .parent = &csb_clk,
336 .div_shift = 23,
337};
338
339/*
340 * Clocks controlled by SCCR1 (.reg = 0)
341 */
342static struct clk lpc_clk = {
343 .name = "lpc_clk",
344 .flags = CLK_HAS_CTRL,
345 .reg = 0,
346 .bit = 30,
347 .calc = generic_div_clk_calc,
348 .parent = &ips_clk,
349 .div_shift = 11,
350};
351
352static struct clk nfc_clk = {
353 .name = "nfc_clk",
354 .flags = CLK_HAS_CTRL,
355 .reg = 0,
356 .bit = 29,
357 .calc = generic_div_clk_calc,
358 .parent = &ips_clk,
359 .div_shift = 8,
360};
361
362static struct clk pata_clk = {
363 .name = "pata_clk",
364 .flags = CLK_HAS_CTRL,
365 .reg = 0,
366 .bit = 28,
367 .calc = unity_clk_calc,
368 .parent = &ips_clk,
369};
370
371/*
372 * PSC clocks (bits 27 - 16)
373 * are setup elsewhere
374 */
375
376static struct clk sata_clk = {
377 .name = "sata_clk",
378 .flags = CLK_HAS_CTRL,
379 .reg = 0,
380 .bit = 14,
381 .calc = unity_clk_calc,
382 .parent = &ips_clk,
383};
384
385static struct clk fec_clk = {
386 .name = "fec_clk",
387 .flags = CLK_HAS_CTRL,
388 .reg = 0,
389 .bit = 13,
390 .calc = unity_clk_calc,
391 .parent = &ips_clk,
392};
393
394static struct clk pci_clk = {
395 .name = "pci_clk",
396 .flags = CLK_HAS_CTRL,
397 .reg = 0,
398 .bit = 11,
399 .calc = generic_div_clk_calc,
400 .parent = &csb_clk,
401 .div_shift = 20,
402};
403
404/*
405 * Clocks controlled by SCCR2 (.reg = 1)
406 */
407static struct clk diu_clk = {
408 .name = "diu_clk",
409 .flags = CLK_HAS_CTRL,
410 .reg = 1,
411 .bit = 31,
412 .calc = diu_clk_calc,
413};
414
415static struct clk axe_clk = {
416 .name = "axe_clk",
417 .flags = CLK_HAS_CTRL,
418 .reg = 1,
419 .bit = 30,
420 .calc = unity_clk_calc,
421 .parent = &csb_clk,
422};
423
424static struct clk usb1_clk = {
425 .name = "usb1_clk",
426 .flags = CLK_HAS_CTRL,
427 .reg = 1,
428 .bit = 28,
429 .calc = unity_clk_calc,
430 .parent = &csb_clk,
431};
432
433static struct clk usb2_clk = {
434 .name = "usb2_clk",
435 .flags = CLK_HAS_CTRL,
436 .reg = 1,
437 .bit = 27,
438 .calc = unity_clk_calc,
439 .parent = &csb_clk,
440};
441
442static struct clk i2c_clk = {
443 .name = "i2c_clk",
444 .flags = CLK_HAS_CTRL,
445 .reg = 1,
446 .bit = 26,
447 .calc = unity_clk_calc,
448 .parent = &ips_clk,
449};
450
451static struct clk mscan_clk = {
452 .name = "mscan_clk",
453 .flags = CLK_HAS_CTRL,
454 .reg = 1,
455 .bit = 25,
456 .calc = unity_clk_calc,
457 .parent = &ips_clk,
458};
459
460static struct clk sdhc_clk = {
461 .name = "sdhc_clk",
462 .flags = CLK_HAS_CTRL,
463 .reg = 1,
464 .bit = 24,
465 .calc = unity_clk_calc,
466 .parent = &ips_clk,
467};
468
469static struct clk mbx_bus_clk = {
470 .name = "mbx_bus_clk",
471 .flags = CLK_HAS_CTRL,
472 .reg = 1,
473 .bit = 22,
474 .calc = half_clk_calc,
475 .parent = &csb_clk,
476};
477
478static struct clk mbx_clk = {
479 .name = "mbx_clk",
480 .flags = CLK_HAS_CTRL,
481 .reg = 1,
482 .bit = 21,
483 .calc = unity_clk_calc,
484 .parent = &csb_clk,
485};
486
487static struct clk mbx_3d_clk = {
488 .name = "mbx_3d_clk",
489 .flags = CLK_HAS_CTRL,
490 .reg = 1,
491 .bit = 20,
492 .calc = generic_div_clk_calc,
493 .parent = &mbx_bus_clk,
494 .div_shift = 14,
495};
496
497static void psc_mclk_in_calc(struct clk *clk)
498{
499 clk->rate = devtree_getfreq("psc_mclk_in");
500 if (!clk->rate)
501 clk->rate = 25000000;
502}
503
504static struct clk psc_mclk_in = {
505 .name = "psc_mclk_in",
506 .calc = psc_mclk_in_calc,
507};
508
509static struct clk spdif_txclk = {
510 .name = "spdif_txclk",
511 .flags = CLK_HAS_CTRL,
512 .reg = 1,
513 .bit = 23,
514};
515
516static struct clk spdif_rxclk = {
517 .name = "spdif_rxclk",
518 .flags = CLK_HAS_CTRL,
519 .reg = 1,
520 .bit = 23,
521};
522
523static void ac97_clk_calc(struct clk *clk)
524{
525 /* ac97 bit clock is always 24.567 MHz */
526 clk->rate = 24567000;
527}
528
529static struct clk ac97_clk = {
530 .name = "ac97_clk_in",
531 .calc = ac97_clk_calc,
532};
533
534struct clk *rate_clks[] = {
535 &ref_clk,
536 &sys_clk,
537 &diu_clk,
538 &csb_clk,
539 &e300_clk,
540 &ips_clk,
541 &fec_clk,
542 &sata_clk,
543 &pata_clk,
544 &nfc_clk,
545 &lpc_clk,
546 &mbx_bus_clk,
547 &mbx_clk,
548 &mbx_3d_clk,
549 &axe_clk,
550 &usb1_clk,
551 &usb2_clk,
552 &i2c_clk,
553 &mscan_clk,
554 &sdhc_clk,
555 &pci_clk,
556 &psc_mclk_in,
557 &spdif_txclk,
558 &spdif_rxclk,
559 &ac97_clk,
560 NULL
561};
562
563static void rate_clk_init(struct clk *clk)
564{
565 if (clk->calc) {
566 clk->calc(clk);
567 clk->flags |= CLK_HAS_RATE;
568 clk_register(clk);
569 } else {
570 printk(KERN_WARNING
571 "Could not initialize clk %s without a calc routine\n",
572 clk->name);
573 }
574}
575
576static void rate_clks_init(void)
577{
578 struct clk **cpp, *clk;
579
580 cpp = rate_clks;
581 while ((clk = *cpp++))
582 rate_clk_init(clk);
583}
584
585/*
586 * There are two clk enable registers with 32 enable bits each
587 * psc clocks and device clocks are all stored in dev_clks
588 */
589struct clk dev_clks[2][32];
590
591/*
592 * Given a psc number return the dev_clk
593 * associated with it
594 */
595static struct clk *psc_dev_clk(int pscnum)
596{
597 int reg, bit;
598 struct clk *clk;
599
600 reg = 0;
601 bit = 27 - pscnum;
602
603 clk = &dev_clks[reg][bit];
604 clk->reg = 0;
605 clk->bit = bit;
606 return clk;
607}
608
609/*
610 * PSC clock rate calculation
611 */
612static void psc_calc_rate(struct clk *clk, int pscnum, struct device_node *np)
613{
614 unsigned long mclk_src = sys_clk.rate;
615 unsigned long mclk_div;
616
617 /*
618 * Can only change value of mclk divider
619 * when the divider is disabled.
620 *
621 * Zero is not a valid divider so minimum
622 * divider is 1
623 *
624 * disable/set divider/enable
625 */
626 out_be32(&clockctl->pccr[pscnum], 0);
627 out_be32(&clockctl->pccr[pscnum], 0x00020000);
628 out_be32(&clockctl->pccr[pscnum], 0x00030000);
629
630 if (clockctl->pccr[pscnum] & 0x80) {
631 clk->rate = spdif_rxclk.rate;
632 return;
633 }
634
635 switch ((clockctl->pccr[pscnum] >> 14) & 0x3) {
636 case 0:
637 mclk_src = sys_clk.rate;
638 break;
639 case 1:
640 mclk_src = ref_clk.rate;
641 break;
642 case 2:
643 mclk_src = psc_mclk_in.rate;
644 break;
645 case 3:
646 mclk_src = spdif_txclk.rate;
647 break;
648 }
649
650 mclk_div = ((clockctl->pccr[pscnum] >> 17) & 0x7fff) + 1;
651 clk->rate = mclk_src / mclk_div;
652}
653
654/*
655 * Find all psc nodes in device tree and assign a clock
656 * with name "psc%d_mclk" and dev pointing at the device
657 * returned from of_find_device_by_node
658 */
659static void psc_clks_init(void)
660{
661 struct device_node *np;
662 const u32 *cell_index;
663 struct of_device *ofdev;
664
665 for_each_compatible_node(np, NULL, "fsl,mpc5121-psc") {
666 cell_index = of_get_property(np, "cell-index", NULL);
667 if (cell_index) {
668 int pscnum = *cell_index;
669 struct clk *clk = psc_dev_clk(pscnum);
670
671 clk->flags = CLK_HAS_RATE | CLK_HAS_CTRL;
672 ofdev = of_find_device_by_node(np);
673 clk->dev = &ofdev->dev;
674 /*
675 * AC97 is special rate clock does
676 * not go through normal path
677 */
678 if (strcmp("ac97", np->name) == 0)
679 clk->rate = ac97_clk.rate;
680 else
681 psc_calc_rate(clk, pscnum, np);
682 sprintf(clk->name, "psc%d_mclk", pscnum);
683 clk_register(clk);
684 clk_enable(clk);
685 }
686 }
687}
688
689static struct clk_interface mpc5121_clk_functions = {
690 .clk_get = mpc5121_clk_get,
691 .clk_enable = mpc5121_clk_enable,
692 .clk_disable = mpc5121_clk_disable,
693 .clk_get_rate = mpc5121_clk_get_rate,
694 .clk_put = mpc5121_clk_put,
695 .clk_round_rate = mpc5121_clk_round_rate,
696 .clk_set_rate = mpc5121_clk_set_rate,
697 .clk_set_parent = NULL,
698 .clk_get_parent = NULL,
699};
700
701static int
702mpc5121_clk_init(void)
703{
704 struct device_node *np;
705
706 np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-clock");
707 if (np) {
708 clockctl = of_iomap(np, 0);
709 of_node_put(np);
710 }
711
712 if (!clockctl) {
713 printk(KERN_ERR "Could not map clock control registers\n");
714 return 0;
715 }
716
717 rate_clks_init();
718 psc_clks_init();
719
720 /* leave clockctl mapped forever */
721 /*iounmap(clockctl); */
722 DEBUG_CLK_DUMP();
723 clocks_initialized++;
724 clk_functions = mpc5121_clk_functions;
725 return 0;
726}
727
728
729arch_initcall(mpc5121_clk_init);
diff --git a/arch/powerpc/platforms/512x/mpc5121_ads.c b/arch/powerpc/platforms/512x/mpc5121_ads.c
index 50bd3a31902..5ebf6939a69 100644
--- a/arch/powerpc/platforms/512x/mpc5121_ads.c
+++ b/arch/powerpc/platforms/512x/mpc5121_ads.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. 2 * Copyright (C) 2007, 2008 Freescale Semiconductor, Inc. All rights reserved.
3 * 3 *
4 * Author: John Rigby, <jrigby@freescale.com>, Thur Mar 29 2007 4 * Author: John Rigby, <jrigby@freescale.com>, Thur Mar 29 2007
5 * 5 *
@@ -15,7 +15,6 @@
15 15
16#include <linux/kernel.h> 16#include <linux/kernel.h>
17#include <linux/io.h> 17#include <linux/io.h>
18#include <linux/irq.h>
19#include <linux/of_platform.h> 18#include <linux/of_platform.h>
20 19
21#include <asm/machdep.h> 20#include <asm/machdep.h>
@@ -23,65 +22,22 @@
23#include <asm/prom.h> 22#include <asm/prom.h>
24#include <asm/time.h> 23#include <asm/time.h>
25 24
26/** 25#include "mpc512x.h"
27 * mpc512x_find_ips_freq - Find the IPS bus frequency for a device 26#include "mpc5121_ads.h"
28 * @node: device node
29 *
30 * Returns IPS bus frequency, or 0 if the bus frequency cannot be found.
31 */
32unsigned long
33mpc512x_find_ips_freq(struct device_node *node)
34{
35 struct device_node *np;
36 const unsigned int *p_ips_freq = NULL;
37
38 of_node_get(node);
39 while (node) {
40 p_ips_freq = of_get_property(node, "bus-frequency", NULL);
41 if (p_ips_freq)
42 break;
43
44 np = of_get_parent(node);
45 of_node_put(node);
46 node = np;
47 }
48 if (node)
49 of_node_put(node);
50
51 return p_ips_freq ? *p_ips_freq : 0;
52}
53EXPORT_SYMBOL(mpc512x_find_ips_freq);
54
55static struct of_device_id __initdata of_bus_ids[] = {
56 { .name = "soc", },
57 { .name = "localbus", },
58 {},
59};
60 27
61static void __init mpc5121_ads_declare_of_platform_devices(void) 28static void __init mpc5121_ads_setup_arch(void)
62{ 29{
63 /* Find every child of the SOC node and add it to of_platform */ 30 printk(KERN_INFO "MPC5121 ADS board from Freescale Semiconductor\n");
64 if (of_platform_bus_probe(NULL, of_bus_ids, NULL)) 31 /*
65 printk(KERN_ERR __FILE__ ": " 32 * cpld regs are needed early
66 "Error while probing of_platform bus\n"); 33 */
34 mpc5121_ads_cpld_map();
67} 35}
68 36
69static void __init mpc5121_ads_init_IRQ(void) 37static void __init mpc5121_ads_init_IRQ(void)
70{ 38{
71 struct device_node *np; 39 mpc512x_init_IRQ();
72 40 mpc5121_ads_cpld_pic_init();
73 np = of_find_compatible_node(NULL, NULL, "fsl,ipic");
74 if (!np)
75 return;
76
77 ipic_init(np, 0);
78 of_node_put(np);
79
80 /*
81 * Initialize the default interrupt mapping priorities,
82 * in case the boot rom changed something on us.
83 */
84 ipic_set_default_priority();
85} 41}
86 42
87/* 43/*
@@ -97,7 +53,8 @@ static int __init mpc5121_ads_probe(void)
97define_machine(mpc5121_ads) { 53define_machine(mpc5121_ads) {
98 .name = "MPC5121 ADS", 54 .name = "MPC5121 ADS",
99 .probe = mpc5121_ads_probe, 55 .probe = mpc5121_ads_probe,
100 .init = mpc5121_ads_declare_of_platform_devices, 56 .setup_arch = mpc5121_ads_setup_arch,
57 .init = mpc512x_declare_of_platform_devices,
101 .init_IRQ = mpc5121_ads_init_IRQ, 58 .init_IRQ = mpc5121_ads_init_IRQ,
102 .get_irq = ipic_get_irq, 59 .get_irq = ipic_get_irq,
103 .calibrate_decr = generic_calibrate_decr, 60 .calibrate_decr = generic_calibrate_decr,
diff --git a/arch/powerpc/platforms/512x/mpc5121_ads.h b/arch/powerpc/platforms/512x/mpc5121_ads.h
new file mode 100644
index 00000000000..662076cfee2
--- /dev/null
+++ b/arch/powerpc/platforms/512x/mpc5121_ads.h
@@ -0,0 +1,16 @@
1/*
2 * Copyright (C) 2008 Freescale Semiconductor, Inc. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * Prototypes for ADS5121 specific code
10 */
11
12#ifndef __MPC512ADS_H__
13#define __MPC512ADS_H__
14extern void __init mpc5121_ads_cpld_map(void);
15extern void __init mpc5121_ads_cpld_pic_init(void);
16#endif /* __MPC512ADS_H__ */
diff --git a/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c b/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c
new file mode 100644
index 00000000000..a6ce8056662
--- /dev/null
+++ b/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c
@@ -0,0 +1,204 @@
1/*
2 * Copyright (C) 2008 Freescale Semiconductor, Inc. All rights reserved.
3 *
4 * Author: John Rigby, <jrigby@freescale.com>
5 *
6 * Description:
7 * MPC5121ADS CPLD irq handling
8 *
9 * This is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 */
14
15#undef DEBUG
16
17#include <linux/kernel.h>
18#include <linux/interrupt.h>
19#include <linux/irq.h>
20#include <linux/io.h>
21#include <asm/prom.h>
22
23static struct device_node *cpld_pic_node;
24static struct irq_host *cpld_pic_host;
25
26/*
27 * Bits to ignore in the misc_status register
28 * 0x10 touch screen pendown is hard routed to irq1
29 * 0x02 pci status is read from pci status register
30 */
31#define MISC_IGNORE 0x12
32
33/*
34 * Nothing to ignore in pci status register
35 */
36#define PCI_IGNORE 0x00
37
38struct cpld_pic {
39 u8 pci_mask;
40 u8 pci_status;
41 u8 route;
42 u8 misc_mask;
43 u8 misc_status;
44 u8 misc_control;
45};
46
47static struct cpld_pic __iomem *cpld_regs;
48
49static void __iomem *
50irq_to_pic_mask(unsigned int irq)
51{
52 return irq <= 7 ? &cpld_regs->pci_mask : &cpld_regs->misc_mask;
53}
54
55static unsigned int
56irq_to_pic_bit(unsigned int irq)
57{
58 return 1 << (irq & 0x7);
59}
60
61static void
62cpld_mask_irq(unsigned int irq)
63{
64 unsigned int cpld_irq = (unsigned int)irq_map[irq].hwirq;
65 void __iomem *pic_mask = irq_to_pic_mask(cpld_irq);
66
67 out_8(pic_mask,
68 in_8(pic_mask) | irq_to_pic_bit(cpld_irq));
69}
70
71static void
72cpld_unmask_irq(unsigned int irq)
73{
74 unsigned int cpld_irq = (unsigned int)irq_map[irq].hwirq;
75 void __iomem *pic_mask = irq_to_pic_mask(cpld_irq);
76
77 out_8(pic_mask,
78 in_8(pic_mask) & ~irq_to_pic_bit(cpld_irq));
79}
80
81static struct irq_chip cpld_pic = {
82 .typename = " CPLD PIC ",
83 .mask = cpld_mask_irq,
84 .ack = cpld_mask_irq,
85 .unmask = cpld_unmask_irq,
86};
87
88static int
89cpld_pic_get_irq(int offset, u8 ignore, u8 __iomem *statusp,
90 u8 __iomem *maskp)
91{
92 int cpld_irq;
93 u8 status = in_8(statusp);
94 u8 mask = in_8(maskp);
95
96 /* ignore don't cares and masked irqs */
97 status |= (ignore | mask);
98
99 if (status == 0xff)
100 return NO_IRQ_IGNORE;
101
102 cpld_irq = ffz(status) + offset;
103
104 return irq_linear_revmap(cpld_pic_host, cpld_irq);
105}
106
107static void
108cpld_pic_cascade(unsigned int irq, struct irq_desc *desc)
109{
110 irq = cpld_pic_get_irq(0, PCI_IGNORE, &cpld_regs->pci_status,
111 &cpld_regs->pci_mask);
112 if (irq != NO_IRQ && irq != NO_IRQ_IGNORE) {
113 generic_handle_irq(irq);
114 return;
115 }
116
117 irq = cpld_pic_get_irq(8, MISC_IGNORE, &cpld_regs->misc_status,
118 &cpld_regs->misc_mask);
119 if (irq != NO_IRQ && irq != NO_IRQ_IGNORE) {
120 generic_handle_irq(irq);
121 return;
122 }
123}
124
125static int
126cpld_pic_host_match(struct irq_host *h, struct device_node *node)
127{
128 return cpld_pic_node == node;
129}
130
131static int
132cpld_pic_host_map(struct irq_host *h, unsigned int virq,
133 irq_hw_number_t hw)
134{
135 get_irq_desc(virq)->status |= IRQ_LEVEL;
136 set_irq_chip_and_handler(virq, &cpld_pic, handle_level_irq);
137 return 0;
138}
139
140static struct
141irq_host_ops cpld_pic_host_ops = {
142 .match = cpld_pic_host_match,
143 .map = cpld_pic_host_map,
144};
145
146void __init
147mpc5121_ads_cpld_map(void)
148{
149 struct device_node *np = NULL;
150
151 np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld-pic");
152 if (!np) {
153 printk(KERN_ERR "CPLD PIC init: can not find cpld-pic node\n");
154 return;
155 }
156
157 cpld_regs = of_iomap(np, 0);
158 of_node_put(np);
159}
160
161void __init
162mpc5121_ads_cpld_pic_init(void)
163{
164 unsigned int cascade_irq;
165 struct device_node *np = NULL;
166
167 pr_debug("cpld_ic_init\n");
168
169 np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld-pic");
170 if (!np) {
171 printk(KERN_ERR "CPLD PIC init: can not find cpld-pic node\n");
172 return;
173 }
174
175 if (!cpld_regs)
176 goto end;
177
178 cascade_irq = irq_of_parse_and_map(np, 0);
179 if (cascade_irq == NO_IRQ)
180 goto end;
181
182 /*
183 * statically route touch screen pendown through 1
184 * and ignore it here
185 * route all others through our cascade irq
186 */
187 out_8(&cpld_regs->route, 0xfd);
188 out_8(&cpld_regs->pci_mask, 0xff);
189 /* unmask pci ints in misc mask */
190 out_8(&cpld_regs->misc_mask, ~(MISC_IGNORE));
191
192 cpld_pic_node = of_node_get(np);
193
194 cpld_pic_host =
195 irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, 16, &cpld_pic_host_ops, 16);
196 if (!cpld_pic_host) {
197 printk(KERN_ERR "CPLD PIC: failed to allocate irq host!\n");
198 goto end;
199 }
200
201 set_irq_chained_handler(cascade_irq, cpld_pic_cascade);
202end:
203 of_node_put(np);
204}
diff --git a/arch/powerpc/platforms/512x/mpc5121_generic.c b/arch/powerpc/platforms/512x/mpc5121_generic.c
new file mode 100644
index 00000000000..2479de9e2d1
--- /dev/null
+++ b/arch/powerpc/platforms/512x/mpc5121_generic.c
@@ -0,0 +1,58 @@
1/*
2 * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. All rights reserved.
3 *
4 * Author: John Rigby, <jrigby@freescale.com>
5 *
6 * Description:
7 * MPC5121 SoC setup
8 *
9 * This is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 */
15
16#include <linux/kernel.h>
17#include <linux/of_platform.h>
18
19#include <asm/machdep.h>
20#include <asm/ipic.h>
21#include <asm/prom.h>
22#include <asm/time.h>
23
24#include "mpc512x.h"
25
26/*
27 * list of supported boards
28 */
29static char *board[] __initdata = {
30 "prt,prtlvt",
31 NULL
32};
33
34/*
35 * Called very early, MMU is off, device-tree isn't unflattened
36 */
37static int __init mpc5121_generic_probe(void)
38{
39 unsigned long node = of_get_flat_dt_root();
40 int i = 0;
41
42 while (board[i]) {
43 if (of_flat_dt_is_compatible(node, board[i]))
44 break;
45 i++;
46 }
47
48 return board[i] != NULL;
49}
50
51define_machine(mpc5121_generic) {
52 .name = "MPC5121 generic",
53 .probe = mpc5121_generic_probe,
54 .init = mpc512x_declare_of_platform_devices,
55 .init_IRQ = mpc512x_init_IRQ,
56 .get_irq = ipic_get_irq,
57 .calibrate_decr = generic_calibrate_decr,
58};
diff --git a/arch/powerpc/platforms/512x/mpc512x.h b/arch/powerpc/platforms/512x/mpc512x.h
new file mode 100644
index 00000000000..9c03693cb00
--- /dev/null
+++ b/arch/powerpc/platforms/512x/mpc512x.h
@@ -0,0 +1,17 @@
1/*
2 * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * Prototypes for MPC512x shared code
10 */
11
12#ifndef __MPC512X_H__
13#define __MPC512X_H__
14extern unsigned long mpc512x_find_ips_freq(struct device_node *node);
15extern void __init mpc512x_init_IRQ(void);
16void __init mpc512x_declare_of_platform_devices(void);
17#endif /* __MPC512X_H__ */
diff --git a/arch/powerpc/platforms/512x/mpc512x_shared.c b/arch/powerpc/platforms/512x/mpc512x_shared.c
new file mode 100644
index 00000000000..d8cd579f319
--- /dev/null
+++ b/arch/powerpc/platforms/512x/mpc512x_shared.c
@@ -0,0 +1,83 @@
1/*
2 * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. All rights reserved.
3 *
4 * Author: John Rigby <jrigby@freescale.com>
5 *
6 * Description:
7 * MPC512x Shared code
8 *
9 * This is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 */
14
15#include <linux/kernel.h>
16#include <linux/io.h>
17#include <linux/irq.h>
18#include <linux/of_platform.h>
19
20#include <asm/machdep.h>
21#include <asm/ipic.h>
22#include <asm/prom.h>
23#include <asm/time.h>
24
25#include "mpc512x.h"
26
27unsigned long
28mpc512x_find_ips_freq(struct device_node *node)
29{
30 struct device_node *np;
31 const unsigned int *p_ips_freq = NULL;
32
33 of_node_get(node);
34 while (node) {
35 p_ips_freq = of_get_property(node, "bus-frequency", NULL);
36 if (p_ips_freq)
37 break;
38
39 np = of_get_parent(node);
40 of_node_put(node);
41 node = np;
42 }
43 if (node)
44 of_node_put(node);
45
46 return p_ips_freq ? *p_ips_freq : 0;
47}
48EXPORT_SYMBOL(mpc512x_find_ips_freq);
49
50void __init mpc512x_init_IRQ(void)
51{
52 struct device_node *np;
53
54 np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-ipic");
55 if (!np)
56 return;
57
58 ipic_init(np, 0);
59 of_node_put(np);
60
61 /*
62 * Initialize the default interrupt mapping priorities,
63 * in case the boot rom changed something on us.
64 */
65 ipic_set_default_priority();
66}
67
68/*
69 * Nodes to do bus probe on, soc and localbus
70 */
71static struct of_device_id __initdata of_bus_ids[] = {
72 { .compatible = "fsl,mpc5121-immr", },
73 { .compatible = "fsl,mpc5121-localbus", },
74 {},
75};
76
77void __init mpc512x_declare_of_platform_devices(void)
78{
79 if (of_platform_bus_probe(NULL, of_bus_ids, NULL))
80 printk(KERN_ERR __FILE__ ": "
81 "Error while probing of_platform bus\n");
82}
83