1/*- 2 * Copyright (c) 2005 Olivier Houchard. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26#include <sys/cdefs.h> 27__FBSDID("$FreeBSD$"); 28 29#include <sys/param.h> 30#include <sys/systm.h> 31#include <sys/kernel.h> 32#include <sys/module.h> 33#include <sys/time.h> 34#include <sys/bus.h> 35#include <sys/resource.h> 36#include <sys/rman.h> 37#include <sys/timetc.h> 38#include <sys/watchdog.h> 39 40#include <machine/bus.h> 41#include <machine/cpu.h> 42#include <machine/cpufunc.h> 43#include <machine/resource.h> 44#include <machine/frame.h> 45#include <machine/intr.h> 46#include <arm/at91/at91rm92reg.h> 47#include <arm/at91/at91var.h> 48#include <arm/at91/at91_streg.h> 49 50static struct at91st_softc { 51 bus_space_tag_t sc_st; 52 bus_space_handle_t sc_sh; 53 device_t sc_dev; 54 eventhandler_tag sc_wet; /* watchdog event handler tag */ 55} *timer_softc; 56 57#define RD4(off) \ 58 bus_space_read_4(timer_softc->sc_st, timer_softc->sc_sh, (off)) 59#define WR4(off, val) \ 60 bus_space_write_4(timer_softc->sc_st, timer_softc->sc_sh, (off), (val)) 61 62static void at91st_watchdog(void *, u_int, int *); 63 64static inline int 65st_crtr(void) 66{ 67 int cur1, cur2; 68 do { 69 cur1 = RD4(ST_CRTR); 70 cur2 = RD4(ST_CRTR); 71 } while (cur1 != cur2); 72 return (cur1); 73} 74 75static unsigned at91st_get_timecount(struct timecounter *tc); 76 77static struct timecounter at91st_timecounter = { 78 at91st_get_timecount, /* get_timecount */ 79 NULL, /* no poll_pps */ 80 0xfffffu, /* counter_mask */ 81 32768, /* frequency */ 82 "AT91RM9200 timer", /* name */ 83 1000 /* quality */ 84}; 85 86static int 87at91st_probe(device_t dev) 88{ 89 90 device_set_desc(dev, "ST"); 91 return (0); 92} 93 94static int 95at91st_attach(device_t dev) 96{ 97 struct at91_softc *sc = device_get_softc(device_get_parent(dev)); 98 99 timer_softc = device_get_softc(dev); 100 timer_softc->sc_st = sc->sc_st; 101 timer_softc->sc_dev = dev; 102 if (bus_space_subregion(sc->sc_st, sc->sc_sh, AT91RM92_ST_BASE, 103 AT91RM92_ST_SIZE, &timer_softc->sc_sh) != 0) 104 panic("couldn't subregion timer registers"); 105 /* 106 * Real time counter increments every clock cycle, need to set before 107 * initializing clocks so that DELAY works. 108 */ 109 WR4(ST_RTMR, 1); 110 /* Disable all interrupts */ 111 WR4(ST_IDR, 0xffffffff); 112 /* disable watchdog timer */ 113 WR4(ST_WDMR, 0); 114 115 timer_softc->sc_wet = EVENTHANDLER_REGISTER(watchdog_list, 116 at91st_watchdog, dev, 0); 117 device_printf(dev, 118 "watchdog registered, timeout intervall max. 64 sec\n"); 119 return (0); 120} 121 122static device_method_t at91st_methods[] = { 123 DEVMETHOD(device_probe, at91st_probe), 124 DEVMETHOD(device_attach, at91st_attach), 125 {0, 0}, 126}; 127 128static driver_t at91st_driver = { 129 "at91_st", 130 at91st_methods, 131 sizeof(struct at91st_softc), 132}; 133static devclass_t at91st_devclass; 134 135DRIVER_MODULE(at91_st, atmelarm, at91st_driver, at91st_devclass, 0, 0); 136 137static unsigned 138at91st_get_timecount(struct timecounter *tc) 139{ 140 return (st_crtr()); 141} 142 143/* 144 * t below is in a weird unit. The watchdog is set to 2^t 145 * nanoseconds. Since our watchdog timer can't really do that too 146 * well, we approximate it by assuming that the timeout interval for 147 * the lsb is 2^22 ns, which is 4.194ms. This is an overestimation of 148 * the actual time (3.906ms), but close enough for watchdogging. 149 * These approximations, though a violation of the spec, improve the 150 * performance of the application which typically specifies things as 151 * WD_TO_32SEC. In that last case, we'd wait 32s before the wdog 152 * reset. The spec says we should wait closer to 34s, but given how 153 * it is likely to be used, and the extremely coarse nature time 154 * interval, I think this is the best solution. 155 */ 156static void 157at91st_watchdog(void *argp, u_int cmd, int *error) 158{ 159 uint32_t wdog; 160 int t; 161 162 t = cmd & WD_INTERVAL; 163 if (t >= 22 && t <= 37) { 164 wdog = (1 << (t - 22)) | ST_WDMR_RSTEN; 165 *error = 0; 166 } else { 167 wdog = 0; 168 } 169 WR4(ST_WDMR, wdog); 170 WR4(ST_CR, ST_CR_WDRST); 171} 172 173static int 174clock_intr(void *arg) 175{ 176 struct trapframe *fp = arg; 177 178 /* The interrupt is shared, so we have to make sure it's for us. */ 179 if (RD4(ST_SR) & ST_SR_PITS) { 180 hardclock(TRAPF_USERMODE(fp), TRAPF_PC(fp)); 181 return (FILTER_HANDLED); 182 } 183 return (FILTER_STRAY); 184} 185 186void 187cpu_initclocks(void) 188{ 189 int rel_value; 190 struct resource *irq; 191 int rid = 0; 192 void *ih; 193 device_t dev = timer_softc->sc_dev; 194 195 rel_value = 32768 / hz; 196 if (rel_value < 1) 197 rel_value = 1; 198 if (32768 % hz) { 199 printf("Cannot get %d Hz clock; using %dHz\n", hz, 32768 / rel_value); 200 hz = 32768 / rel_value; 201 tick = 1000000 / hz; 202 } 203 /* Disable all interrupts.�*/ 204 WR4(ST_IDR, 0xffffffff); 205 /* The system timer shares the system irq (1) */ 206 irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 1, 1, 1, 207 RF_ACTIVE | RF_SHAREABLE); 208 if (!irq) 209 panic("Unable to allocate irq for the system timer"); 210 else 211 bus_setup_intr(dev, irq, INTR_TYPE_CLK, 212 clock_intr, NULL, NULL, &ih); 213 214 WR4(ST_PIMR, rel_value); 215 216 /* Enable PITS interrupts. */ 217 WR4(ST_IER, ST_SR_PITS); 218 tc_init(&at91st_timecounter); 219} 220 221void 222DELAY(int n) 223{ 224 uint32_t start, end, cur; 225 226 start = st_crtr(); 227 n = (n * 1000) / 32768; 228 if (n <= 0) 229 n = 1; 230 end = (start + n) & ST_CRTR_MASK; 231 cur = start; 232 if (start > end) { 233 while (cur >= start || cur < end) 234 cur = st_crtr(); 235 } else { 236 while (cur < end) 237 cur = st_crtr(); 238 } 239} 240 241void 242cpu_reset(void) 243{ 244 /* 245 * Reset the CPU by programmig the watchdog timer to reset the 246 * CPU after 128 'slow' clocks, or about ~4ms. Loop until 247 * the reset happens for safety. 248 */ 249 WR4(ST_WDMR, ST_WDMR_RSTEN | 2); 250 WR4(ST_CR, ST_CR_WDRST); 251 while (1) 252 continue; 253} 254 255void 256cpu_startprofclock(void) 257{ 258} 259 260void 261cpu_stopprofclock(void) 262{ 263} 264