at91_st.c revision 308325
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: stable/11/sys/arm/at91/at91_st.c 308325 2016-11-05 04:30:44Z mmel $"); 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/resource.h> 43#include <machine/frame.h> 44#include <machine/intr.h> 45#include <arm/at91/at91var.h> 46#include <arm/at91/at91_streg.h> 47#include <arm/at91/at91rm92reg.h> 48 49static struct at91_st_softc { 50 struct resource * sc_irq_res; 51 struct resource * sc_mem_res; 52 void * sc_intrhand; 53 eventhandler_tag sc_wet; /* watchdog event handler tag */ 54} *timer_softc; 55 56static inline uint32_t 57RD4(bus_size_t off) 58{ 59 60 if (timer_softc == NULL) { 61 uint32_t *p = (uint32_t *)(AT91_BASE + AT91RM92_ST_BASE + off); 62 63 return *p; 64 } 65 66 return (bus_read_4(timer_softc->sc_mem_res, off)); 67} 68 69static inline void 70WR4(bus_size_t off, uint32_t val) 71{ 72 73 if (timer_softc == NULL) { 74 uint32_t *p = (uint32_t *)(AT91_BASE + AT91RM92_ST_BASE + off); 75 76 *p = val; 77 } 78 else 79 bus_write_4(timer_softc->sc_mem_res, off, val); 80} 81 82static void at91_st_watchdog(void *, u_int, int *); 83static void at91_st_initclocks(device_t , struct at91_st_softc *); 84 85static inline int 86st_crtr(void) 87{ 88 int cur1, cur2; 89 do { 90 cur1 = RD4(ST_CRTR); 91 cur2 = RD4(ST_CRTR); 92 } while (cur1 != cur2); 93 return (cur1); 94} 95 96static unsigned at91_st_get_timecount(struct timecounter *tc); 97 98static struct timecounter at91_st_timecounter = { 99 at91_st_get_timecount, /* get_timecount */ 100 NULL, /* no poll_pps */ 101 0xfffffu, /* counter_mask */ 102 32768, /* frequency */ 103 "AT91RM9200 timer", /* name */ 104 1000 /* quality */ 105}; 106 107static int 108clock_intr(void *arg) 109{ 110 struct trapframe *fp = arg; 111 112 /* The interrupt is shared, so we have to make sure it's for us. */ 113 if (RD4(ST_SR) & ST_SR_PITS) { 114 hardclock(TRAPF_USERMODE(fp), TRAPF_PC(fp)); 115 return (FILTER_HANDLED); 116 } 117 return (FILTER_STRAY); 118} 119 120void 121at91_st_delay(int n) 122{ 123 uint32_t start, end, cur; 124 125 start = st_crtr(); 126 n = (n * 1000) / 32768; 127 if (n <= 0) 128 n = 1; 129 end = (start + n) & ST_CRTR_MASK; 130 cur = start; 131 if (start > end) { 132 while (cur >= start || cur < end) 133 cur = st_crtr(); 134 } else { 135 while (cur < end) 136 cur = st_crtr(); 137 } 138} 139 140void 141at91_st_cpu_reset(void) 142{ 143 /* 144 * Reset the CPU by programmig the watchdog timer to reset the 145 * CPU after 128 'slow' clocks, or about ~4ms. Loop until 146 * the reset happens for safety. 147 */ 148 WR4(ST_WDMR, ST_WDMR_RSTEN | 2); 149 WR4(ST_CR, ST_CR_WDRST); 150 while (1) 151 continue; 152} 153 154static int 155at91_st_probe(device_t dev) 156{ 157 158 device_set_desc(dev, "ST"); 159 return (0); 160} 161 162static void 163at91_st_deactivate(device_t dev) 164{ 165 struct at91_st_softc *sc = timer_softc; 166 167 if (sc->sc_intrhand) 168 bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); 169 sc->sc_intrhand = NULL; 170 171 if (sc->sc_irq_res) 172 bus_release_resource(dev, SYS_RES_IRQ, 173 rman_get_rid(sc->sc_irq_res), sc->sc_irq_res); 174 sc->sc_irq_res = NULL; 175 176 if (sc->sc_mem_res) 177 bus_release_resource(dev, SYS_RES_MEMORY, 178 rman_get_rid(sc->sc_mem_res), sc->sc_mem_res); 179 sc->sc_mem_res = NULL; 180} 181 182static int 183at91_st_activate(device_t dev) 184{ 185 int rid; 186 int err; 187 struct at91_st_softc *sc = timer_softc; 188 189 rid = 0; 190 sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 191 RF_ACTIVE); 192 err = ENOMEM; 193 if (sc->sc_mem_res == NULL) 194 goto out; 195 /* Disable all interrupts */ 196 WR4(ST_IDR, 0xffffffff); 197 198 /* The system timer shares the system irq (1) */ 199 rid = 0; 200 sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 201 RF_ACTIVE | RF_SHAREABLE); 202 if (sc->sc_irq_res == NULL) { 203 printf("Unable to allocate irq for the system timer"); 204 goto out; 205 } 206 err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_CLK, clock_intr, 207 NULL, NULL, &sc->sc_intrhand); 208out: 209 if (err != 0) 210 at91_st_deactivate(dev); 211 return (err); 212} 213 214static int 215at91_st_attach(device_t dev) 216{ 217 int err; 218 219 timer_softc = device_get_softc(dev); 220 err = at91_st_activate(dev); 221 if (err) 222 return err; 223 224 timer_softc->sc_wet = EVENTHANDLER_REGISTER(watchdog_list, 225 at91_st_watchdog, dev, 0); 226 227 device_printf(dev, 228 "watchdog registered, timeout intervall max. 64 sec\n"); 229 230 at91_st_initclocks(dev, timer_softc); 231 return (0); 232} 233 234static device_method_t at91_st_methods[] = { 235 DEVMETHOD(device_probe, at91_st_probe), 236 DEVMETHOD(device_attach, at91_st_attach), 237 {0, 0}, 238}; 239 240static driver_t at91_st_driver = { 241 "at91_st", 242 at91_st_methods, 243 sizeof(struct at91_st_softc), 244}; 245static devclass_t at91_st_devclass; 246 247DRIVER_MODULE(at91_st, atmelarm, at91_st_driver, at91_st_devclass, 0, 0); 248 249static unsigned 250at91_st_get_timecount(struct timecounter *tc) 251{ 252 return (st_crtr()); 253} 254 255/* 256 * t below is in a weird unit. The watchdog is set to 2^t 257 * nanoseconds. Since our watchdog timer can't really do that too 258 * well, we approximate it by assuming that the timeout interval for 259 * the lsb is 2^22 ns, which is 4.194ms. This is an overestimation of 260 * the actual time (3.906ms), but close enough for watchdogging. 261 * These approximations, though a violation of the spec, improve the 262 * performance of the application which typically specifies things as 263 * WD_TO_32SEC. In that last case, we'd wait 32s before the wdog 264 * reset. The spec says we should wait closer to 34s, but given how 265 * it is likely to be used, and the extremely coarse nature time 266 * interval, I think this is the best solution. 267 */ 268static void 269at91_st_watchdog(void *argp, u_int cmd, int *error) 270{ 271 uint32_t wdog; 272 int t; 273 274 t = cmd & WD_INTERVAL; 275 if (t >= 22 && t <= 37) { 276 wdog = (1 << (t - 22)) | ST_WDMR_RSTEN; 277 *error = 0; 278 } else { 279 wdog = 0; 280 } 281 WR4(ST_WDMR, wdog); 282 WR4(ST_CR, ST_CR_WDRST); 283} 284 285static void 286at91_st_initclocks(device_t dev, struct at91_st_softc *sc) 287{ 288 int rel_value; 289 290 /* 291 * Real time counter increments every clock cycle, need to set before 292 * initializing clocks so that DELAY works. 293 */ 294 WR4(ST_RTMR, 1); 295 /* disable watchdog timer */ 296 WR4(ST_WDMR, 0); 297 298 rel_value = 32768 / hz; 299 if (rel_value < 1) 300 rel_value = 1; 301 if (32768 % hz) { 302 device_printf(dev, "Cannot get %d Hz clock; using %dHz\n", hz, 303 32768 / rel_value); 304 hz = 32768 / rel_value; 305 tick = 1000000 / hz; 306 } 307 WR4(ST_PIMR, rel_value); 308 309 /* Enable PITS interrupts. */ 310 WR4(ST_IER, ST_SR_PITS); 311 tc_init(&at91_st_timecounter); 312} 313