Index: lib/libthr/Makefile =================================================================== --- lib/libthr/Makefile +++ lib/libthr/Makefile @@ -50,6 +50,10 @@ .include "${.CURDIR}/sys/Makefile.inc" .include "${.CURDIR}/thread/Makefile.inc" +.if defined(WITH_PLOCKSTAT) && ${DTRACE} == "dtrace" +SRCS+= plockstat.d +.endif + .if ${MK_INSTALLLIB} != "no" SYMLINKS+=lib${LIB}.a ${LIBDIR}/libpthread.a .endif Index: lib/libthr/thread/Makefile.inc =================================================================== --- lib/libthr/thread/Makefile.inc +++ lib/libthr/thread/Makefile.inc @@ -56,3 +56,7 @@ thr_symbols.c \ thr_umtx.c \ thr_yield.c + +.if defined(WITH_PLOCKSTAT) && ${DTRACE} == "dtrace" +SRCS+= thr_lockstat.c +.endif Index: lib/libthr/thread/thr_lockstat.h =================================================================== --- /dev/null +++ lib/libthr/thread/thr_lockstat.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Rui Paulo under sponsorship from the + * FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _THR_PLOCKSTAT_H +#define _THR_PLOCKSTAT_H + +#ifdef _DTRACE_VERSION + +#include "plockstat.h" + +void _thr_plockstat_mutex_acquire(void *, int, int); +void _thr_plockstat_mutex_release(void *, int); +void _thr_plockstat_mutex_block(void *); +void _thr_plockstat_mutex_spin(void *); +void _thr_plockstat_mutex_spun(void *, int, int); +void _thr_plockstat_mutex_blocked(void *, int); +void _thr_plockstat_mutex_error(void *, int); +void _thr_plockstat_rw_acquire(void *, int); +void _thr_plockstat_rw_release(void *, int); +void _thr_plockstat_rw_block(void *, int); +void _thr_plockstat_rw_blocked(void *, int, int); +void _thr_plockstat_rw_error(void *, int, int); + +#define THR_PLOCKSTAT_MUTEX_ACQUIRE(m, r, s) \ + _thr_plockstat_mutex_acquire((m), (r), (s)) +#define THR_PLOCKSTAT_MUTEX_RELEASE(m, r) \ + _thr_plockstat_mutex_release((m), (r)) +#define THR_PLOCKSTAT_MUTEX_BLOCK(m) \ + _thr_plockstat_mutex_block((m)) +#define THR_PLOCKSTAT_MUTEX_SPIN(m) \ + _thr_plockstat_mutex_spin((m)) +#define THR_PLOCKSTAT_MUTEX_SPUN(m, s, c) \ + _thr_plockstat_mutex_spun((m), (s), (c)) +#define THR_PLOCKSTAT_MUTEX_BLOCKED(m, s) \ + _thr_plockstat_mutex_blocked((m), (s)) +#define THR_PLOCKSTAT_MUTEX_ERROR(m, e) \ + _thr_plockstat_mutex_error((m), (e)) +#define THR_PLOCKSTAT_RW_ACQUIRE(l, w) \ + _thr_plockstat_rw_acquire((l), (w)) +#define THR_PLOCKSTAT_RW_RELEASE(l, w) \ + _thr_plockstat_rw_release((l), (w)) +#define THR_PLOCKSTAT_RW_BLOCK(l, w) \ + _thr_plockstat_rw_block((l), (w)) +#define THR_PLOCKSTAT_RW_BLOCKED(l, w, s) \ + _thr_plockstat_rw_blocked((l), (w), (s)) +#define THR_PLOCKSTAT_RW_ERROR(l, w, e) \ + _thr_plockstat_rw_error((l), (w), (e)) + +#else /* _DTRACE_VERSION */ + +#define THR_PLOCKSTAT_MUTEX_ACQUIRE(m, r, s) +#define THR_PLOCKSTAT_MUTEX_RELEASE(m, r) +#define THR_PLOCKSTAT_MUTEX_BLOCK(m) +#define THR_PLOCKSTAT_MUTEX_SPIN(m) +#define THR_PLOCKSTAT_MUTEX_SPUN(m, s, c) +#define THR_PLOCKSTAT_MUTEX_BLOCKED(m, s) +#define THR_PLOCKSTAT_MUTEX_ERROR(m, e) +#define THR_PLOCKSTAT_RW_ACQUIRE(l, w) +#define THR_PLOCKSTAT_RW_RELEASE(l, w) +#define THR_PLOCKSTAT_RW_BLOCK(l, w) +#define THR_PLOCKSTAT_RW_BLOCKED(l, w, s) +#define THR_PLOCKSTAT_RW_ERROR(l, w, e) + +#endif /* _DTRACE_VERSION */ +#endif /* _THR_PLOCKSTAT_H */ Index: lib/libthr/thread/thr_lockstat.c =================================================================== --- /dev/null +++ lib/libthr/thread/thr_lockstat.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Rui Paulo under sponsorship from the + * FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "thr_lockstat.h" +#include "plockstat.h" + +void +_thr_plockstat_mutex_acquire(void *mutex, int rec, int spincount) +{ + PLOCKSTAT_MUTEX_ACQUIRE(mutex, rec, spincount); +} + +void +_thr_plockstat_mutex_release(void *mutex, int rec) +{ + PLOCKSTAT_MUTEX_RELEASE(mutex, rec); +} + +void +_thr_plockstat_mutex_block(void *mutex) +{ + PLOCKSTAT_MUTEX_BLOCK(mutex); +} + +void +_thr_plockstat_mutex_spin(void *mutex) +{ + PLOCKSTAT_MUTEX_SPIN(mutex); +} + +void +_thr_plockstat_mutex_spun(void *mutex, int success, int spincount) +{ + PLOCKSTAT_MUTEX_SPUN(mutex, success, spincount); +} + +void +_thr_plockstat_mutex_blocked(void *mutex, int success) +{ + PLOCKSTAT_MUTEX_BLOCKED(mutex, success); +} + +void +_thr_plockstat_mutex_error(void *mutex, int err) +{ + PLOCKSTAT_MUTEX_ERROR(mutex, err); +} + +void +_thr_plockstat_rw_acquire(void *lock, int wr) +{ + PLOCKSTAT_RW_ACQUIRE(lock, wr); +} + +void +_thr_plockstat_rw_release(void *lock, int wr) +{ + PLOCKSTAT_RW_RELEASE(lock, wr); +} + +void +_thr_plockstat_rw_block(void *lock, int wr) +{ + PLOCKSTAT_RW_BLOCK(lock, wr); +} + +void +_thr_plockstat_rw_blocked(void *lock, int wr, int success) +{ + PLOCKSTAT_RW_BLOCKED(lock, wr, success); +} + +void +_thr_plockstat_rw_error(void *lock, int wr, int err) +{ + PLOCKSTAT_RW_ERROR(lock, wr, err); +} Index: lib/libthr/thread/thr_mutex.c =================================================================== --- lib/libthr/thread/thr_mutex.c +++ lib/libthr/thread/thr_mutex.c @@ -44,6 +44,7 @@ #include "un-namespace.h" #include "thr_private.h" +#include "thr_lockstat.h" #if defined(_PTHREADS_INVARIANTS) #define MUTEX_INIT_LINK(m) do { \ @@ -325,6 +326,7 @@ ret = _thr_umutex_trylock(&m->m_lock, id); if (__predict_true(ret == 0)) { ENQUEUE_MUTEX(curthread, m); + THR_PLOCKSTAT_MUTEX_ACQUIRE(m, 0, 0); } else if (m->m_owner == curthread) { ret = mutex_self_trylock(m); } /* else {} */ @@ -362,7 +364,7 @@ * faster than entering the kernel */ if (__predict_false( - (m->m_lock.m_flags & + (m->m_lock.m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) != 0)) goto sleep_in_kernel; @@ -375,9 +377,11 @@ if ((owner & ~UMUTEX_CONTESTED) == 0) { if (atomic_cmpset_acq_32(&m->m_lock.m_owner, owner, id|owner)) { ret = 0; + THR_PLOCKSTAT_MUTEX_SPUN(m, 1, count); goto done; } } + THR_PLOCKSTAT_MUTEX_SPIN(m); CPU_SPINWAIT; } @@ -389,24 +393,33 @@ if ((owner & ~UMUTEX_CONTESTED) == 0) { if (atomic_cmpset_acq_32(&m->m_lock.m_owner, owner, id|owner)) { ret = 0; + THR_PLOCKSTAT_MUTEX_SPUN(m, 1, count); goto done; } } + THR_PLOCKSTAT_MUTEX_SPIN(m); } sleep_in_kernel: if (abstime == NULL) { + THR_PLOCKSTAT_MUTEX_BLOCK(m); ret = __thr_umutex_lock(&m->m_lock, id); + THR_PLOCKSTAT_MUTEX_BLOCKED(m, !ret); } else if (__predict_false( abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)) { ret = EINVAL; + THR_PLOCKSTAT_MUTEX_ERROR(m, ret); } else { + THR_PLOCKSTAT_MUTEX_BLOCK(m); ret = __thr_umutex_timedlock(&m->m_lock, id, abstime); + THR_PLOCKSTAT_MUTEX_BLOCKED(m, !ret); } done: - if (ret == 0) + if (ret == 0) { ENQUEUE_MUTEX(curthread, m); + THR_PLOCKSTAT_MUTEX_ACQUIRE(m, 0, count); + } return (ret); } @@ -422,6 +435,7 @@ THR_CRITICAL_ENTER(curthread); if (_thr_umutex_trylock2(&m->m_lock, TID(curthread)) == 0) { ENQUEUE_MUTEX(curthread, m); + THR_PLOCKSTAT_MUTEX_ACQUIRE(m, 0, 0); ret = 0; } else { ret = mutex_lock_sleep(curthread, m, abstime); @@ -539,7 +553,7 @@ case PTHREAD_MUTEX_ERRORCHECK: case PTHREAD_MUTEX_NORMAL: case PTHREAD_MUTEX_ADAPTIVE_NP: - ret = EBUSY; + ret = EBUSY; break; case PTHREAD_MUTEX_RECURSIVE: @@ -583,7 +597,7 @@ * POSIX specifies that mutexes should return * EDEADLK if a recursive lock is detected. */ - ret = EDEADLK; + ret = EDEADLK; } break; @@ -644,8 +658,10 @@ /* * Check if the running thread is not the owner of the mutex. */ - if (__predict_false(m->m_owner != curthread)) + if (__predict_false(m->m_owner != curthread)) { + THR_PLOCKSTAT_MUTEX_ERROR(m, EPERM); return (EPERM); + } id = TID(curthread); if (__predict_false( @@ -661,6 +677,7 @@ DEQUEUE_MUTEX(curthread, m); _thr_umutex_unlock2(&m->m_lock, id, mtx_defer); + THR_PLOCKSTAT_MUTEX_RELEASE(m, 0); if (mtx_defer == NULL && defered) { _thr_wake_all(curthread->defer_waiters, @@ -793,5 +810,5 @@ } if (mp->m_owner != curthread) return (EPERM); - return (0); + return (0); } Index: lib/libthr/thread/thr_rwlock.c =================================================================== --- lib/libthr/thread/thr_rwlock.c +++ lib/libthr/thread/thr_rwlock.c @@ -34,6 +34,7 @@ #include #include "un-namespace.h" #include "thr_private.h" +#include "thr_lockstat.h" __weak_reference(_pthread_rwlock_destroy, pthread_rwlock_destroy); __weak_reference(_pthread_rwlock_init, pthread_rwlock_init); @@ -150,21 +151,30 @@ * POSIX said the validity of the abstimeout parameter need * not be checked if the lock can be immediately acquired. */ + THR_PLOCKSTAT_RW_BLOCK(&prwlock, 0); ret = _thr_rwlock_tryrdlock(&prwlock->lock, flags); + THR_PLOCKSTAT_RW_BLOCKED(&prwlock, 0, !ret); if (ret == 0) { curthread->rdlock_count++; + THR_PLOCKSTAT_RW_ACQUIRE(&prwlock, 0); return (ret); } if (__predict_false(abstime && - (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0))) + (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0))) { + THR_PLOCKSTAT_RW_ERROR(&prwlock, 0, EINVAL); return (EINVAL); + } for (;;) { /* goto kernel and lock it */ + THR_PLOCKSTAT_RW_BLOCK(&prwlock, 0); ret = __thr_rwlock_rdlock(&prwlock->lock, flags, abstime); - if (ret != EINTR) + THR_PLOCKSTAT_RW_BLOCKED(&prwlock, 0, !ret); + if (ret != EINTR) { + THR_PLOCKSTAT_RW_ERROR(&prwlock, 0, ret); break; + } /* if interrupted, try to lock it in userland again. */ if (_thr_rwlock_tryrdlock(&prwlock->lock, flags) == 0) { @@ -172,8 +182,10 @@ break; } } - if (ret == 0) + if (ret == 0) { curthread->rdlock_count++; + THR_PLOCKSTAT_RW_ACQUIRE(&prwlock, 0); + } return (ret); } @@ -218,9 +230,13 @@ flags = 0; } + THR_PLOCKSTAT_RW_BLOCK(&prwlock, 0); ret = _thr_rwlock_tryrdlock(&prwlock->lock, flags); - if (ret == 0) + THR_PLOCKSTAT_RW_BLOCKED(&prwlock, 0, !ret); + if (ret == 0) { + THR_PLOCKSTAT_RW_ACQUIRE(&prwlock, 0); curthread->rdlock_count++; + } return (ret); } @@ -233,9 +249,13 @@ CHECK_AND_INIT_RWLOCK + THR_PLOCKSTAT_RW_BLOCK(&prwlock, 1); ret = _thr_rwlock_trywrlock(&prwlock->lock); - if (ret == 0) + THR_PLOCKSTAT_RW_BLOCKED(&prwlock, 1, !ret); + if (ret == 0) { + THR_PLOCKSTAT_RW_ACQUIRE(&prwlock, 1); prwlock->owner = curthread; + } return (ret); } @@ -252,31 +272,42 @@ * POSIX said the validity of the abstimeout parameter need * not be checked if the lock can be immediately acquired. */ + THR_PLOCKSTAT_RW_BLOCK(&prwlock, 1); ret = _thr_rwlock_trywrlock(&prwlock->lock); + THR_PLOCKSTAT_RW_BLOCKED(&prwlock, 1, !ret); if (ret == 0) { prwlock->owner = curthread; + THR_PLOCKSTAT_RW_ACQUIRE(&prwlock, 1); return (ret); } if (__predict_false(abstime && - (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0))) + (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0))) { + THR_PLOCKSTAT_RW_ERROR(&prwlock, 1, EINVAL); return (EINVAL); + } for (;;) { /* goto kernel and lock it */ + THR_PLOCKSTAT_RW_BLOCK(&prwlock, 1); ret = __thr_rwlock_wrlock(&prwlock->lock, abstime); + THR_PLOCKSTAT_RW_BLOCKED(&prwlock, 1, !ret); if (ret == 0) { prwlock->owner = curthread; + THR_PLOCKSTAT_RW_ACQUIRE(&prwlock, 1); break; } - if (ret != EINTR) + if (ret != EINTR) { + THR_PLOCKSTAT_RW_ERROR(&prwlock, 1, ret); break; + } /* if interrupted, try to lock it in userland again. */ if (_thr_rwlock_trywrlock(&prwlock->lock) == 0) { ret = 0; prwlock->owner = curthread; + THR_PLOCKSTAT_RW_ACQUIRE(&prwlock, 1); break; } } @@ -311,14 +342,20 @@ state = prwlock->lock.rw_state; if (state & URWLOCK_WRITE_OWNER) { - if (__predict_false(prwlock->owner != curthread)) + if (__predict_false(prwlock->owner != curthread)) { + THR_PLOCKSTAT_RW_ERROR(&prwlock, 0, EPERM); return (EPERM); + } prwlock->owner = NULL; } + THR_PLOCKSTAT_RW_BLOCK(&prwlock, 0); ret = _thr_rwlock_unlock(&prwlock->lock); - if (ret == 0 && (state & URWLOCK_WRITE_OWNER) == 0) + THR_PLOCKSTAT_RW_BLOCKED(&prwlock, 0, !ret); + if (ret == 0 && (state & URWLOCK_WRITE_OWNER) == 0) { curthread->rdlock_count--; + THR_PLOCKSTAT_RW_RELEASE(&prwlock, 0); + } return (ret); } Index: lib/libthr/thread/thr_umtx.c =================================================================== --- lib/libthr/thread/thr_umtx.c +++ lib/libthr/thread/thr_umtx.c @@ -29,6 +29,7 @@ #include "thr_private.h" #include "thr_umtx.h" +#include "thr_lockstat.h" #ifndef HAS__UMTX_OP_ERR int _umtx_op_err(void *obj, int op, u_long val, void *uaddr, void *uaddr2) @@ -61,13 +62,16 @@ if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { for (;;) { + THR_PLOCKSTAT_MUTEX_BLOCK(mtx); /* wait in kernel */ _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, 0); owner = mtx->m_owner; if ((owner & ~UMUTEX_CONTESTED) == 0 && - atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner)) + atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner)) { + THR_PLOCKSTAT_MUTEX_ACQUIRE(mtx, 0, 0); return (0); + } } } @@ -100,7 +104,9 @@ } /* wait in kernel */ + THR_PLOCKSTAT_MUTEX_BLOCK(mtx); _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, 0); + THR_PLOCKSTAT_MUTEX_BLOCKED(mtx, 1); } } @@ -131,19 +137,27 @@ if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { /* wait in kernel */ + THR_PLOCKSTAT_MUTEX_BLOCK(mtx); ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, (void *)tm_size, __DECONST(void *, tm_p)); /* now try to lock it */ owner = mtx->m_owner; if ((owner & ~UMUTEX_CONTESTED) == 0 && - atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner)) + atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner)) { + THR_PLOCKSTAT_MUTEX_BLOCKED(mtx, 1); + THR_PLOCKSTAT_MUTEX_ACQUIRE(mtx, 0, 0); return (0); + } else + THR_PLOCKSTAT_MUTEX_BLOCKED(mtx, 0); + } else { ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, (void *)tm_size, __DECONST(void *, tm_p)); - if (ret == 0) + if (ret == 0) { + THR_PLOCKSTAT_MUTEX_ACQUIRE(mtx, 0, 0); break; + } } if (ret == ETIMEDOUT) break; Index: sys/arm/conf/BEAGLEBONE =================================================================== --- sys/arm/conf/BEAGLEBONE +++ sys/arm/conf/BEAGLEBONE @@ -108,6 +108,12 @@ # ADC support device ti_adc +# Watchdog support +# If we don't enable the watchdog driver, the system could potentially +# reboot automatically because the boot loader might have enabled the +# watchdog. +device ti_wdt + # USB support device usb options USB_HOST_ALIGN=64 # Align usb buffers to cache line size. Index: sys/arm/ti/files.ti =================================================================== --- sys/arm/ti/files.ti +++ sys/arm/ti/files.ti @@ -19,6 +19,8 @@ arm/ti/ti_mbox.c standard arm/ti/ti_pruss.c standard +arm/ti/ti_wdt.c optional ti_wdt + arm/ti/ti_adc.c optional ti_adc arm/ti/ti_gpio.c optional gpio arm/ti/ti_i2c.c optional ti_i2c Index: sys/arm/ti/ti_wdt.h =================================================================== --- /dev/null +++ sys/arm/ti/ti_wdt.h @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 2014 Rui Paulo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _TI_WDT_H_ +#define _TI_WDT_H_ + +/* TI WDT registers */ +#define TI_WDT_WIDR 0x00 /* Watchdog Identification Register */ +#define TI_WDT_WDSC 0x10 /* Watchdog System Control Register */ +#define TI_WDT_WDST 0x14 /* Watchdog Status Register */ +#define TI_WDT_WISR 0x18 /* Watchdog Interrupt Status Register */ +#define TI_WDT_WIER 0x1c /* Watchdog Interrupt Enable Register */ +#define TI_WDT_WCLR 0x24 /* Watchdog Control Register */ +#define TI_WDT_WCRR 0x28 /* Watchdog Counter Register */ +#define TI_WDT_WLDR 0x2c /* Watchdog Load Register */ +#define TI_WDT_WTGR 0x30 /* Watchdog Trigger Register */ +#define TI_WDT_WWPS 0x34 /* Watchdog Write Posting Register */ +#define TI_WDT_WDLY 0x44 /* Watchdog Delay Configuration Reg */ +#define TI_WDT_WSPR 0x48 /* Watchdog Start/Stop Register */ +#define TI_WDT_WIRQSTATRAW 0x54 /* Watchdog Raw Interrupt Status Reg. */ +#define TI_WDT_WIRQSTAT 0x58 /* Watchdog Int. Status Register */ +#define TI_WDT_WIRQENSET 0x5c /* Watchdog Int. Enable Set Register */ +#define TI_WDT_WIRQENCLR 0x60 /* Watchdog Int. Enable Clear Reg. */ + +/* WDT_WDSC Register */ +#define TI_WDSC_SR (1 << 1) /* Soft reset */ + +/* + * WDT_WWPS Register + * + * Writes to some registers require synchronisation with a different clock + * domain. The WDT_WWPS register is the place where this synchronisation + * happens. + */ +#define TI_W_PEND_WCLR (1 << 0) +#define TI_W_PEND_WCRR (1 << 1) +#define TI_W_PEND_WLDR (1 << 2) +#define TI_W_PEND_WTGR (1 << 3) +#define TI_W_PEND_WSPR (1 << 4) +#define TI_W_PEND_WDLY (1 << 5) + +/* WDT_WIRQENSET Register */ +#define TI_IRQ_EN_OVF (1 << 0) /* Overflow interrupt */ +#define TI_IRQ_EN_DLY (1 << 1) /* Delay interrupt */ + +/* WDT_WIRQSTAT Register */ +#define TI_IRQ_EV_OVF (1 << 0) /* Overflow event */ +#define TI_IRQ_EV_DLY (1 << 1) /* Delay event */ + +#endif /* _TI_WDT_H_ */ Index: sys/arm/ti/ti_wdt.c =================================================================== --- /dev/null +++ sys/arm/ti/ti_wdt.c @@ -0,0 +1,275 @@ +/*- + * Copyright (c) 2014 Rui Paulo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifdef DEBUG +#define DPRINTF(fmt, ...) do { \ + printf("%s: ", __func__); \ + printf(fmt, __VA_ARGS__); \ +} while (0) +#else +#define DPRINTF(fmt, ...) +#endif + +static device_probe_t ti_wdt_probe; +static device_attach_t ti_wdt_attach; +static device_detach_t ti_wdt_detach; +static void ti_wdt_intr(void *); +static void ti_wdt_event(void *, unsigned int, int *); + +struct ti_wdt_softc { + struct mtx sc_mtx; + struct resource *sc_mem_res; + struct resource *sc_irq_res; + void *sc_intr; + bus_space_tag_t sc_bt; + bus_space_handle_t sc_bh; + eventhandler_tag sc_ev_tag; +}; + +static device_method_t ti_wdt_methods[] = { + DEVMETHOD(device_probe, ti_wdt_probe), + DEVMETHOD(device_attach, ti_wdt_attach), + DEVMETHOD(device_detach, ti_wdt_detach), + + DEVMETHOD_END +}; + +static driver_t ti_wdt_driver = { + "ti_wdt", + ti_wdt_methods, + sizeof(struct ti_wdt_softc) +}; + +static devclass_t ti_wdt_devclass; + +DRIVER_MODULE(ti_wdt, simplebus, ti_wdt_driver, ti_wdt_devclass, 0, 0); + +static volatile __inline uint32_t +ti_wdt_reg_read(struct ti_wdt_softc *sc, uint32_t reg) +{ + return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); +} + +static __inline void +ti_wdt_reg_write(struct ti_wdt_softc *sc, uint32_t reg, uint32_t val) +{ + bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); +} + +/* + * Wait for the write to a specific synchronised register to complete. + */ +static __inline void +ti_wdt_reg_wait(struct ti_wdt_softc *sc, uint32_t bit) +{ + while (ti_wdt_reg_read(sc, TI_WDT_WWPS) & bit) + DELAY(10); + +} + +static __inline void +ti_wdt_disable(struct ti_wdt_softc *sc) +{ + DPRINTF("disabling watchdog %p\n", sc); + ti_wdt_reg_write(sc, TI_WDT_WSPR, 0xAAAA); + ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); + ti_wdt_reg_write(sc, TI_WDT_WSPR, 0x5555); + ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); +} + +static __inline void +ti_wdt_enable(struct ti_wdt_softc *sc) +{ + DPRINTF("enabling watchdog %p\n", sc); + ti_wdt_reg_write(sc, TI_WDT_WSPR, 0xBBBB); + ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); + ti_wdt_reg_write(sc, TI_WDT_WSPR, 0x4444); + ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); +} + +static int +ti_wdt_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + if (ofw_bus_is_compatible(dev, "ti,wdt")) { + device_set_desc(dev, "TI Watchdog Timer"); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +ti_wdt_attach(device_t dev) +{ + struct ti_wdt_softc *sc; + int rid; + + sc = device_get_softc(dev); + rid = 0; + mtx_init(&sc->sc_mtx, "TI WDT", NULL, MTX_DEF); + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->sc_mem_res == NULL) { + device_printf(dev, "could not allocate memory resource\n"); + return (ENXIO); + } + sc->sc_bt = rman_get_bustag(sc->sc_mem_res); + sc->sc_bh = rman_get_bushandle(sc->sc_mem_res); + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + device_printf(dev, "could not allocate interrupt resource\n"); + ti_wdt_detach(dev); + return (ENXIO); + } + if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC, + NULL, ti_wdt_intr, sc, &sc->sc_intr) != 0) { + device_printf(dev, + "unable to setup the interrupt handler\n"); + ti_wdt_detach(dev); + return (ENXIO); + } + /* Reset, enable interrupts and stop the watchdog. */ + ti_wdt_reg_write(sc, TI_WDT_WDSC, + ti_wdt_reg_read(sc, TI_WDT_WDSC) | TI_WDSC_SR); + while (ti_wdt_reg_read(sc, TI_WDT_WDSC) & TI_WDSC_SR) + DELAY(10); + ti_wdt_reg_write(sc, TI_WDT_WIRQENSET, TI_IRQ_EN_OVF | TI_IRQ_EN_DLY); + ti_wdt_disable(sc); + if (bootverbose) + device_printf(dev, "revision: 0x%x\n", + ti_wdt_reg_read(sc, TI_WDT_WIDR)); + sc->sc_ev_tag = EVENTHANDLER_REGISTER(watchdog_list, ti_wdt_event, sc, + 0); + + return (0); +} + +static int +ti_wdt_detach(device_t dev) +{ + struct ti_wdt_softc *sc; + + sc = device_get_softc(dev); + if (sc->sc_ev_tag) + EVENTHANDLER_DEREGISTER(watchdog_list, sc->sc_ev_tag); + if (sc->sc_intr) + bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr); + if (sc->sc_irq_res) + bus_release_resource(dev, SYS_RES_IRQ, + rman_get_rid(sc->sc_irq_res), sc->sc_irq_res); + if (sc->sc_mem_res) + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(sc->sc_mem_res), sc->sc_mem_res); + + return (0); +} + +static void +ti_wdt_intr(void *arg) +{ + struct ti_wdt_softc *sc; + + sc = arg; + DPRINTF("interrupt %p", sc); + ti_wdt_reg_write(sc, TI_WDT_WIRQSTAT, TI_IRQ_EV_OVF | TI_IRQ_EV_DLY); + /* TODO: handle interrupt */ +} + +static void +ti_wdt_event(void *arg, unsigned int cmd, int *error) +{ + struct ti_wdt_softc *sc; + uint8_t s; + uint32_t wldr; + uint32_t ptv; + + sc = arg; + ti_wdt_disable(sc); + if (cmd == WD_TO_NEVER) { + *error = 0; + return; + } + DPRINTF("cmd 0x%x\n", cmd); + cmd &= WD_INTERVAL; + if (cmd < WD_TO_1SEC) { + *error = EINVAL; + return; + } + s = 1 << (cmd - WD_TO_1SEC); + DPRINTF("seconds %u\n", s); + /* + * Leave the pre-scaler with its default values: + * PTV = 0 == 2**0 == 1 + * PRE = 1 (enabled) + * + * Compute the load register value assuming a 32kHz clock. + * See OVF_Rate in the WDT section of the AM335x TRM. + */ + ptv = 0; + wldr = 0xffffffff - (s * (32768 / (1 << ptv))) + 1; + DPRINTF("wldr 0x%x\n", wldr); + ti_wdt_reg_write(sc, TI_WDT_WLDR, wldr); + /* + * Trigger a timer reload. + */ + ti_wdt_reg_write(sc, TI_WDT_WTGR, + ti_wdt_reg_read(sc, TI_WDT_WTGR) + 1); + ti_wdt_reg_wait(sc, TI_W_PEND_WTGR); + ti_wdt_enable(sc); + *error = 0; +}