Commit | Line | Data |
---|---|---|
6065170c GL |
1 | /* |
2 | * | |
3 | * Utility functions for the Freescale MPC52xx. | |
4 | * | |
5 | * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com> | |
6 | * | |
7 | * This file is licensed under the terms of the GNU General Public License | |
8 | * version 2. This program is licensed "as is" without any warranty of any | |
9 | * kind, whether express or implied. | |
10 | * | |
11 | */ | |
12 | ||
13 | #undef DEBUG | |
14 | ||
15 | #include <linux/kernel.h> | |
9fe2e796 | 16 | #include <linux/of_platform.h> |
6065170c GL |
17 | #include <asm/io.h> |
18 | #include <asm/prom.h> | |
6065170c GL |
19 | #include <asm/mpc52xx.h> |
20 | ||
86b92cdd MB |
21 | /* |
22 | * This variable is mapped in mpc52xx_map_wdt() and used in mpc52xx_restart(). | |
23 | * Permanent mapping is required because mpc52xx_restart() can be called | |
24 | * from interrupt context while node mapping (which calls ioremap()) | |
25 | * cannot be used at such point. | |
26 | */ | |
27 | static volatile struct mpc52xx_gpt *mpc52xx_wdt = NULL; | |
6065170c | 28 | |
c5c01c97 MB |
29 | static void __iomem * |
30 | mpc52xx_map_node(struct device_node *ofn) | |
6065170c | 31 | { |
6065170c GL |
32 | const u32 *regaddr_p; |
33 | u64 regaddr64, size64; | |
34 | ||
6065170c GL |
35 | if (!ofn) |
36 | return NULL; | |
37 | ||
38 | regaddr_p = of_get_address(ofn, 0, &size64, NULL); | |
39 | if (!regaddr_p) { | |
40 | of_node_put(ofn); | |
41 | return NULL; | |
42 | } | |
43 | ||
44 | regaddr64 = of_translate_address(ofn, regaddr_p); | |
45 | ||
46 | of_node_put(ofn); | |
47 | ||
48 | return ioremap((u32)regaddr64, (u32)size64); | |
49 | } | |
c5c01c97 MB |
50 | |
51 | void __iomem * | |
52 | mpc52xx_find_and_map(const char *compatible) | |
53 | { | |
54 | return mpc52xx_map_node( | |
55 | of_find_compatible_node(NULL, NULL, compatible)); | |
56 | } | |
57 | ||
d8594d63 | 58 | EXPORT_SYMBOL(mpc52xx_find_and_map); |
6065170c | 59 | |
c5c01c97 MB |
60 | void __iomem * |
61 | mpc52xx_find_and_map_path(const char *path) | |
62 | { | |
63 | return mpc52xx_map_node(of_find_node_by_path(path)); | |
64 | } | |
65 | ||
66 | EXPORT_SYMBOL(mpc52xx_find_and_map_path); | |
6065170c GL |
67 | |
68 | /** | |
69 | * mpc52xx_find_ipb_freq - Find the IPB bus frequency for a device | |
70 | * @node: device node | |
71 | * | |
72 | * Returns IPB bus frequency, or 0 if the bus frequency cannot be found. | |
73 | */ | |
74 | unsigned int | |
75 | mpc52xx_find_ipb_freq(struct device_node *node) | |
76 | { | |
77 | struct device_node *np; | |
78 | const unsigned int *p_ipb_freq = NULL; | |
79 | ||
80 | of_node_get(node); | |
81 | while (node) { | |
e2eb6392 | 82 | p_ipb_freq = of_get_property(node, "bus-frequency", NULL); |
6065170c GL |
83 | if (p_ipb_freq) |
84 | break; | |
85 | ||
86 | np = of_get_parent(node); | |
87 | of_node_put(node); | |
88 | node = np; | |
89 | } | |
90 | if (node) | |
91 | of_node_put(node); | |
92 | ||
93 | return p_ipb_freq ? *p_ipb_freq : 0; | |
94 | } | |
d8594d63 | 95 | EXPORT_SYMBOL(mpc52xx_find_ipb_freq); |
6065170c GL |
96 | |
97 | ||
4de3b992 GL |
98 | /* |
99 | * Configure the XLB arbiter settings to match what Linux expects. | |
100 | */ | |
6065170c | 101 | void __init |
4de3b992 | 102 | mpc5200_setup_xlb_arbiter(void) |
6065170c | 103 | { |
6065170c GL |
104 | struct mpc52xx_xlb __iomem *xlb; |
105 | ||
e3aba81d | 106 | xlb = mpc52xx_find_and_map("mpc5200-xlb"); |
4de3b992 | 107 | if (!xlb) { |
6065170c | 108 | printk(KERN_ERR __FILE__ ": " |
4de3b992 | 109 | "Error mapping XLB in mpc52xx_setup_cpu(). " |
6065170c | 110 | "Expect some abnormal behavior\n"); |
4de3b992 | 111 | return; |
6065170c GL |
112 | } |
113 | ||
6065170c GL |
114 | /* Configure the XLB Arbiter priorities */ |
115 | out_be32(&xlb->master_pri_enable, 0xff); | |
116 | out_be32(&xlb->master_priority, 0x11111111); | |
117 | ||
4de3b992 GL |
118 | /* Disable XLB pipelining |
119 | * (cfr errate 292. We could do this only just before ATA PIO | |
120 | * transaction and re-enable it afterwards ...) | |
121 | */ | |
6065170c GL |
122 | out_be32(&xlb->config, in_be32(&xlb->config) | MPC52xx_XLB_CFG_PLDIS); |
123 | ||
4de3b992 | 124 | iounmap(xlb); |
6065170c GL |
125 | } |
126 | ||
5c334eed | 127 | void __init |
6065170c GL |
128 | mpc52xx_declare_of_platform_devices(void) |
129 | { | |
130 | /* Find every child of the SOC node and add it to of_platform */ | |
5c334eed SM |
131 | if (of_platform_bus_probe(NULL, NULL, NULL)) |
132 | printk(KERN_ERR __FILE__ ": " | |
133 | "Error while probing of_platform bus\n"); | |
6065170c GL |
134 | } |
135 | ||
86b92cdd MB |
136 | void __init |
137 | mpc52xx_map_wdt(void) | |
138 | { | |
139 | const void *has_wdt; | |
140 | struct device_node *np; | |
141 | ||
142 | /* mpc52xx_wdt is mapped here and used in mpc52xx_restart, | |
143 | * possibly from a interrupt context. wdt is only implement | |
144 | * on a gpt0, so check has-wdt property before mapping. | |
145 | */ | |
146 | for_each_compatible_node(np, NULL, "fsl,mpc5200-gpt") { | |
147 | has_wdt = of_get_property(np, "fsl,has-wdt", NULL); | |
148 | if (has_wdt) { | |
149 | mpc52xx_wdt = mpc52xx_map_node(np); | |
150 | return; | |
151 | } | |
152 | } | |
153 | for_each_compatible_node(np, NULL, "mpc5200-gpt") { | |
154 | has_wdt = of_get_property(np, "has-wdt", NULL); | |
155 | if (has_wdt) { | |
156 | mpc52xx_wdt = mpc52xx_map_node(np); | |
157 | return; | |
158 | } | |
159 | } | |
160 | } | |
161 | ||
162 | void | |
163 | mpc52xx_restart(char *cmd) | |
164 | { | |
165 | local_irq_disable(); | |
166 | ||
167 | /* Turn on the watchdog and wait for it to expire. | |
168 | * It effectively does a reset. */ | |
169 | if (mpc52xx_wdt) { | |
170 | out_be32(&mpc52xx_wdt->mode, 0x00000000); | |
171 | out_be32(&mpc52xx_wdt->count, 0x000000ff); | |
172 | out_be32(&mpc52xx_wdt->mode, 0x00009004); | |
173 | } else | |
174 | printk("mpc52xx_restart: Can't access wdt. " | |
175 | "Restart impossible, system halted.\n"); | |
176 | ||
177 | while (1); | |
178 | } |