xref: /openbsd-src/sys/arch/loongson/dev/glxclk.c (revision 0ed1bf01ac7e45759b65902d0ab711deb359316d)
1 /*	$OpenBSD: glxclk.c,v 1.11 2023/09/17 14:50:51 cheloha 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/clockintr.h>
22 #include <sys/device.h>
23 #include <sys/kernel.h>
24 #include <sys/stdint.h>
25 
26 #include <machine/bus.h>
27 #include <machine/autoconf.h>
28 
29 #include <dev/isa/isavar.h>
30 
31 #include <dev/pci/pcireg.h>
32 #include <dev/pci/pcivar.h>
33 #include <dev/pci/pcidevs.h>
34 
35 #include <dev/pci/glxreg.h>
36 #include <dev/pci/glxvar.h>
37 
38 struct glxclk_softc {
39 	struct device		sc_dev;
40 
41 	bus_space_tag_t		sc_iot;
42 	bus_space_handle_t	sc_ioh;
43 
44 	struct intrclock	sc_intrclock;
45 };
46 
47 struct cfdriver glxclk_cd = {
48 	NULL, "glxclk", DV_DULL
49 };
50 
51 int	glxclk_match(struct device *, void *, void *);
52 void	glxclk_attach(struct device *, struct device *, void *);
53 int	glxclk_intr(void *);
54 void	glxclk_initclock(void);
55 void	glxclk_startclock(struct cpu_info *);
56 void	glxclk_rearm(void *, uint64_t);
57 void	glxclk_trigger(void *);
58 
59 const struct cfattach glxclk_ca = {
60 	sizeof(struct glxclk_softc), glxclk_match, glxclk_attach,
61 };
62 
63 #define	MSR_LBAR_ENABLE		0x100000000ULL
64 #define	MSR_LBAR_MFGPT		DIVIL_LBAR_MFGPT
65 #define	MSR_MFGPT_SIZE		0x40
66 #define	MSR_MFGPT_ADDR_MASK	0xffc0
67 
68 /*
69  * Experience shows that the clock source goes crazy on scale factors
70  * lower than 8, so keep it at 8.
71  */
72 
73 #define	AMD5536_MFGPT1_CMP2	0x0000000a	/* Compare value for CMP2 */
74 #define	AMD5536_MFGPT1_CNT	0x0000000c	/* Up counter */
75 #define	AMD5536_MFGPT1_SETUP	0x0000000e	/* Setup register */
76 #define	AMD5536_MFGPT1_SCALE	0x3		/* Set divider to 8 */
77 #define	AMD5536_MFGPT1_CLOCK	(1 << 15)	/* Clock frequency */
78 #define	AMD5536_MFGPT1_C2_IRQM	0x00000200
79 
80 #define	AMD5536_MFGPT_CNT_EN	(1 << 15)	/* Enable counting */
81 #define	AMD5536_MFGPT_CMP2	(1 << 14)	/* Compare 2 output */
82 #define	AMD5536_MFGPT_CMP1	(1 << 13)	/* Compare 1 output */
83 #define AMD5536_MFGPT_SETUP	(1 << 12)	/* Set to 1 after 1st write */
84 #define	AMD5536_MFGPT_STOP_EN	(1 << 11)	/* Stop enable */
85 #define	AMD5536_MFGPT_CMP2MODE	(1 << 9)|(1 << 8)/* Set to GE + activate IRQ */
86 #define AMD5536_MFGPT_CLKSEL	(1 << 4)	/* Clock select 14MHz */
87 
88 
89 struct glxclk_softc *glxclk_sc;
90 
91 int
glxclk_match(struct device * parent,void * match,void * aux)92 glxclk_match(struct device *parent, void *match, void *aux)
93 {
94 	struct glxpcib_attach_args *gaa = aux;
95 	struct cfdata *cf = match;
96 
97 	if (strcmp(gaa->gaa_name, cf->cf_driver->cd_name) != 0)
98 		return 0;
99 
100 	return 1;
101 }
102 
103 void
glxclk_attach(struct device * parent,struct device * self,void * aux)104 glxclk_attach(struct device *parent, struct device *self, void *aux)
105 {
106 	glxclk_sc = (struct glxclk_softc *)self;
107 	struct glxpcib_attach_args *gaa = aux;
108 	u_int64_t wa;
109 
110 	glxclk_sc->sc_iot = gaa->gaa_iot;
111 	glxclk_sc->sc_ioh = gaa->gaa_ioh;
112 
113 	wa = rdmsr(MSR_LBAR_MFGPT);
114 
115 	if ((wa & MSR_LBAR_ENABLE) == 0) {
116 		printf(" not configured\n");
117 		return;
118 	}
119 
120 	if (bus_space_map(glxclk_sc->sc_iot, wa & MSR_MFGPT_ADDR_MASK,
121 	    MSR_MFGPT_SIZE, 0, &glxclk_sc->sc_ioh)) {
122 		printf(" not configured\n");
123 		return;
124 	}
125 
126 	/* Set comparator 2 */
127 	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
128 	    AMD5536_MFGPT1_CMP2, 1);
129 
130 	/* Reset counter to 0 */
131 	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
132 	    AMD5536_MFGPT1_CNT, 0);
133 
134 	/*
135 	 * All the bits in the range 11:0 have to be written at once.
136 	 * After they're set the first time all further writes are
137 	 * ignored.
138 	 */
139 	uint16_t setup = (AMD5536_MFGPT1_SCALE | AMD5536_MFGPT_CMP2MODE);
140 
141 	bus_space_write_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
142 	    AMD5536_MFGPT1_SETUP, setup);
143 
144 	/* Check to see if the MFGPT_SETUP bit was set */
145 	setup = bus_space_read_2(glxclk_sc->sc_iot, glxclk_sc->sc_ioh,
146 	    AMD5536_MFGPT1_SETUP);
147 	if ((setup & AMD5536_MFGPT_SETUP) == 0) {
148 		printf(" not configured\n");
149 		return;
150 	}
151 
152 	/* Enable MFGPT1 Comparator 2 Output to the Interrupt Mapper */
153 	wa = rdmsr(MFGPT_IRQ);
154 	wa |= AMD5536_MFGPT1_C2_IRQM;
155 	wrmsr(MFGPT_IRQ, wa);
156 
157 	/*
158 	 * Tie PIC input 5 to IG7 for glxclk(4).
159 	 */
160 	wa = rdmsr(PIC_ZSEL_LOW);
161 	wa &= ~(0xfUL << 20);
162 	wa |= 7 << 20;
163 	wrmsr(PIC_ZSEL_LOW, wa);
164 
165 	/*
166 	 * The interrupt argument is NULL in order to notify the dispatcher
167 	 * to pass the clock frame as argument. This trick also forces keeping
168 	 * the soft state global because during the interrupt we need to
169 	 * disable the counter in the MFGPT setup register.
170 	 */
171 	isa_intr_establish(sys_platform->isa_chipset, 7, IST_LEVEL, IPL_CLOCK,
172 	    glxclk_intr, NULL, "clock");
173 
174 	glxclk_sc->sc_intrclock.ic_cookie = glxclk_sc;
175 	glxclk_sc->sc_intrclock.ic_rearm = glxclk_rearm;
176 	glxclk_sc->sc_intrclock.ic_trigger = glxclk_trigger;
177 
178 	md_initclock = glxclk_initclock;
179 	md_startclock = glxclk_startclock;
180 
181 	printf("\n");
182 }
183 
184 void
glxclk_initclock(void)185 glxclk_initclock(void)
186 {
187 	/*
188 	 * MFGPT runs on powers of two, adjust the hz value accordingly.
189 	 */
190 	stathz = hz = 128;
191 	profhz = hz * 10;
192 	statclock_is_randomized = 1;
193 	tick = 1000000 / hz;
194 	tick_nsec = 1000000000 / hz;
195 }
196 
197 void
glxclk_startclock(struct cpu_info * ci)198 glxclk_startclock(struct cpu_info *ci)
199 {
200 	clockintr_cpu_init(&glxclk_sc->sc_intrclock);
201 
202 	/* Start the clock. */
203 	int s = splclock();
204 	ci->ci_clock_started++;
205 	clockintr_trigger();
206 	splx(s);
207 }
208 
209 int
glxclk_intr(void * arg)210 glxclk_intr(void *arg)
211 {
212 	struct clockframe *frame = arg;
213 	struct cpu_info *ci = curcpu();
214 	struct glxclk_softc *sc = glxclk_sc;
215 
216 	/* Clear the current event and disable the counter. */
217 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT1_SETUP,
218 	    AMD5536_MFGPT_CMP1 | AMD5536_MFGPT_CMP2);
219 
220 	if (ci->ci_clock_started == 0)
221 		return 1;
222 
223 	clockintr_dispatch(frame);
224 
225 	return 1;
226 }
227 
228 void
glxclk_rearm(void * cookie,uint64_t nsecs)229 glxclk_rearm(void *cookie, uint64_t nsecs)
230 {
231 	const uint64_t freq = AMD5536_MFGPT1_CLOCK >> AMD5536_MFGPT1_SCALE;
232 	const uint64_t nsec_ratio = (freq << 32) / 1000000000;
233 	/* Subtract 1 to leave room for rounding. */
234 	const uint64_t nsec_max = UINT64_MAX / nsec_ratio - 1;
235 	struct glxclk_softc *sc = cookie;
236 	uint64_t count;
237 	register_t sr;
238 
239 	nsecs = MIN(nsecs, nsec_max);
240 	count = MAX((nsecs * nsec_ratio + (1U << 31)) >> 32, 1);
241 
242 	sr = disableintr();
243 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT1_CMP2, count);
244 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT1_CNT, 0);
245 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT1_SETUP,
246 	    AMD5536_MFGPT_CNT_EN | AMD5536_MFGPT_CMP2);
247 	setsr(sr);
248 }
249 
250 void
glxclk_trigger(void * cookie)251 glxclk_trigger(void *cookie)
252 {
253 	glxclk_rearm(cookie, 0);
254 }
255