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