xref: /openbsd/sys/arch/loongson/dev/glxclk.c (revision 264ca280)
1 /*	$OpenBSD: glxclk.c,v 1.5 2015/07/19 21:11:47 jasper Exp $	*/
2 
3 /*
4  * Copyright (c) 2013 Paul Irofti.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/proc.h>
23 
24 #include <machine/bus.h>
25 #include <machine/autoconf.h>
26 
27 #include <dev/isa/isavar.h>
28 
29 #include <dev/pci/pcireg.h>
30 #include <dev/pci/pcivar.h>
31 #include <dev/pci/pcidevs.h>
32 
33 #include <dev/pci/glxreg.h>
34 #include <dev/pci/glxvar.h>
35 
36 struct glxclk_softc {
37 	struct device		sc_dev;
38 
39 	bus_space_tag_t		sc_iot;
40 	bus_space_handle_t	sc_ioh;
41 };
42 
43 struct cfdriver glxclk_cd = {
44 	NULL, "glxclk", DV_DULL
45 };
46 
47 int	glxclk_match(struct device *, void *, void *);
48 void	glxclk_attach(struct device *, struct device *, void *);
49 int	glxclk_intr(void *);
50 int	glxclk_stat_intr(void *arg);
51 void	glxclk_startclock(struct cpu_info *);
52 
53 struct cfattach glxclk_ca = {
54 	sizeof(struct glxclk_softc), glxclk_match, glxclk_attach,
55 };
56 
57 #define	MSR_LBAR_ENABLE		0x100000000ULL
58 #define	MSR_LBAR_MFGPT		DIVIL_LBAR_MFGPT
59 #define	MSR_MFGPT_SIZE		0x40
60 #define	MSR_MFGPT_ADDR_MASK	0xffc0
61 
62 #define	AMD5536_MFGPT1_CMP2	0x0000000a	/* Compare value for CMP2 */
63 #define	AMD5536_MFGPT1_CNT	0x0000000c	/* Up counter */
64 #define	AMD5536_MFGPT1_SETUP	0x0000000e	/* Setup register */
65 #define	AMD5536_MFGPT1_SCALE	0x7		/* Set to 128 */
66 #define	AMD5536_MFGPT1_C2_IRQM	0x00000200
67 
68 #define	AMD5536_MFGPT2_CMP2	0x00000012	/* Compare value for CMP2 */
69 #define	AMD5536_MFGPT2_CNT	0x00000014	/* Up counter */
70 #define	AMD5536_MFGPT2_SETUP	0x00000016	/* Setup register */
71 #define	AMD5536_MFGPT2_SCALE	0x3		/* Divide by 8 */
72 #define	AMD5536_MFGPT2_C2_IRQM	0x00000400
73 
74 #define	AMD5536_MFGPT_CNT_EN	(1 << 15)	/* Enable counting */
75 #define	AMD5536_MFGPT_CMP2	(1 << 14)	/* Compare 2 output */
76 #define	AMD5536_MFGPT_CMP1	(1 << 13)	/* Compare 1 output */
77 #define AMD5536_MFGPT_SETUP	(1 << 12)	/* Set to 1 after 1st write */
78 #define	AMD5536_MFGPT_STOP_EN	(1 << 11)	/* Stop enable */
79 #define	AMD5536_MFGPT_CMP2MODE	(1 << 9)|(1 << 8)/* Set to GE + activate IRQ */
80 #define AMD5536_MFGPT_CLKSEL	(1 << 4)	/* Clock select 14MHz */
81 
82 
83 struct glxclk_softc *glxclk_sc;
84 
85 /*
86  * Statistics clock interval and variance, in usec.  Variance must be a
87  * power of two.  Since this gives us an even number, not an odd number,
88  * we discard one case and compensate.  That is, a variance of 1024 would
89  * give us offsets in [0..1023].  Instead, we take offsets in [1..1023].
90  * This is symmetric about the point 512, or statvar/2, and thus averages
91  * to that value (assuming uniform random numbers).
92  */
93 /* XXX fix comment to match value */
94 int statvar = 8192;
95 int statmin;			/* statclock interval - 1/2*variance */
96 
97 int
98 glxclk_match(struct device *parent, void *match, void *aux)
99 {
100 	struct glxpcib_attach_args *gaa = aux;
101 	struct cfdata *cf = match;
102 
103 	if (strcmp(gaa->gaa_name, cf->cf_driver->cd_name) != 0)
104 		return 0;
105 
106 	return 1;
107 }
108 
109 void
110 glxclk_attach(struct device *parent, struct device *self, void *aux)
111 {
112 	glxclk_sc = (struct glxclk_softc *)self;
113 	struct glxpcib_attach_args *gaa = aux;
114 	u_int64_t wa;
115 	int statint, minint;
116 
117 	glxclk_sc->sc_iot = gaa->gaa_iot;
118 	glxclk_sc->sc_ioh = gaa->gaa_ioh;
119 
120 	wa = rdmsr(MSR_LBAR_MFGPT);
121 
122 	if ((wa & MSR_LBAR_ENABLE) == 0) {
123 		printf(" not configured\n");
124 		return;
125 	}
126 
127 	if (bus_space_map(glxclk_sc->sc_iot, wa & MSR_MFGPT_ADDR_MASK,
128 	    MSR_MFGPT_SIZE, 0, &glxclk_sc->sc_ioh)) {
129 		printf(" not configured\n");
130 		return;
131 	}
132 
133 	printf(": clock");
134 
135 	/* Set comparator 2 */
136 	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
137 	    AMD5536_MFGPT1_CMP2, 1);
138 
139 	/* Reset counter to 0 */
140 	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
141 	    AMD5536_MFGPT1_CNT, 0);
142 
143 	/*
144 	 * All the bits in the range 11:0 have to be written at once.
145 	 * After they're set the first time all further writes are
146 	 * ignored.
147 	 */
148 	uint16_t setup = (AMD5536_MFGPT1_SCALE | AMD5536_MFGPT_CMP2MODE |
149 	    AMD5536_MFGPT_CMP1 | AMD5536_MFGPT_CMP2 | AMD5536_MFGPT_CNT_EN);
150 
151 	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
152 	    AMD5536_MFGPT1_SETUP, setup);
153 
154 	/* Check to see if the MFGPT_SETUP bit was set */
155 	setup = bus_space_read_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
156 	    AMD5536_MFGPT1_SETUP);
157 	if ((setup & AMD5536_MFGPT_SETUP) == 0) {
158 		printf(" not configured\n");
159 		return;
160 	}
161 
162 	/* Enable MFGPT1 Comparator 2 Output to the Interrupt Mapper */
163 	wa = rdmsr(MFGPT_IRQ);
164 	wa |= AMD5536_MFGPT1_C2_IRQM;
165 	wrmsr(MFGPT_IRQ, wa);
166 
167 	/*
168 	 * Tie PIC input 5 to IG7 for glxclk(4).
169 	 */
170 	wa = rdmsr(PIC_ZSEL_LOW);
171 	wa &= ~(0xfUL << 20);
172 	wa |= 7 << 20;
173 	wrmsr(PIC_ZSEL_LOW, wa);
174 
175 	/* Start the counter */
176 	setup = (AMD5536_MFGPT_CNT_EN | AMD5536_MFGPT_CMP2);
177 	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
178 	    AMD5536_MFGPT1_SETUP, setup);
179 
180 	/*
181 	 * The interrupt argument is NULL in order to notify the dispatcher
182 	 * to pass the clock frame as argument. This trick also forces keeping
183 	 * the soft state global because during the interrupt we need to clear
184 	 * the comp2 event in the MFGPT setup register.
185 	 */
186 	isa_intr_establish(sys_platform->isa_chipset, 7, IST_PULSE, IPL_CLOCK,
187 	    glxclk_intr, NULL, "clock");
188 
189 	md_startclock = glxclk_startclock;
190 
191 	printf(", prof");
192 
193 
194 	/*
195 	 * Try to be as close as possible, w/o the variance, to the hardclock.
196 	 * The stat clock has its source set to the 14MHz clock so that the
197 	 * variance interval can be more generous.
198 	 *
199 	 * Experience shows that the clock source goes crazy on scale factors
200 	 * lower than 8, so keep it at 8 and adjust the counter (statint) so
201 	 * that it results a 128Hz statclock, just like the hardclock.
202 	 */
203 	statint = 16000;
204 	minint = statint / 2 + 100;
205 	while (statvar > minint)
206 		statvar >>= 1;
207 
208 	/* Set comparator 2 */
209 	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
210 	    AMD5536_MFGPT2_CMP2, statint);
211 	statmin = statint - (statvar >> 1);
212 
213 	/* Reset counter to 0 */
214 	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
215 	    AMD5536_MFGPT2_CNT, 0);
216 
217 	/*
218 	 * All the bits in the range 11:0 have to be written at once.
219 	 * After they're set the first time all further writes are
220 	 * ignored.
221 	 */
222 	setup = (AMD5536_MFGPT2_SCALE | AMD5536_MFGPT_CMP2MODE |
223 	    AMD5536_MFGPT_CLKSEL | AMD5536_MFGPT_CMP1 | AMD5536_MFGPT_CMP2 |
224 	    AMD5536_MFGPT_CNT_EN);
225 
226 	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
227 	    AMD5536_MFGPT2_SETUP, setup);
228 
229 	/* Check to see if the MFGPT_SETUP bit was set */
230 	setup = bus_space_read_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
231 	    AMD5536_MFGPT2_SETUP);
232 	if ((setup & AMD5536_MFGPT_SETUP) == 0) {
233 		printf(" not configured\n");
234 		return;
235 	}
236 
237 	/* Enable MFGPT2 Comparator 2 Output to the Interrupt Mapper */
238 	wa = rdmsr(MFGPT_IRQ);
239 	wa |= AMD5536_MFGPT2_C2_IRQM;
240 	wrmsr(MFGPT_IRQ, wa);
241 
242 	/*
243 	 * Tie PIC input 6 to IG8 for glxstat(4).
244 	 */
245 	wa = rdmsr(PIC_ZSEL_LOW);
246 	wa &= ~(0xfUL << 24);
247 	wa |= 8 << 24;
248 	wrmsr(PIC_ZSEL_LOW, wa);
249 
250 	/*
251 	 * The interrupt argument is NULL in order to notify the dispatcher
252 	 * to pass the clock frame as argument. This trick also forces keeping
253 	 * the soft state global because during the interrupt we need to clear
254 	 * the comp2 event in the MFGPT setup register.
255 	 */
256 	isa_intr_establish(sys_platform->isa_chipset, 8, IST_PULSE,
257 	    IPL_STATCLOCK, glxclk_stat_intr, NULL, "prof");
258 
259 	printf("\n");
260 }
261 
262 void
263 glxclk_startclock(struct cpu_info *ci)
264 {
265 	/* Start the clock. */
266 	int s = splclock();
267 	ci->ci_clock_started++;
268 	splx(s);
269 }
270 
271 int
272 glxclk_intr(void *arg)
273 {
274 	struct clockframe *frame = arg;
275 	uint16_t setup = 0;
276 	struct cpu_info *ci = curcpu();
277 
278 	/* Clear the current event */
279 	setup = bus_space_read_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
280 	    AMD5536_MFGPT1_SETUP);
281 	setup |= AMD5536_MFGPT_CMP2;
282 	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
283 	    AMD5536_MFGPT1_SETUP, setup);
284 
285 	if (ci->ci_clock_started == 0)
286 		return 1;
287 
288 	hardclock(frame);
289 
290 	return 1;
291 }
292 
293 int
294 glxclk_stat_intr(void *arg)
295 {
296 	struct clockframe *frame = arg;
297 	uint16_t setup = 0;
298 	struct cpu_info *ci = curcpu();
299 	u_long newint, r, var;
300 
301 	/* Clear the current event */
302 	setup = bus_space_read_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
303 	    AMD5536_MFGPT2_SETUP);
304 	setup |= AMD5536_MFGPT_CMP2;
305 	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
306 	    AMD5536_MFGPT2_SETUP, setup);
307 
308 	if (ci->ci_clock_started == 0)
309 		return 1;
310 
311 	statclock(frame);
312 
313 	/*
314 	 * Compute new randomized interval.  The intervals are uniformly
315 	 * distributed on [statint - statvar / 2, statint + statvar / 2],
316 	 * and therefore have mean statint, giving a stathz frequency clock.
317 	 */
318 	var = statvar;
319 	do {
320 		r = random() & (var - 1);
321 	} while (r == 0);
322 	newint = statmin + r;
323 
324 	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
325 	    AMD5536_MFGPT2_CMP2, newint);
326 
327 	return 1;
328 }
329