1 /* $NetBSD: sbtimer.c,v 1.8 2003/02/07 17:38:49 cgd Exp $ */ 2 3 /* 4 * Copyright 2000, 2001 5 * Broadcom Corporation. All rights reserved. 6 * 7 * This software is furnished under license and may be used and copied only 8 * in accordance with the following terms and conditions. Subject to these 9 * conditions, you may download, copy, install, use, modify and distribute 10 * modified or unmodified copies of this software in source and/or binary 11 * form. No title or ownership is transferred hereby. 12 * 13 * 1) Any source code used, modified or distributed must reproduce and 14 * retain this copyright notice and list of conditions as they appear in 15 * the source file. 16 * 17 * 2) No right is granted to use any trade name, trademark, or logo of 18 * Broadcom Corporation. The "Broadcom Corporation" name may not be 19 * used to endorse or promote products derived from this software 20 * without the prior written permission of Broadcom Corporation. 21 * 22 * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR IMPLIED 23 * WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED WARRANTIES OF 24 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 25 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM BE LIABLE 26 * FOR ANY DAMAGES WHATSOEVER, AND IN PARTICULAR, BROADCOM SHALL NOT BE 27 * LIABLE FOR DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 30 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 32 * OR OTHERWISE), EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/device.h> 37 #include <sys/systm.h> 38 #include <sys/kernel.h> 39 40 #include <mips/locore.h> 41 42 #include <mips/sibyte/include/sb1250_regs.h> 43 #include <mips/sibyte/include/sb1250_scd.h> 44 #include <mips/sibyte/dev/sbscdvar.h> 45 46 struct sbtimer_softc { 47 struct device sc_dev; 48 void *sc_intrhand; 49 int sc_flags; 50 void *sc_addr_icnt, *sc_addr_cnt, *sc_addr_cfg; 51 }; 52 #define SBTIMER_CLOCK 1 53 #define SBTIMER_STATCLOCK 2 54 55 #define READ_REG(rp) (mips3_ld((uint64_t *)(rp))) 56 #define WRITE_REG(rp, val) (mips3_sd((uint64_t *)(rp), (val))) 57 58 static int sbtimer_match(struct device *, struct cfdata *, void *); 59 static void sbtimer_attach(struct device *, struct device *, void *); 60 61 CFATTACH_DECL(sbtimer, sizeof(struct sbtimer_softc), 62 sbtimer_match, sbtimer_attach, NULL, NULL); 63 64 static void sbtimer_clockintr(void *arg, uint32_t status, uint32_t pc); 65 static void sbtimer_statclockintr(void *arg, uint32_t status, 66 uint32_t pc); 67 static void sbtimer_miscintr(void *arg, uint32_t status, uint32_t pc); 68 69 static void sbtimer_clock_init(void *arg); 70 71 static int 72 sbtimer_match(struct device *parent, struct cfdata *match, void *aux) 73 { 74 struct sbscd_attach_args *sap = aux; 75 76 if (sap->sa_locs.sa_type != SBSCD_DEVTYPE_TIMER) 77 return (0); 78 79 return 1; 80 } 81 82 static void 83 sbtimer_attach(struct device *parent, struct device *self, void *aux) 84 { 85 struct sbscd_attach_args *sa = aux; 86 struct sbtimer_softc *sc = (struct sbtimer_softc *)self; 87 void (*fun)(void *, uint32_t, uint32_t); 88 int ipl; 89 const char *comment = ""; 90 91 sc->sc_flags = sc->sc_dev.dv_cfdata->cf_flags; 92 sc->sc_addr_icnt = (uint64_t *)MIPS_PHYS_TO_KSEG1(sa->sa_base + 93 sa->sa_locs.sa_offset + R_SCD_TIMER_INIT); 94 sc->sc_addr_cnt = (uint64_t *)MIPS_PHYS_TO_KSEG1(sa->sa_base + 95 sa->sa_locs.sa_offset + R_SCD_TIMER_CNT); 96 sc->sc_addr_cfg = (uint64_t *)MIPS_PHYS_TO_KSEG1(sa->sa_base + 97 sa->sa_locs.sa_offset + R_SCD_TIMER_CFG); 98 99 printf(": "); 100 if ((sc->sc_flags & SBTIMER_CLOCK) != 0) { 101 ipl = IPL_CLOCK; 102 fun = sbtimer_clockintr; 103 104 if (system_set_clockfns(sc, sbtimer_clock_init)) { 105 /* not really the clock */ 106 sc->sc_flags &= ~SBTIMER_CLOCK; 107 comment = " (not system timer)"; 108 goto not_really; 109 } 110 printf("system timer"); 111 } else if ((sc->sc_flags & SBTIMER_STATCLOCK) != 0) { 112 ipl = IPL_STATCLOCK; 113 fun = sbtimer_statclockintr; 114 115 /* XXX make sure it's the statclock */ 116 if (1) { 117 /* not really the statclock */ 118 sc->sc_flags &= ~SBTIMER_STATCLOCK; 119 comment = " (not system statistics timer)"; 120 goto not_really; 121 } 122 printf("system statistics timer"); 123 } else { 124 not_really: 125 ipl = IPL_BIO; /* XXX -- pretty low */ 126 fun = sbtimer_miscintr; 127 printf("general-purpose timer%s", comment); 128 } 129 printf("\n"); 130 131 /* clear intr & disable timer. */ 132 WRITE_REG(sc->sc_addr_cfg, 0x00); /* XXX */ 133 134 sc->sc_intrhand = cpu_intr_establish(sa->sa_locs.sa_intr[0], ipl, 135 fun, sc); 136 } 137 138 static void 139 sbtimer_clock_init(void *arg) 140 { 141 struct sbtimer_softc *sc = arg; 142 143 printf("%s: ", sc->sc_dev.dv_xname); 144 if ((1000000 % hz) == 0) 145 printf("%dHz system timer\n", hz); 146 else { 147 printf("cannot get %dHz clock; using 1000Hz\n", hz); 148 hz = 1000; 149 tick = 1000000 / hz; 150 } 151 152 WRITE_REG(sc->sc_addr_cfg, 0x00); /* XXX */ 153 if (G_SYS_PLL_DIV(READ_REG(MIPS_PHYS_TO_KSEG1(A_SCD_SYSTEM_CFG))) == 0) { 154 printf("%s: PLL_DIV == 0; speeding up clock ticks for simulator\n", 155 sc->sc_dev.dv_xname); 156 WRITE_REG(sc->sc_addr_icnt, (tick/100) - 1); /* XXX */ 157 } else { 158 WRITE_REG(sc->sc_addr_icnt, tick - 1); /* XXX */ 159 } 160 WRITE_REG(sc->sc_addr_cfg, 0x03); /* XXX */ 161 } 162 163 static void 164 sbtimer_clockintr(void *arg, uint32_t status, uint32_t pc) 165 { 166 struct sbtimer_softc *sc = arg; 167 struct clockframe cf; 168 169 /* clear interrupt, but leave timer enabled and in repeating mode */ 170 WRITE_REG(sc->sc_addr_cfg, 0x03); /* XXX */ 171 172 cf.pc = pc; 173 cf.sr = status; 174 175 /* reset the CPU count register (used by microtime) */ 176 mips3_cp0_count_write(0); 177 178 hardclock(&cf); 179 } 180 181 static void 182 sbtimer_statclockintr(void *arg, uint32_t status, uint32_t pc) 183 { 184 struct sbtimer_softc *sc = arg; 185 struct clockframe cf; 186 187 /* clear intr & disable timer, reset initial count, re-enable timer */ 188 WRITE_REG(sc->sc_addr_cfg, 0x00); /* XXX */ 189 /* XXX more to do */ 190 191 cf.pc = pc; 192 cf.sr = status; 193 194 statclock(&cf); 195 } 196 197 static void 198 sbtimer_miscintr(void *arg, uint32_t status, uint32_t pc) 199 { 200 struct sbtimer_softc *sc = arg; 201 202 /* disable timer */ 203 WRITE_REG(sc->sc_addr_cfg, 0x00); /* XXX */ 204 205 /* XXX more to do */ 206 } 207