xref: /freebsd/sys/arm/ti/am335x/am335x_dmtpps.c (revision 3ce9b2ee9404381a002316df670939a3bd3c994f)
14159fbabSIan Lepore /*-
24159fbabSIan Lepore  * Copyright (c) 2015 Ian lepore <ian@freebsd.org>
34159fbabSIan Lepore  * All rights reserved.
44159fbabSIan Lepore  *
54159fbabSIan Lepore  * Redistribution and use in source and binary forms, with or without
64159fbabSIan Lepore  * modification, are permitted provided that the following conditions
74159fbabSIan Lepore  * are met:
84159fbabSIan Lepore  * 1. Redistributions of source code must retain the above copyright
94159fbabSIan Lepore  *    notice, this list of conditions and the following disclaimer.
104159fbabSIan Lepore  * 2. Redistributions in binary form must reproduce the above copyright
114159fbabSIan Lepore  *    notice, this list of conditions and the following disclaimer in the
124159fbabSIan Lepore  *    documentation and/or other materials provided with the distribution.
134159fbabSIan Lepore  *
144159fbabSIan Lepore  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
154159fbabSIan Lepore  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
164159fbabSIan Lepore  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
174159fbabSIan Lepore  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
184159fbabSIan Lepore  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
194159fbabSIan Lepore  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
204159fbabSIan Lepore  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
214159fbabSIan Lepore  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
224159fbabSIan Lepore  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
234159fbabSIan Lepore  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
244159fbabSIan Lepore  * SUCH DAMAGE.
254159fbabSIan Lepore  */
264159fbabSIan Lepore 
274159fbabSIan Lepore /*
284159fbabSIan Lepore  * AM335x PPS driver using DMTimer capture.
294159fbabSIan Lepore  *
304159fbabSIan Lepore  * Note that this PPS driver does not use an interrupt.  Instead it uses the
314159fbabSIan Lepore  * hardware's ability to latch the timer's count register in response to a
324159fbabSIan Lepore  * signal on an IO pin.  Each of timers 4-7 have an associated pin, and this
334159fbabSIan Lepore  * code allows any one of those to be used.
344159fbabSIan Lepore  *
354159fbabSIan Lepore  * The timecounter routines in kern_tc.c call the pps poll routine periodically
364159fbabSIan Lepore  * to see if a new counter value has been latched.  When a new value has been
374159fbabSIan Lepore  * latched, the only processing done in the poll routine is to capture the
384159fbabSIan Lepore  * current set of timecounter timehands (done with pps_capture()) and the
394159fbabSIan Lepore  * latched value from the timer.  The remaining work (done by pps_event() while
404159fbabSIan Lepore  * holding a mutex) is scheduled to be done later in a non-interrupt context.
414159fbabSIan Lepore  */
424159fbabSIan Lepore 
434159fbabSIan Lepore #include <sys/cdefs.h>
440050ea24SMichal Meloun #include "opt_platform.h"
450050ea24SMichal Meloun 
464159fbabSIan Lepore #include <sys/param.h>
474159fbabSIan Lepore #include <sys/systm.h>
484159fbabSIan Lepore #include <sys/bus.h>
494159fbabSIan Lepore #include <sys/conf.h>
504159fbabSIan Lepore #include <sys/kernel.h>
5153117a36SIan Lepore #include <sys/lock.h>
524159fbabSIan Lepore #include <sys/module.h>
534159fbabSIan Lepore #include <sys/malloc.h>
542d5913e4SIan Lepore #include <sys/mutex.h>
554159fbabSIan Lepore #include <sys/rman.h>
564159fbabSIan Lepore #include <sys/timepps.h>
574159fbabSIan Lepore #include <sys/timetc.h>
584159fbabSIan Lepore #include <machine/bus.h>
594159fbabSIan Lepore 
604159fbabSIan Lepore #include <dev/ofw/openfirm.h>
614159fbabSIan Lepore #include <dev/ofw/ofw_bus.h>
624159fbabSIan Lepore #include <dev/ofw/ofw_bus_subr.h>
63be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
644159fbabSIan Lepore 
650050ea24SMichal Meloun #include <arm/ti/ti_sysc.h>
664159fbabSIan Lepore #include <arm/ti/ti_pinmux.h>
674159fbabSIan Lepore #include <arm/ti/am335x/am335x_scm_padconf.h>
684159fbabSIan Lepore 
694159fbabSIan Lepore #include "am335x_dmtreg.h"
704159fbabSIan Lepore 
714159fbabSIan Lepore #define	PPS_CDEV_NAME	"dmtpps"
724159fbabSIan Lepore 
734159fbabSIan Lepore struct dmtpps_softc {
744159fbabSIan Lepore 	device_t		dev;
754159fbabSIan Lepore 	int			mem_rid;
764159fbabSIan Lepore 	struct resource *	mem_res;
774159fbabSIan Lepore 	int			tmr_num;	/* N from hwmod str "timerN" */
784159fbabSIan Lepore 	char			tmr_name[12];	/* "DMTimerN" */
794159fbabSIan Lepore 	uint32_t		tclr;		/* Cached TCLR register. */
804159fbabSIan Lepore 	struct timecounter	tc;
814159fbabSIan Lepore 	int			pps_curmode;	/* Edge mode now set in hw. */
824159fbabSIan Lepore 	struct cdev *		pps_cdev;
834159fbabSIan Lepore 	struct pps_state	pps_state;
844159fbabSIan Lepore 	struct mtx		pps_mtx;
850050ea24SMichal Meloun 	clk_t			clk_fck;
860050ea24SMichal Meloun 	uint64_t		sysclk_freq;
874159fbabSIan Lepore };
884159fbabSIan Lepore 
894159fbabSIan Lepore static int dmtpps_tmr_num;	/* Set by probe() */
904159fbabSIan Lepore 
914159fbabSIan Lepore /* List of compatible strings for FDT tree */
924159fbabSIan Lepore static struct ofw_compat_data compat_data[] = {
934159fbabSIan Lepore 	{"ti,am335x-timer",     1},
944159fbabSIan Lepore 	{"ti,am335x-timer-1ms", 1},
954159fbabSIan Lepore 	{NULL,                  0},
964159fbabSIan Lepore };
97519b64e2SMark Johnston SIMPLEBUS_PNP_INFO(compat_data);
984159fbabSIan Lepore 
994159fbabSIan Lepore /*
1004159fbabSIan Lepore  * A table relating pad names to the hardware timer number they can be mux'd to.
1014159fbabSIan Lepore  */
1024159fbabSIan Lepore struct padinfo {
1034159fbabSIan Lepore 	char *	ballname;
1044159fbabSIan Lepore 	int	tmr_num;
1054159fbabSIan Lepore };
1064159fbabSIan Lepore static struct padinfo dmtpps_padinfo[] = {
1074159fbabSIan Lepore 	{"GPMC_ADVn_ALE",    4},
1084159fbabSIan Lepore 	{"I2C0_SDA",         4},
1094159fbabSIan Lepore 	{"MII1_TX_EN",       4},
1104159fbabSIan Lepore 	{"XDMA_EVENT_INTR0", 4},
1114159fbabSIan Lepore 	{"GPMC_BEn0_CLE",    5},
1124159fbabSIan Lepore 	{"MDC",              5},
1134159fbabSIan Lepore 	{"MMC0_DAT3",        5},
1144159fbabSIan Lepore 	{"UART1_RTSn",       5},
1154159fbabSIan Lepore 	{"GPMC_WEn",         6},
1164159fbabSIan Lepore 	{"MDIO",             6},
1174159fbabSIan Lepore 	{"MMC0_DAT2",        6},
1184159fbabSIan Lepore 	{"UART1_CTSn",       6},
1194159fbabSIan Lepore 	{"GPMC_OEn_REn",     7},
1204159fbabSIan Lepore 	{"I2C0_SCL",         7},
1214159fbabSIan Lepore 	{"UART0_CTSn",       7},
1224159fbabSIan Lepore 	{"XDMA_EVENT_INTR1", 7},
1234159fbabSIan Lepore 	{NULL, 0}
1244159fbabSIan Lepore };
1254159fbabSIan Lepore 
1264159fbabSIan Lepore /*
1274159fbabSIan Lepore  * This is either brilliantly user-friendly, or utterly lame...
1284159fbabSIan Lepore  *
1294159fbabSIan Lepore  * The am335x chip is used on the popular Beaglebone boards.  Those boards have
1304159fbabSIan Lepore  * pins for all four capture-capable timers available on the P8 header. Allow
1314159fbabSIan Lepore  * users to configure the input pin by giving the name of the header pin.
1324159fbabSIan Lepore  */
1334159fbabSIan Lepore struct nicknames {
1344159fbabSIan Lepore 	const char * nick;
1354159fbabSIan Lepore 	const char * name;
1364159fbabSIan Lepore };
1374159fbabSIan Lepore static struct nicknames dmtpps_pin_nicks[] = {
1384159fbabSIan Lepore 	{"P8-7",  "GPMC_ADVn_ALE"},
1394159fbabSIan Lepore 	{"P8-9",  "GPMC_BEn0_CLE"},
1404159fbabSIan Lepore 	{"P8-10", "GPMC_WEn"},
1414159fbabSIan Lepore 	{"P8-8",  "GPMC_OEn_REn",},
1424159fbabSIan Lepore 	{NULL, NULL}
1434159fbabSIan Lepore };
1444159fbabSIan Lepore 
1454159fbabSIan Lepore #define	DMTIMER_READ4(sc, reg)		bus_read_4((sc)->mem_res, (reg))
1464159fbabSIan Lepore #define	DMTIMER_WRITE4(sc, reg, val)	bus_write_4((sc)->mem_res, (reg), (val))
1474159fbabSIan Lepore 
1484159fbabSIan Lepore /*
1494159fbabSIan Lepore  * Translate a short friendly case-insensitive name to its canonical name.
1504159fbabSIan Lepore  */
1514159fbabSIan Lepore static const char *
dmtpps_translate_nickname(const char * nick)1524159fbabSIan Lepore dmtpps_translate_nickname(const char *nick)
1534159fbabSIan Lepore {
1544159fbabSIan Lepore 	struct nicknames *nn;
1554159fbabSIan Lepore 
1564159fbabSIan Lepore 	for (nn = dmtpps_pin_nicks; nn->nick != NULL; nn++)
1574159fbabSIan Lepore 		if (strcasecmp(nick, nn->nick) == 0)
1584159fbabSIan Lepore 			return nn->name;
1594159fbabSIan Lepore 	return (nick);
1604159fbabSIan Lepore }
1614159fbabSIan Lepore 
1624159fbabSIan Lepore /*
1634159fbabSIan Lepore  * See if our tunable is set to the name of the input pin.  If not, that's NOT
1644159fbabSIan Lepore  * an error, return 0.  If so, try to configure that pin as a timer capture
1654159fbabSIan Lepore  * input pin, and if that works, then we have our timer unit number and if it
1664159fbabSIan Lepore  * fails that IS an error, return -1.
1674159fbabSIan Lepore  */
1684159fbabSIan Lepore static int
dmtpps_find_tmr_num_by_tunable(void)16959249a51SAndrew Turner dmtpps_find_tmr_num_by_tunable(void)
1704159fbabSIan Lepore {
1714159fbabSIan Lepore 	struct padinfo *pi;
1724159fbabSIan Lepore 	char iname[20];
1734159fbabSIan Lepore 	char muxmode[12];
1744159fbabSIan Lepore 	const char * ballname;
1754159fbabSIan Lepore 	int err;
1764159fbabSIan Lepore 
1774159fbabSIan Lepore 	if (!TUNABLE_STR_FETCH("hw.am335x_dmtpps.input", iname, sizeof(iname)))
1784159fbabSIan Lepore 		return (0);
1794159fbabSIan Lepore 	ballname = dmtpps_translate_nickname(iname);
1804159fbabSIan Lepore 	for (pi = dmtpps_padinfo; pi->ballname != NULL; pi++) {
1814159fbabSIan Lepore 		if (strcmp(ballname, pi->ballname) != 0)
1824159fbabSIan Lepore 			continue;
1834159fbabSIan Lepore 		snprintf(muxmode, sizeof(muxmode), "timer%d", pi->tmr_num);
1844159fbabSIan Lepore 		err = ti_pinmux_padconf_set(pi->ballname, muxmode,
1854159fbabSIan Lepore 		    PADCONF_INPUT);
1864159fbabSIan Lepore 		if (err != 0) {
1874159fbabSIan Lepore 			printf("am335x_dmtpps: unable to configure capture pin "
1884159fbabSIan Lepore 			    "for %s to input mode\n", muxmode);
1894159fbabSIan Lepore 			return (-1);
1904159fbabSIan Lepore 		} else if (bootverbose) {
1914159fbabSIan Lepore 			printf("am335x_dmtpps: configured pin %s as input "
1924159fbabSIan Lepore 			    "for %s\n", iname, muxmode);
1934159fbabSIan Lepore 		}
1944159fbabSIan Lepore 		return (pi->tmr_num);
1954159fbabSIan Lepore 	}
1964159fbabSIan Lepore 
1974159fbabSIan Lepore 	/* Invalid name in the tunable, that's an error. */
1984159fbabSIan Lepore 	printf("am335x_dmtpps: unknown pin name '%s'\n", iname);
1994159fbabSIan Lepore 	return (-1);
2004159fbabSIan Lepore }
2014159fbabSIan Lepore 
2024159fbabSIan Lepore /*
2034159fbabSIan Lepore  * Ask the pinmux driver whether any pin has been configured as a TIMER4..TIMER7
2044159fbabSIan Lepore  * input pin.  If so, return the timer number, if not return 0.
2054159fbabSIan Lepore  */
2064159fbabSIan Lepore static int
dmtpps_find_tmr_num_by_padconf(void)20759249a51SAndrew Turner dmtpps_find_tmr_num_by_padconf(void)
2084159fbabSIan Lepore {
2094159fbabSIan Lepore 	int err;
2104159fbabSIan Lepore 	unsigned int padstate;
2114159fbabSIan Lepore 	const char * padmux;
2124159fbabSIan Lepore 	struct padinfo *pi;
2134159fbabSIan Lepore 	char muxmode[12];
2144159fbabSIan Lepore 
2154159fbabSIan Lepore 	for (pi = dmtpps_padinfo; pi->ballname != NULL; pi++) {
2164159fbabSIan Lepore 		err = ti_pinmux_padconf_get(pi->ballname, &padmux, &padstate);
2174159fbabSIan Lepore 		snprintf(muxmode, sizeof(muxmode), "timer%d", pi->tmr_num);
2184159fbabSIan Lepore 		if (err == 0 && (padstate & RXACTIVE) != 0 &&
2194159fbabSIan Lepore 		    strcmp(muxmode, padmux) == 0)
2204159fbabSIan Lepore 			return (pi->tmr_num);
2214159fbabSIan Lepore 	}
2224159fbabSIan Lepore 	/* Nothing found, not an error. */
2234159fbabSIan Lepore 	return (0);
2244159fbabSIan Lepore }
2254159fbabSIan Lepore 
2264159fbabSIan Lepore /*
2274159fbabSIan Lepore  * Figure out which hardware timer number to use based on input pin
2284159fbabSIan Lepore  * configuration.  This is done just once, the first time probe() runs.
2294159fbabSIan Lepore  */
2304159fbabSIan Lepore static int
dmtpps_find_tmr_num(void)23159249a51SAndrew Turner dmtpps_find_tmr_num(void)
2324159fbabSIan Lepore {
2334159fbabSIan Lepore 	int tmr_num;
2344159fbabSIan Lepore 
2354159fbabSIan Lepore 	if ((tmr_num = dmtpps_find_tmr_num_by_tunable()) == 0)
2364159fbabSIan Lepore 		tmr_num = dmtpps_find_tmr_num_by_padconf();
2374159fbabSIan Lepore 
2384159fbabSIan Lepore 	if (tmr_num <= 0) {
2394159fbabSIan Lepore 		printf("am335x_dmtpps: PPS driver not enabled: unable to find "
2404159fbabSIan Lepore 		    "or configure a capture input pin\n");
2414159fbabSIan Lepore 		tmr_num = -1; /* Must return non-zero to prevent re-probing. */
2424159fbabSIan Lepore 	}
2434159fbabSIan Lepore 	return (tmr_num);
2444159fbabSIan Lepore }
2454159fbabSIan Lepore 
2464159fbabSIan Lepore static void
dmtpps_set_hw_capture(struct dmtpps_softc * sc,bool force_off)2474159fbabSIan Lepore dmtpps_set_hw_capture(struct dmtpps_softc *sc, bool force_off)
2484159fbabSIan Lepore {
2494159fbabSIan Lepore 	int newmode;
2504159fbabSIan Lepore 
2514159fbabSIan Lepore 	if (force_off)
2524159fbabSIan Lepore 		newmode = 0;
2534159fbabSIan Lepore 	else
2544159fbabSIan Lepore 		newmode = sc->pps_state.ppsparam.mode & PPS_CAPTUREASSERT;
2554159fbabSIan Lepore 
2564159fbabSIan Lepore 	if (newmode == sc->pps_curmode)
2574159fbabSIan Lepore 		return;
2584159fbabSIan Lepore 	sc->pps_curmode = newmode;
2594159fbabSIan Lepore 
2604159fbabSIan Lepore 	if (newmode == PPS_CAPTUREASSERT)
2614159fbabSIan Lepore 		sc->tclr |= DMT_TCLR_CAPTRAN_LOHI;
2624159fbabSIan Lepore 	else
2634159fbabSIan Lepore 		sc->tclr &= ~DMT_TCLR_CAPTRAN_MASK;
2644159fbabSIan Lepore 	DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr);
2654159fbabSIan Lepore }
2664159fbabSIan Lepore 
2674159fbabSIan Lepore static unsigned
dmtpps_get_timecount(struct timecounter * tc)2684159fbabSIan Lepore dmtpps_get_timecount(struct timecounter *tc)
2694159fbabSIan Lepore {
2704159fbabSIan Lepore 	struct dmtpps_softc *sc;
2714159fbabSIan Lepore 
2724159fbabSIan Lepore 	sc = tc->tc_priv;
2734159fbabSIan Lepore 
2744159fbabSIan Lepore 	return (DMTIMER_READ4(sc, DMT_TCRR));
2754159fbabSIan Lepore }
2764159fbabSIan Lepore 
2774159fbabSIan Lepore static void
dmtpps_poll(struct timecounter * tc)2784159fbabSIan Lepore dmtpps_poll(struct timecounter *tc)
2794159fbabSIan Lepore {
2804159fbabSIan Lepore 	struct dmtpps_softc *sc;
2814159fbabSIan Lepore 
2824159fbabSIan Lepore 	sc = tc->tc_priv;
2834159fbabSIan Lepore 
2844159fbabSIan Lepore 	/*
2854159fbabSIan Lepore 	 * If a new value has been latched we've got a PPS event.  Capture the
2864159fbabSIan Lepore 	 * timecounter data, then override the capcount field (pps_capture()
2874159fbabSIan Lepore 	 * populates it from the current DMT_TCRR register) with the latched
2884159fbabSIan Lepore 	 * value from the TCAR1 register.
2894159fbabSIan Lepore 	 *
2904159fbabSIan Lepore 	 * Note that we don't have the TCAR interrupt enabled, but the hardware
2914159fbabSIan Lepore 	 * still provides the status bits in the "RAW" status register even when
2924159fbabSIan Lepore 	 * they're masked from generating an irq.  However, when clearing the
2934159fbabSIan Lepore 	 * TCAR status to re-arm the capture for the next second, we have to
2944159fbabSIan Lepore 	 * write to the IRQ status register, not the RAW register.  Quirky.
295192122bdSIan Lepore 	 *
296192122bdSIan Lepore 	 * We do not need to hold a lock while capturing the pps data, because
297192122bdSIan Lepore 	 * it is captured into an area of the pps_state struct which is read
298192122bdSIan Lepore 	 * only by pps_event().  We do need to hold a lock while calling
299192122bdSIan Lepore 	 * pps_event(), because it manipulates data which is also accessed from
300192122bdSIan Lepore 	 * the ioctl(2) context by userland processes.
3014159fbabSIan Lepore 	 */
3024159fbabSIan Lepore 	if (DMTIMER_READ4(sc, DMT_IRQSTATUS_RAW) & DMT_IRQ_TCAR) {
3034159fbabSIan Lepore 		pps_capture(&sc->pps_state);
3044159fbabSIan Lepore 		sc->pps_state.capcount = DMTIMER_READ4(sc, DMT_TCAR1);
3054159fbabSIan Lepore 		DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_TCAR);
3064159fbabSIan Lepore 
307192122bdSIan Lepore 		mtx_lock_spin(&sc->pps_mtx);
3084159fbabSIan Lepore 		pps_event(&sc->pps_state, PPS_CAPTUREASSERT);
309192122bdSIan Lepore 		mtx_unlock_spin(&sc->pps_mtx);
310192122bdSIan Lepore 	}
3114159fbabSIan Lepore }
3124159fbabSIan Lepore 
3134159fbabSIan Lepore static int
dmtpps_open(struct cdev * dev,int flags,int fmt,struct thread * td)3144159fbabSIan Lepore dmtpps_open(struct cdev *dev, int flags, int fmt,
3154159fbabSIan Lepore     struct thread *td)
3164159fbabSIan Lepore {
3174159fbabSIan Lepore 	struct dmtpps_softc *sc;
3184159fbabSIan Lepore 
3194159fbabSIan Lepore 	sc = dev->si_drv1;
3204159fbabSIan Lepore 
3214159fbabSIan Lepore 	/*
3224159fbabSIan Lepore 	 * Begin polling for pps and enable capture in the hardware whenever the
3234159fbabSIan Lepore 	 * device is open.  Doing this stuff again is harmless if this isn't the
3244159fbabSIan Lepore 	 * first open.
3254159fbabSIan Lepore 	 */
3264159fbabSIan Lepore 	sc->tc.tc_poll_pps = dmtpps_poll;
3274159fbabSIan Lepore 	dmtpps_set_hw_capture(sc, false);
3284159fbabSIan Lepore 
3294159fbabSIan Lepore 	return 0;
3304159fbabSIan Lepore }
3314159fbabSIan Lepore 
3324159fbabSIan Lepore static	int
dmtpps_close(struct cdev * dev,int flags,int fmt,struct thread * td)3334159fbabSIan Lepore dmtpps_close(struct cdev *dev, int flags, int fmt,
3344159fbabSIan Lepore     struct thread *td)
3354159fbabSIan Lepore {
3364159fbabSIan Lepore 	struct dmtpps_softc *sc;
3374159fbabSIan Lepore 
3384159fbabSIan Lepore 	sc = dev->si_drv1;
3394159fbabSIan Lepore 
3404159fbabSIan Lepore 	/*
3414159fbabSIan Lepore 	 * Stop polling and disable capture on last close.  Use the force-off
3424159fbabSIan Lepore 	 * flag to override the configured mode and turn off the hardware.
3434159fbabSIan Lepore 	 */
3444159fbabSIan Lepore 	sc->tc.tc_poll_pps = NULL;
3454159fbabSIan Lepore 	dmtpps_set_hw_capture(sc, true);
3464159fbabSIan Lepore 
3474159fbabSIan Lepore 	return 0;
3484159fbabSIan Lepore }
3494159fbabSIan Lepore 
3504159fbabSIan Lepore static int
dmtpps_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int flags,struct thread * td)3514159fbabSIan Lepore dmtpps_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
3524159fbabSIan Lepore     int flags, struct thread *td)
3534159fbabSIan Lepore {
3544159fbabSIan Lepore 	struct dmtpps_softc *sc;
3554159fbabSIan Lepore 	int err;
3564159fbabSIan Lepore 
3574159fbabSIan Lepore 	sc = dev->si_drv1;
3584159fbabSIan Lepore 
3594159fbabSIan Lepore 	/* Let the kernel do the heavy lifting for ioctl. */
360192122bdSIan Lepore 	mtx_lock_spin(&sc->pps_mtx);
3614159fbabSIan Lepore 	err = pps_ioctl(cmd, data, &sc->pps_state);
362192122bdSIan Lepore 	mtx_unlock_spin(&sc->pps_mtx);
3634159fbabSIan Lepore 	if (err != 0)
3644159fbabSIan Lepore 		return (err);
3654159fbabSIan Lepore 
3664159fbabSIan Lepore 	/*
3674159fbabSIan Lepore 	 * The capture mode could have changed, set the hardware to whatever
3684159fbabSIan Lepore 	 * mode is now current.  Effectively a no-op if nothing changed.
3694159fbabSIan Lepore 	 */
3704159fbabSIan Lepore 	dmtpps_set_hw_capture(sc, false);
3714159fbabSIan Lepore 
3724159fbabSIan Lepore 	return (err);
3734159fbabSIan Lepore }
3744159fbabSIan Lepore 
3754159fbabSIan Lepore static struct cdevsw dmtpps_cdevsw = {
3764159fbabSIan Lepore 	.d_version =    D_VERSION,
3774159fbabSIan Lepore 	.d_open =       dmtpps_open,
3784159fbabSIan Lepore 	.d_close =      dmtpps_close,
3794159fbabSIan Lepore 	.d_ioctl =      dmtpps_ioctl,
3804159fbabSIan Lepore 	.d_name =       PPS_CDEV_NAME,
3814159fbabSIan Lepore };
3824159fbabSIan Lepore 
3834159fbabSIan Lepore static int
dmtpps_probe(device_t dev)3844159fbabSIan Lepore dmtpps_probe(device_t dev)
3854159fbabSIan Lepore {
3864159fbabSIan Lepore 	int tmr_num;
3870050ea24SMichal Meloun 	uint64_t rev_address;
3884159fbabSIan Lepore 
3894159fbabSIan Lepore 	if (!ofw_bus_status_okay(dev))
3904159fbabSIan Lepore 		return (ENXIO);
3914159fbabSIan Lepore 
3924159fbabSIan Lepore 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
3934159fbabSIan Lepore 		return (ENXIO);
3944159fbabSIan Lepore 
3954159fbabSIan Lepore 	/*
3964159fbabSIan Lepore 	 * If we haven't chosen which hardware timer to use yet, go do that now.
3974159fbabSIan Lepore 	 * We need to know that to decide whether to return success for this
3984159fbabSIan Lepore 	 * hardware timer instance or not.
3994159fbabSIan Lepore 	 */
4004159fbabSIan Lepore 	if (dmtpps_tmr_num == 0)
4014159fbabSIan Lepore 		dmtpps_tmr_num = dmtpps_find_tmr_num();
4024159fbabSIan Lepore 
4034159fbabSIan Lepore 	/*
4044159fbabSIan Lepore 	 * Figure out which hardware timer is being probed and see if it matches
4054159fbabSIan Lepore 	 * the configured timer number determined earlier.
4064159fbabSIan Lepore 	 */
4070050ea24SMichal Meloun 	rev_address = ti_sysc_get_rev_address(device_get_parent(dev));
4080050ea24SMichal Meloun 	switch (rev_address) {
4090050ea24SMichal Meloun 		case DMTIMER1_1MS_REV:
4100050ea24SMichal Meloun 			tmr_num = 1;
4110050ea24SMichal Meloun 			break;
4120050ea24SMichal Meloun 		case DMTIMER2_REV:
4130050ea24SMichal Meloun 			tmr_num = 2;
4140050ea24SMichal Meloun 			break;
4150050ea24SMichal Meloun 		case DMTIMER3_REV:
4160050ea24SMichal Meloun 			tmr_num = 3;
4170050ea24SMichal Meloun 			break;
4180050ea24SMichal Meloun 		case DMTIMER4_REV:
4190050ea24SMichal Meloun 			tmr_num = 4;
4200050ea24SMichal Meloun 			break;
4210050ea24SMichal Meloun 		case DMTIMER5_REV:
4220050ea24SMichal Meloun 			tmr_num = 5;
4230050ea24SMichal Meloun 			break;
4240050ea24SMichal Meloun 		case DMTIMER6_REV:
4250050ea24SMichal Meloun 			tmr_num = 6;
4260050ea24SMichal Meloun 			break;
4270050ea24SMichal Meloun 		case DMTIMER7_REV:
4280050ea24SMichal Meloun 			tmr_num = 7;
4290050ea24SMichal Meloun 			break;
4300050ea24SMichal Meloun 		default:
4310050ea24SMichal Meloun 			return (ENXIO);
4320050ea24SMichal Meloun         }
4330050ea24SMichal Meloun 
4344159fbabSIan Lepore 	if (dmtpps_tmr_num != tmr_num)
4354159fbabSIan Lepore 		return (ENXIO);
4364159fbabSIan Lepore 
437*3ce9b2eeSMark Johnston 	device_set_descf(dev, "AM335x PPS-Capture DMTimer%d", tmr_num);
4384159fbabSIan Lepore 
4394159fbabSIan Lepore 	return(BUS_PROBE_DEFAULT);
4404159fbabSIan Lepore }
4414159fbabSIan Lepore 
4424159fbabSIan Lepore static int
dmtpps_attach(device_t dev)4434159fbabSIan Lepore dmtpps_attach(device_t dev)
4444159fbabSIan Lepore {
4454159fbabSIan Lepore 	struct dmtpps_softc *sc;
446192122bdSIan Lepore 	struct make_dev_args mda;
4470050ea24SMichal Meloun 	int err;
4480050ea24SMichal Meloun 	clk_t sys_clkin;
4490050ea24SMichal Meloun 	uint64_t rev_address;
4504159fbabSIan Lepore 
4514159fbabSIan Lepore 	sc = device_get_softc(dev);
4524159fbabSIan Lepore 	sc->dev = dev;
4534159fbabSIan Lepore 
4540050ea24SMichal Meloun 	/* Figure out which hardware timer this is and set the name string. */
4550050ea24SMichal Meloun 	rev_address = ti_sysc_get_rev_address(device_get_parent(dev));
4560050ea24SMichal Meloun 	switch (rev_address) {
4570050ea24SMichal Meloun 		case DMTIMER1_1MS_REV:
4580050ea24SMichal Meloun 			sc->tmr_num = 1;
4590050ea24SMichal Meloun 			break;
4600050ea24SMichal Meloun 		case DMTIMER2_REV:
4610050ea24SMichal Meloun 			sc->tmr_num = 2;
4620050ea24SMichal Meloun 			break;
4630050ea24SMichal Meloun 		case DMTIMER3_REV:
4640050ea24SMichal Meloun 			sc->tmr_num = 3;
4650050ea24SMichal Meloun 			break;
4660050ea24SMichal Meloun 		case DMTIMER4_REV:
4670050ea24SMichal Meloun 			sc->tmr_num = 4;
4680050ea24SMichal Meloun 			break;
4690050ea24SMichal Meloun 		case DMTIMER5_REV:
4700050ea24SMichal Meloun 			sc->tmr_num = 5;
4710050ea24SMichal Meloun 			break;
4720050ea24SMichal Meloun 		case DMTIMER6_REV:
4730050ea24SMichal Meloun 			sc->tmr_num = 6;
4740050ea24SMichal Meloun 			break;
4750050ea24SMichal Meloun 		case DMTIMER7_REV:
4760050ea24SMichal Meloun 			sc->tmr_num = 7;
4770050ea24SMichal Meloun 			break;
4780050ea24SMichal Meloun         }
4790050ea24SMichal Meloun 	snprintf(sc->tmr_name, sizeof(sc->tmr_name), "DMTimer%d", sc->tmr_num);
4800050ea24SMichal Meloun 
4810050ea24SMichal Meloun 	/* expect one clock */
4820050ea24SMichal Meloun 	err = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_fck);
4830050ea24SMichal Meloun 	if (err != 0) {
4840050ea24SMichal Meloun 		device_printf(dev, "Cant find clock index 0. err: %d\n", err);
4850050ea24SMichal Meloun 		return (ENXIO);
4860050ea24SMichal Meloun 	}
4870050ea24SMichal Meloun 
4880050ea24SMichal Meloun 	err = clk_get_by_name(dev, "sys_clkin_ck@40", &sys_clkin);
4890050ea24SMichal Meloun 	if (err != 0) {
4900050ea24SMichal Meloun 		device_printf(dev, "Cant find sys_clkin_ck@40 err: %d\n", err);
4910050ea24SMichal Meloun 		return (ENXIO);
4920050ea24SMichal Meloun 	}
4930050ea24SMichal Meloun 
4940050ea24SMichal Meloun 	/* Select M_OSC as DPLL parent */
4950050ea24SMichal Meloun 	err = clk_set_parent_by_clk(sc->clk_fck, sys_clkin);
4960050ea24SMichal Meloun 	if (err != 0) {
4970050ea24SMichal Meloun 		device_printf(dev, "Cant set mux to CLK_M_OSC\n");
4980050ea24SMichal Meloun 		return (ENXIO);
4990050ea24SMichal Meloun 	}
5004159fbabSIan Lepore 
5014159fbabSIan Lepore 	/* Enable clocks and power on the device. */
5020050ea24SMichal Meloun 	err = ti_sysc_clock_enable(device_get_parent(dev));
5030050ea24SMichal Meloun 	if (err != 0) {
5040050ea24SMichal Meloun 		device_printf(dev, "Cant enable sysc clkctrl, err %d\n", err);
5054159fbabSIan Lepore 		return (ENXIO);
5060050ea24SMichal Meloun 	}
5074159fbabSIan Lepore 
5080050ea24SMichal Meloun 	/* Get the base clock frequency. */
5090050ea24SMichal Meloun 	err = clk_get_freq(sc->clk_fck, &sc->sysclk_freq);
5100050ea24SMichal Meloun 	if (err != 0) {
5110050ea24SMichal Meloun 		device_printf(dev, "Cant get sysclk frequency, err %d\n", err);
5120050ea24SMichal Meloun 		return (ENXIO);
5130050ea24SMichal Meloun 	}
5144159fbabSIan Lepore 	/* Request the memory resources. */
5154159fbabSIan Lepore 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
5164159fbabSIan Lepore 	    &sc->mem_rid, RF_ACTIVE);
5174159fbabSIan Lepore 	if (sc->mem_res == NULL) {
5184159fbabSIan Lepore 		return (ENXIO);
5194159fbabSIan Lepore 	}
5204159fbabSIan Lepore 
52128b3a4a6SIan Lepore 	/*
52228b3a4a6SIan Lepore 	 * Configure the timer pulse/capture pin to input/capture mode.  This is
52328b3a4a6SIan Lepore 	 * required in addition to configuring the pin as input with the pinmux
52428b3a4a6SIan Lepore 	 * controller (which was done via fdt data or tunable at probe time).
52528b3a4a6SIan Lepore 	 */
52628b3a4a6SIan Lepore 	sc->tclr = DMT_TCLR_GPO_CFG;
52728b3a4a6SIan Lepore 	DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr);
52828b3a4a6SIan Lepore 
5294159fbabSIan Lepore 	/* Set up timecounter hardware, start it. */
5304159fbabSIan Lepore 	DMTIMER_WRITE4(sc, DMT_TSICR, DMT_TSICR_RESET);
5314159fbabSIan Lepore 	while (DMTIMER_READ4(sc, DMT_TIOCP_CFG) & DMT_TIOCP_RESET)
5324159fbabSIan Lepore 		continue;
5334159fbabSIan Lepore 
5344159fbabSIan Lepore 	sc->tclr |= DMT_TCLR_START | DMT_TCLR_AUTOLOAD;
5354159fbabSIan Lepore 	DMTIMER_WRITE4(sc, DMT_TLDR, 0);
5364159fbabSIan Lepore 	DMTIMER_WRITE4(sc, DMT_TCRR, 0);
5374159fbabSIan Lepore 	DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr);
5384159fbabSIan Lepore 
5394159fbabSIan Lepore 	/* Register the timecounter. */
5404159fbabSIan Lepore 	sc->tc.tc_name           = sc->tmr_name;
5414159fbabSIan Lepore 	sc->tc.tc_get_timecount  = dmtpps_get_timecount;
5424159fbabSIan Lepore 	sc->tc.tc_counter_mask   = ~0u;
5430050ea24SMichal Meloun 	sc->tc.tc_frequency      = sc->sysclk_freq;
5444159fbabSIan Lepore 	sc->tc.tc_quality        = 1000;
5454159fbabSIan Lepore 	sc->tc.tc_priv           = sc;
5464159fbabSIan Lepore 
5474159fbabSIan Lepore 	tc_init(&sc->tc);
5484159fbabSIan Lepore 
5494159fbabSIan Lepore 	/*
5504159fbabSIan Lepore 	 * Indicate our PPS capabilities.  Have the kernel init its part of the
5514159fbabSIan Lepore 	 * pps_state struct and add its capabilities.
5524159fbabSIan Lepore 	 *
5534159fbabSIan Lepore 	 * While the hardware has a mode to capture each edge, it's not clear we
5544159fbabSIan Lepore 	 * can use it that way, because there's only a single interrupt/status
5554159fbabSIan Lepore 	 * bit to say something was captured, but not which edge it was.  For
5564159fbabSIan Lepore 	 * now, just say we can only capture assert events (the positive-going
5574159fbabSIan Lepore 	 * edge of the pulse).
5584159fbabSIan Lepore 	 */
559192122bdSIan Lepore 	mtx_init(&sc->pps_mtx, "dmtpps", NULL, MTX_SPIN);
560192122bdSIan Lepore 	sc->pps_state.flags = PPSFLAG_MTX_SPIN;
5614159fbabSIan Lepore 	sc->pps_state.ppscap = PPS_CAPTUREASSERT;
5624159fbabSIan Lepore 	sc->pps_state.driver_abi = PPS_ABI_VERSION;
5634159fbabSIan Lepore 	sc->pps_state.driver_mtx = &sc->pps_mtx;
5644159fbabSIan Lepore 	pps_init_abi(&sc->pps_state);
5654159fbabSIan Lepore 
5664159fbabSIan Lepore 	/* Create the PPS cdev. */
567192122bdSIan Lepore 	make_dev_args_init(&mda);
568192122bdSIan Lepore 	mda.mda_flags = MAKEDEV_WAITOK;
569192122bdSIan Lepore 	mda.mda_devsw = &dmtpps_cdevsw;
570192122bdSIan Lepore 	mda.mda_cr = NULL;
571192122bdSIan Lepore 	mda.mda_uid = UID_ROOT;
572192122bdSIan Lepore 	mda.mda_gid = GID_WHEEL;
573192122bdSIan Lepore 	mda.mda_mode = 0600;
574192122bdSIan Lepore 	mda.mda_unit = device_get_unit(dev);
575192122bdSIan Lepore 	mda.mda_si_drv1 = sc;
576192122bdSIan Lepore 	if ((err = make_dev_s(&mda, &sc->pps_cdev, PPS_CDEV_NAME)) != 0) {
577192122bdSIan Lepore 		device_printf(dev, "Failed to create cdev %s\n", PPS_CDEV_NAME);
578192122bdSIan Lepore 		return (err);
579192122bdSIan Lepore 	}
5804159fbabSIan Lepore 
5814159fbabSIan Lepore 	if (bootverbose)
5824159fbabSIan Lepore 		device_printf(sc->dev, "Using %s for PPS device /dev/%s\n",
5834159fbabSIan Lepore 		    sc->tmr_name, PPS_CDEV_NAME);
5844159fbabSIan Lepore 
5854159fbabSIan Lepore 	return (0);
5864159fbabSIan Lepore }
5874159fbabSIan Lepore 
5884159fbabSIan Lepore static int
dmtpps_detach(device_t dev)5894159fbabSIan Lepore dmtpps_detach(device_t dev)
5904159fbabSIan Lepore {
5914159fbabSIan Lepore 
5924159fbabSIan Lepore 	/*
5934159fbabSIan Lepore 	 * There is no way to remove a timecounter once it has been registered,
5944159fbabSIan Lepore 	 * even if it's not in use, so we can never detach.  If we were
5954159fbabSIan Lepore 	 * dynamically loaded as a module this will prevent unloading.
5964159fbabSIan Lepore 	 */
5974159fbabSIan Lepore 	return (EBUSY);
5984159fbabSIan Lepore }
5994159fbabSIan Lepore 
6004159fbabSIan Lepore static device_method_t dmtpps_methods[] = {
6014159fbabSIan Lepore 	DEVMETHOD(device_probe,		dmtpps_probe),
6024159fbabSIan Lepore 	DEVMETHOD(device_attach,	dmtpps_attach),
6034159fbabSIan Lepore 	DEVMETHOD(device_detach,	dmtpps_detach),
6044159fbabSIan Lepore 	{ 0, 0 }
6054159fbabSIan Lepore };
6064159fbabSIan Lepore 
6074159fbabSIan Lepore static driver_t dmtpps_driver = {
6084159fbabSIan Lepore 	"am335x_dmtpps",
6094159fbabSIan Lepore 	dmtpps_methods,
6104159fbabSIan Lepore 	sizeof(struct dmtpps_softc),
6114159fbabSIan Lepore };
6124159fbabSIan Lepore 
6138537e671SJohn Baldwin DRIVER_MODULE(am335x_dmtpps, simplebus, dmtpps_driver, 0, 0);
6140050ea24SMichal Meloun MODULE_DEPEND(am335x_dmtpps, ti_sysc, 1, 1, 1);
615