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>
16 #include <linux/spinlock.h>
17 #include <linux/of_platform.h>
18 #include <asm/io.h>
19 #include <asm/prom.h>
20 #include <asm/mpc52xx.h>
21 
22 /* MPC5200 device tree match tables */
23 static struct of_device_id mpc52xx_xlb_ids[] __initdata = {
24 	{ .compatible = "fsl,mpc5200-xlb", },
25 	{ .compatible = "mpc5200-xlb", },
26 	{}
27 };
28 static struct of_device_id mpc52xx_bus_ids[] __initdata = {
29 	{ .compatible = "fsl,mpc5200-immr", },
30 	{ .compatible = "fsl,mpc5200b-immr", },
31 	{ .compatible = "simple-bus", },
32 
33 	/* depreciated matches; shouldn't be used in new device trees */
34 	{ .compatible = "fsl,lpb", },
35 	{ .type = "builtin", .compatible = "mpc5200", }, /* efika */
36 	{ .type = "soc", .compatible = "mpc5200", }, /* lite5200 */
37 	{}
38 };
39 
40 /*
41  * This variable is mapped in mpc52xx_map_wdt() and used in mpc52xx_restart().
42  * Permanent mapping is required because mpc52xx_restart() can be called
43  * from interrupt context while node mapping (which calls ioremap())
44  * cannot be used at such point.
45  */
46 static DEFINE_SPINLOCK(mpc52xx_lock);
47 static struct mpc52xx_gpt __iomem *mpc52xx_wdt;
48 static struct mpc52xx_cdm __iomem *mpc52xx_cdm;
49 
50 /*
51  * Configure the XLB arbiter settings to match what Linux expects.
52  */
53 void __init
54 mpc5200_setup_xlb_arbiter(void)
55 {
56 	struct device_node *np;
57 	struct mpc52xx_xlb  __iomem *xlb;
58 
59 	np = of_find_matching_node(NULL, mpc52xx_xlb_ids);
60 	xlb = of_iomap(np, 0);
61 	of_node_put(np);
62 	if (!xlb) {
63 		printk(KERN_ERR __FILE__ ": "
64 			"Error mapping XLB in mpc52xx_setup_cpu(). "
65 			"Expect some abnormal behavior\n");
66 		return;
67 	}
68 
69 	/* Configure the XLB Arbiter priorities */
70 	out_be32(&xlb->master_pri_enable, 0xff);
71 	out_be32(&xlb->master_priority, 0x11111111);
72 
73 	/*
74 	 * Disable XLB pipelining
75 	 * (cfr errate 292. We could do this only just before ATA PIO
76 	 *  transaction and re-enable it afterwards ...)
77 	 * Not needed on MPC5200B.
78 	 */
79 	if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR)
80 		out_be32(&xlb->config, in_be32(&xlb->config) | MPC52xx_XLB_CFG_PLDIS);
81 
82 	iounmap(xlb);
83 }
84 
85 /**
86  * mpc52xx_declare_of_platform_devices: register internal devices and children
87  *					of the localplus bus to the of_platform
88  *					bus.
89  */
90 void __init
91 mpc52xx_declare_of_platform_devices(void)
92 {
93 	/* Find every child of the SOC node and add it to of_platform */
94 	if (of_platform_bus_probe(NULL, mpc52xx_bus_ids, NULL))
95 		printk(KERN_ERR __FILE__ ": "
96 			"Error while probing of_platform bus\n");
97 }
98 
99 /*
100  * match tables used by mpc52xx_map_common_devices()
101  */
102 static struct of_device_id mpc52xx_gpt_ids[] __initdata = {
103 	{ .compatible = "fsl,mpc5200-gpt", },
104 	{ .compatible = "mpc5200-gpt", }, /* old */
105 	{}
106 };
107 static struct of_device_id mpc52xx_cdm_ids[] __initdata = {
108 	{ .compatible = "fsl,mpc5200-cdm", },
109 	{ .compatible = "mpc5200-cdm", }, /* old */
110 	{}
111 };
112 
113 /**
114  * mpc52xx_map_common_devices: iomap devices required by common code
115  */
116 void __init
117 mpc52xx_map_common_devices(void)
118 {
119 	struct device_node *np;
120 
121 	/* mpc52xx_wdt is mapped here and used in mpc52xx_restart,
122 	 * possibly from a interrupt context. wdt is only implement
123 	 * on a gpt0, so check has-wdt property before mapping.
124 	 */
125 	for_each_matching_node(np, mpc52xx_gpt_ids) {
126 		if (of_get_property(np, "fsl,has-wdt", NULL) ||
127 		    of_get_property(np, "has-wdt", NULL)) {
128 			mpc52xx_wdt = of_iomap(np, 0);
129 			of_node_put(np);
130 			break;
131 		}
132 	}
133 
134 	/* Clock Distribution Module, used by PSC clock setting function */
135 	np = of_find_matching_node(NULL, mpc52xx_cdm_ids);
136 	mpc52xx_cdm = of_iomap(np, 0);
137 	of_node_put(np);
138 }
139 
140 /**
141  * mpc52xx_set_psc_clkdiv: Set clock divider in the CDM for PSC ports
142  *
143  * @psc_id: id of psc port; must be 1,2,3 or 6
144  * @clkdiv: clock divider value to put into CDM PSC register.
145  */
146 int mpc52xx_set_psc_clkdiv(int psc_id, int clkdiv)
147 {
148 	unsigned long flags;
149 	u16 __iomem *reg;
150 	u32 val;
151 	u32 mask;
152 	u32 mclken_div;
153 
154 	if (!mpc52xx_cdm)
155 		return -ENODEV;
156 
157 	mclken_div = 0x8000 | (clkdiv & 0x1FF);
158 	switch (psc_id) {
159 	case 1: reg = &mpc52xx_cdm->mclken_div_psc1; mask = 0x20; break;
160 	case 2: reg = &mpc52xx_cdm->mclken_div_psc2; mask = 0x40; break;
161 	case 3: reg = &mpc52xx_cdm->mclken_div_psc3; mask = 0x80; break;
162 	case 6: reg = &mpc52xx_cdm->mclken_div_psc6; mask = 0x10; break;
163 	default:
164 		return -ENODEV;
165 	}
166 
167 	/* Set the rate and enable the clock */
168 	spin_lock_irqsave(&mpc52xx_lock, flags);
169 	out_be16(reg, mclken_div);
170 	val = in_be32(&mpc52xx_cdm->clk_enables);
171 	out_be32(&mpc52xx_cdm->clk_enables, val | mask);
172 	spin_unlock_irqrestore(&mpc52xx_lock, flags);
173 
174 	return 0;
175 }
176 EXPORT_SYMBOL(mpc52xx_set_psc_clkdiv);
177 
178 /**
179  * mpc52xx_get_xtal_freq - Get SYS_XTAL_IN frequency for a device
180  *
181  * @node: device node
182  *
183  * Returns the frequency of the external oscillator clock connected
184  * to the SYS_XTAL_IN pin, or 0 if it cannot be determined.
185  */
186 unsigned int mpc52xx_get_xtal_freq(struct device_node *node)
187 {
188 	u32 val;
189 	unsigned int freq;
190 
191 	if (!mpc52xx_cdm)
192 		return 0;
193 
194 	freq = mpc5xxx_get_bus_frequency(node);
195 	if (!freq)
196 		return 0;
197 
198 	if (in_8(&mpc52xx_cdm->ipb_clk_sel) & 0x1)
199 		freq *= 2;
200 
201 	val  = in_be32(&mpc52xx_cdm->rstcfg);
202 	if (val & (1 << 5))
203 		freq *= 8;
204 	else
205 		freq *= 4;
206 	if (val & (1 << 6))
207 		freq /= 12;
208 	else
209 		freq /= 16;
210 
211 	return freq;
212 }
213 EXPORT_SYMBOL(mpc52xx_get_xtal_freq);
214 
215 /**
216  * mpc52xx_restart: ppc_md->restart hook for mpc5200 using the watchdog timer
217  */
218 void
219 mpc52xx_restart(char *cmd)
220 {
221 	local_irq_disable();
222 
223 	/* Turn on the watchdog and wait for it to expire.
224 	 * It effectively does a reset. */
225 	if (mpc52xx_wdt) {
226 		out_be32(&mpc52xx_wdt->mode, 0x00000000);
227 		out_be32(&mpc52xx_wdt->count, 0x000000ff);
228 		out_be32(&mpc52xx_wdt->mode, 0x00009004);
229 	} else
230 		printk(KERN_ERR __FILE__ ": "
231 			"mpc52xx_restart: Can't access wdt. "
232 			"Restart impossible, system halted.\n");
233 
234 	while (1);
235 }
236