1 /* $NetBSD: sbtimer.c,v 1.13 2008/01/08 14:38:47 simonb 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/cdefs.h> 36 __KERNEL_RCSID(0, "$NetBSD: sbtimer.c,v 1.13 2008/01/08 14:38:47 simonb Exp $"); 37 38 #include <sys/param.h> 39 #include <sys/device.h> 40 #include <sys/systm.h> 41 #include <sys/kernel.h> 42 43 #include <mips/locore.h> 44 45 #include <mips/sibyte/include/sb1250_regs.h> 46 #include <mips/sibyte/include/sb1250_scd.h> 47 #include <mips/sibyte/dev/sbscdvar.h> 48 49 struct sbtimer_softc { 50 struct device sc_dev; 51 void *sc_intrhand; 52 int sc_flags; 53 void *sc_addr_icnt, *sc_addr_cnt, *sc_addr_cfg; 54 }; 55 #define SBTIMER_CLOCK 1 56 #define SBTIMER_STATCLOCK 2 57 58 #define READ_REG(rp) (mips3_ld((uint64_t *)(rp))) 59 #define WRITE_REG(rp, val) (mips3_sd((uint64_t *)(rp), (val))) 60 61 static int sbtimer_match(struct device *, struct cfdata *, void *); 62 static void sbtimer_attach(struct device *, struct device *, void *); 63 64 CFATTACH_DECL(sbtimer, sizeof(struct sbtimer_softc), 65 sbtimer_match, sbtimer_attach, NULL, NULL); 66 67 static void sbtimer_clockintr(void *arg, uint32_t status, uint32_t pc); 68 static void sbtimer_statclockintr(void *arg, uint32_t status, 69 uint32_t pc); 70 static void sbtimer_miscintr(void *arg, uint32_t status, uint32_t pc); 71 72 static void sbtimer_clock_init(void *arg); 73 74 static int 75 sbtimer_match(struct device *parent, struct cfdata *match, void *aux) 76 { 77 struct sbscd_attach_args *sap = aux; 78 79 if (sap->sa_locs.sa_type != SBSCD_DEVTYPE_TIMER) 80 return (0); 81 82 return 1; 83 } 84 85 static void 86 sbtimer_attach(struct device *parent, struct device *self, void *aux) 87 { 88 struct sbscd_attach_args *sa = aux; 89 struct sbtimer_softc *sc = (struct sbtimer_softc *)self; 90 void (*fun)(void *, uint32_t, uint32_t); 91 int ipl; 92 const char *comment = ""; 93 94 sc->sc_flags = device_cfdata(&sc->sc_dev)->cf_flags; 95 sc->sc_addr_icnt = (uint64_t *)MIPS_PHYS_TO_KSEG1(sa->sa_base + 96 sa->sa_locs.sa_offset + R_SCD_TIMER_INIT); 97 sc->sc_addr_cnt = (uint64_t *)MIPS_PHYS_TO_KSEG1(sa->sa_base + 98 sa->sa_locs.sa_offset + R_SCD_TIMER_CNT); 99 sc->sc_addr_cfg = (uint64_t *)MIPS_PHYS_TO_KSEG1(sa->sa_base + 100 sa->sa_locs.sa_offset + R_SCD_TIMER_CFG); 101 102 printf(": "); 103 if ((sc->sc_flags & SBTIMER_CLOCK) != 0) { 104 ipl = IPL_CLOCK; 105 fun = sbtimer_clockintr; 106 107 if (system_set_clockfns(sc, sbtimer_clock_init)) { 108 /* not really the clock */ 109 sc->sc_flags &= ~SBTIMER_CLOCK; 110 comment = " (not system timer)"; 111 goto not_really; 112 } 113 printf("system timer"); 114 } else if ((sc->sc_flags & SBTIMER_STATCLOCK) != 0) { 115 ipl = IPL_HIGH; 116 fun = sbtimer_statclockintr; 117 118 /* XXX make sure it's the statclock */ 119 if (1) { 120 /* not really the statclock */ 121 sc->sc_flags &= ~SBTIMER_STATCLOCK; 122 comment = " (not system statistics timer)"; 123 goto not_really; 124 } 125 printf("system statistics timer"); 126 } else { 127 not_really: 128 ipl = IPL_BIO; /* XXX -- pretty low */ 129 fun = sbtimer_miscintr; 130 printf("general-purpose timer%s", comment); 131 } 132 printf("\n"); 133 134 /* clear intr & disable timer. */ 135 WRITE_REG(sc->sc_addr_cfg, 0x00); /* XXX */ 136 137 sc->sc_intrhand = cpu_intr_establish(sa->sa_locs.sa_intr[0], ipl, 138 fun, sc); 139 } 140 141 static void 142 sbtimer_clock_init(void *arg) 143 { 144 struct sbtimer_softc *sc = arg; 145 146 printf("%s: ", sc->sc_dev.dv_xname); 147 if ((1000000 % hz) == 0) 148 printf("%dHz system timer\n", hz); 149 else { 150 printf("cannot get %dHz clock; using 1000Hz\n", hz); 151 hz = 1000; 152 tick = 1000000 / hz; 153 } 154 155 WRITE_REG(sc->sc_addr_cfg, 0x00); /* XXX */ 156 if (G_SYS_PLL_DIV(READ_REG(MIPS_PHYS_TO_KSEG1(A_SCD_SYSTEM_CFG))) == 0) { 157 printf("%s: PLL_DIV == 0; speeding up clock ticks for simulator\n", 158 sc->sc_dev.dv_xname); 159 WRITE_REG(sc->sc_addr_icnt, (tick/100) - 1); /* XXX */ 160 } else { 161 WRITE_REG(sc->sc_addr_icnt, tick - 1); /* XXX */ 162 } 163 WRITE_REG(sc->sc_addr_cfg, 0x03); /* XXX */ 164 } 165 166 static void 167 sbtimer_clockintr(void *arg, uint32_t status, uint32_t pc) 168 { 169 struct sbtimer_softc *sc = arg; 170 struct clockframe cf; 171 172 /* clear interrupt, but leave timer enabled and in repeating mode */ 173 WRITE_REG(sc->sc_addr_cfg, 0x03); /* XXX */ 174 175 cf.pc = pc; 176 cf.sr = status; 177 178 hardclock(&cf); 179 180 /* 181 * We never want a CPU core clock interrupt, so adjust the CP0 182 * compare register to just before the CP0 clock register's value 183 * each time. 184 */ 185 mips3_cp0_compare_write(mips3_cp0_count_read() - 1); 186 } 187 188 static void 189 sbtimer_statclockintr(void *arg, uint32_t status, uint32_t pc) 190 { 191 struct sbtimer_softc *sc = arg; 192 struct clockframe cf; 193 194 /* clear intr & disable timer, reset initial count, re-enable timer */ 195 WRITE_REG(sc->sc_addr_cfg, 0x00); /* XXX */ 196 /* XXX more to do */ 197 198 cf.pc = pc; 199 cf.sr = status; 200 201 statclock(&cf); 202 } 203 204 static void 205 sbtimer_miscintr(void *arg, uint32_t status, uint32_t pc) 206 { 207 struct sbtimer_softc *sc = arg; 208 209 /* disable timer */ 210 WRITE_REG(sc->sc_addr_cfg, 0x00); /* XXX */ 211 212 /* XXX more to do */ 213 } 214