Module Name: src Committed By: bouyer Date: Wed Mar 7 20:55:31 UTC 2018
Modified Files: src/sys/arch/arm/sunxi: files.sunxi src/sys/arch/evbarm/conf: SUNXI Added Files: src/sys/arch/arm/sunxi: sunxi_can.c sunxi_can.h sunxi_lradc.c sunxi_lradc.h Log Message: fdt-ise the allwinner can and lradc drivers. To generate a diff of this commit: cvs rdiff -u -r1.42 -r1.43 src/sys/arch/arm/sunxi/files.sunxi cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/sunxi/sunxi_can.c \ src/sys/arch/arm/sunxi/sunxi_can.h src/sys/arch/arm/sunxi/sunxi_lradc.c \ src/sys/arch/arm/sunxi/sunxi_lradc.h cvs rdiff -u -r1.59 -r1.60 src/sys/arch/evbarm/conf/SUNXI Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/sunxi/files.sunxi diff -u src/sys/arch/arm/sunxi/files.sunxi:1.42 src/sys/arch/arm/sunxi/files.sunxi:1.43 --- src/sys/arch/arm/sunxi/files.sunxi:1.42 Sun Feb 18 10:28:37 2018 +++ src/sys/arch/arm/sunxi/files.sunxi Wed Mar 7 20:55:31 2018 @@ -1,4 +1,4 @@ -# $NetBSD: files.sunxi,v 1.42 2018/02/18 10:28:37 jmcneill Exp $ +# $NetBSD: files.sunxi,v 1.43 2018/03/07 20:55:31 bouyer Exp $ # # Configuration info for Allwinner sunxi family SoCs # @@ -236,6 +236,15 @@ device sun6ispi: spibus attach sun6ispi at fdt with sun6i_spi file arch/arm/sunxi/sun6i_spi.c sun6i_spi +# A10/A20 CAN +device sunxican { } : ifnet +attach sunxican at fdt with sunxi_can +file arch/arm/sunxi/sunxi_can.c sunxi_can + +# LRADC +device sunxilradc +attach sunxilradc at fdt with sunxi_lradc +file arch/arm/sunxi/sunxi_lradc.c sunxi_lradc # SOC parameters defflag opt_soc.h SOC_SUNXI defflag opt_soc.h SOC_SUN4I: SOC_SUNXI Index: src/sys/arch/evbarm/conf/SUNXI diff -u src/sys/arch/evbarm/conf/SUNXI:1.59 src/sys/arch/evbarm/conf/SUNXI:1.60 --- src/sys/arch/evbarm/conf/SUNXI:1.59 Wed Feb 14 20:10:17 2018 +++ src/sys/arch/evbarm/conf/SUNXI Wed Mar 7 20:55:31 2018 @@ -1,5 +1,5 @@ # -# $NetBSD: SUNXI,v 1.59 2018/02/14 20:10:17 jakllsch Exp $ +# $NetBSD: SUNXI,v 1.60 2018/03/07 20:55:31 bouyer Exp $ # # Allwinner sunxi family # @@ -240,6 +240,11 @@ seeprom* at iic? # AT24Cxx Serial EEPR sun6ispi* at fdt? spi* at spibus? +# CAN bus +options CAN # Controller Area Network protocol +pseudo-device canloop # CAN loopback interface +sunxican* at fdt? # A10/A20 CAN controller + # RTC sunxirtc* at fdt? # RTC @@ -295,6 +300,9 @@ options WSDISPLAY_MULTICONS sunxits* at fdt? # Touch Screen controller wsmouse* at sunxits? mux 0 +# Low res ADC +sunxilradc* at fdt? + # wscons pseudo-devices pseudo-device wsfont pseudo-device wsmux Added files: Index: src/sys/arch/arm/sunxi/sunxi_can.c diff -u /dev/null src/sys/arch/arm/sunxi/sunxi_can.c:1.1 --- /dev/null Wed Mar 7 20:55:31 2018 +++ src/sys/arch/arm/sunxi/sunxi_can.c Wed Mar 7 20:55:31 2018 @@ -0,0 +1,644 @@ +/* $NetBSD: sunxi_can.c,v 1.1 2018/03/07 20:55:31 bouyer Exp $ */ + +/*- + * Copyright (c) 2017,2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Manuel Bouyer. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + + +#include "locators.h" +#include "opt_can.h" + + +#include <sys/cdefs.h> + +__KERNEL_RCSID(1, "$NetBSD: sunxi_can.c,v 1.1 2018/03/07 20:55:31 bouyer Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/intr.h> +#include <sys/ioctl.h> +#include <sys/mutex.h> +#include <sys/rndsource.h> +#include <sys/mbuf.h> +#include <sys/systm.h> + +#include <net/if.h> +#include <net/if_types.h> +#include <net/bpf.h> + +#ifdef CAN +#include <netcan/can.h> +#include <netcan/can_var.h> +#endif + +#include <dev/fdt/fdtvar.h> + +#include <arm/sunxi/sunxi_can.h> + +/* shortcut for all error interrupts */ +#define SUNXI_CAN_INT_ALLERRS (\ + SUNXI_CAN_INT_BERR | \ + SUNXI_CAN_INT_ARB_LOST | \ + SUNXI_CAN_INT_ERR_PASSIVE | \ + SUNXI_CAN_INT_DATA_OR | \ + SUNXI_CAN_INT_ERR \ + ) + +struct sunxi_can_softc { + struct canif_softc sc_cansc; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + kmutex_t sc_intr_lock; + void *sc_ih; + struct ifnet *sc_ifp; + krndsource_t sc_rnd_source; /* random source */ + struct mbuf *sc_m_transmit; /* mbuf being transmitted */ +}; +#define sc_dev sc_cansc.csc_dev +#define sc_timecaps sc_cansc.csc_timecaps +#define sc_timings sc_cansc.csc_timings +#define sc_linkmodes sc_cansc.csc_linkmodes + +static const struct of_compat_data compat_data[] = { + {"allwinner,sun4i-a10-can", 0}, + {NULL} +}; + +static int sunxi_can_match(device_t, cfdata_t, void *); +static void sunxi_can_attach(device_t, device_t, void *); + +static int sunxi_can_intr(void *); + +static void sunxi_can_ifstart(struct ifnet *); +static int sunxi_can_ifioctl(struct ifnet *, u_long, void *); +static void sunxi_can_ifwatchdog(struct ifnet *); + +static void sunxi_can_enter_reset(struct sunxi_can_softc *); +static void sunxi_can_exit_reset(struct sunxi_can_softc *); + +CFATTACH_DECL_NEW(sunxi_can, sizeof(struct sunxi_can_softc), + sunxi_can_match, sunxi_can_attach, NULL, NULL); + +static inline uint32_t +sunxi_can_read(struct sunxi_can_softc *sc, bus_size_t o) +{ + return bus_space_read_4(sc->sc_bst, sc->sc_bsh, o); +} + +static inline void +sunxi_can_write(struct sunxi_can_softc *sc, bus_size_t o, uint32_t v) +{ + return bus_space_write_4(sc->sc_bst, sc->sc_bsh, o, v); +} + +static int +sunxi_can_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_match_compat_data(faa->faa_phandle, compat_data); +} + +static void +sunxi_can_attach(device_t parent, device_t self, void *aux) +{ + struct sunxi_can_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + struct ifnet *ifp; + const int phandle = faa->faa_phandle; + bus_addr_t addr; + bus_size_t size; + char intrstr[128]; + struct clk *clk; + struct fdtbus_reset *rst; + + sc->sc_dev = self; + mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_NET); + + sc->sc_bst = faa->faa_bst; + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + + if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + + if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { + aprint_error(": failed to decode interrupt\n"); + return; + } + + if ((clk = fdtbus_clock_get_index(phandle, 0)) != NULL) { + if (clk_enable(clk) != 0) { + aprint_error(": couldn't enable clock\n"); + return; + } + } + + if ((rst = fdtbus_reset_get_index(phandle, 0)) != NULL) { + if (fdtbus_reset_deassert(rst) != 0) { + aprint_error(": couldn't de-assert reset\n"); + return; + } + } + + sc->sc_timecaps.cltc_prop_min = 0; + sc->sc_timecaps.cltc_prop_max = 0; + sc->sc_timecaps.cltc_ps1_min = 1; + sc->sc_timecaps.cltc_ps1_max = 16; + sc->sc_timecaps.cltc_ps2_min = 1; + sc->sc_timecaps.cltc_ps2_max = 8; + sc->sc_timecaps.cltc_sjw_max = 4; + sc->sc_timecaps.cltc_brp_min = 1; + sc->sc_timecaps.cltc_brp_max = 64; + sc->sc_timecaps.cltc_brp_inc = 1; + sc->sc_timecaps.cltc_clock_freq = clk_get_rate(clk); + sc->sc_timecaps.cltc_linkmode_caps = + CAN_LINKMODE_3SAMPLES | CAN_LINKMODE_LISTENONLY | + CAN_LINKMODE_LOOPBACK; + can_ifinit_timings(&sc->sc_cansc); + sc->sc_timings.clt_prop = 0; + sc->sc_timings.clt_sjw = 1; + + aprint_naive("\n"); + aprint_normal(": CAN bus controller\n"); + aprint_debug_dev(self, ": clock freq %d\n", + sc->sc_timecaps.cltc_clock_freq); + + sunxi_can_enter_reset(sc); + /* + * Disable and then clear all interrupts + */ + sunxi_can_write(sc, SUNXI_CAN_INTE_REG, 0); + sunxi_can_write(sc, SUNXI_CAN_INT_REG, + sunxi_can_read(sc, SUNXI_CAN_INT_REG)); + + sc->sc_ih = fdtbus_intr_establish(phandle, 0, IPL_NET, 0, + sunxi_can_intr, sc); + if (sc->sc_ih == NULL) { + aprint_error_dev(self, "failed to establish interrupt on %s\n", + intrstr); + return; + } + aprint_normal_dev(self, "interrupting on %s\n", intrstr); + + ifp = if_alloc(IFT_OTHER); + sc->sc_ifp = ifp; + strlcpy(ifp->if_xname, device_xname(self), IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_capabilities = 0; + ifp->if_flags = 0; + ifp->if_start = sunxi_can_ifstart; + ifp->if_ioctl = sunxi_can_ifioctl; + ifp->if_watchdog = sunxi_can_ifwatchdog; + + /* + * Attach the interface. + */ + can_ifattach(ifp); + if_deferred_start_init(ifp, NULL); + bpf_mtap_softint_init(ifp); + rnd_attach_source(&sc->sc_rnd_source, device_xname(self), + RND_TYPE_NET, RND_FLAG_DEFAULT); +#ifdef MBUFTRACE + ifp->if_mowner = malloc(sizeof(struct mowner), M_DEVBUF, + M_WAITOK | M_ZERO); + strlcpy(ifp->if_mowner->mo_name, ifp->if_xname, + sizeof(ifp->if_mowner->mo_name)); + MOWNER_ATTACH(ifp->if_mowner); +#endif +} + +static void +sunxi_can_rx_intr(struct sunxi_can_softc *sc) +{ + uint32_t reg0v; + struct mbuf *m; + struct ifnet *ifp = sc->sc_ifp; + struct can_frame *cf; + int dlc; + int regd, i; + + KASSERT(mutex_owned(&sc->sc_intr_lock)); + reg0v = sunxi_can_read(sc, SUNXI_CAN_TXBUF0_REG); + dlc = reg0v & SUNXI_CAN_TXBUF0_DL; + + if (dlc > CAN_MAX_DLC) { + ifp->if_ierrors++; + sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_REL_RX_BUF); + return; + } + + m = m_gethdr(M_NOWAIT, MT_HEADER); + if (m == NULL) { + ifp->if_ierrors++; + sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_REL_RX_BUF); + return; + } + cf = mtod(m, struct can_frame *); + memset(cf, 0, sizeof(struct can_frame)); + + cf->can_dlc = dlc; + + if (reg0v & SUNXI_CAN_TXBUF0_EFF) { + cf->can_id = + (sunxi_can_read(sc, SUNXI_CAN_TXBUF1_REG) << 21) | + (sunxi_can_read(sc, SUNXI_CAN_TXBUF2_REG) << 13) | + (sunxi_can_read(sc, SUNXI_CAN_TXBUF3_REG) << 5) | + ((sunxi_can_read(sc, SUNXI_CAN_TXBUF4_REG) >> 3) & 0x1f); + cf->can_id |= CAN_EFF_FLAG; + regd = SUNXI_CAN_TXBUF5_REG; + } else { + cf->can_id = + (sunxi_can_read(sc, SUNXI_CAN_TXBUF1_REG) << 3) | + ((sunxi_can_read(sc, SUNXI_CAN_TXBUF2_REG) << 5) & 0x7); + regd = SUNXI_CAN_TXBUF3_REG; + } + if (reg0v & SUNXI_CAN_TXBUF0_RTR) { + cf->can_id |= CAN_RTR_FLAG; + } else { + for (i = 0; i < cf->can_dlc; i++) { + cf->data[i] = sunxi_can_read(sc, regd + i * 4); + } + } + sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_REL_RX_BUF); + m->m_len = m->m_pkthdr.len = CAN_MTU; + ifp->if_ipackets++; + ifp->if_ibytes += m->m_len; + m_set_rcvif(m, ifp); + can_bpf_mtap(ifp, m, 1); + can_input(ifp, m); +} + +static void +sunxi_can_tx_intr(struct sunxi_can_softc *sc) +{ + struct ifnet * const ifp = sc->sc_ifp; + struct mbuf *m; + + KASSERT(mutex_owned(&sc->sc_intr_lock)); + if ((m = sc->sc_m_transmit) != NULL) { + ifp->if_obytes += m->m_len; + ifp->if_opackets++; + can_mbuf_tag_clean(m); + m_set_rcvif(m, ifp); + can_input(ifp, m); /* loopback */ + sc->sc_m_transmit = NULL; + ifp->if_timer = 0; + } + ifp->if_flags &= ~IFF_OACTIVE; + if_schedule_deferred_start(ifp); +} + +static int +sunxi_can_tx_abort(struct sunxi_can_softc *sc) +{ + KASSERT(mutex_owned(&sc->sc_intr_lock)); + if (sc->sc_m_transmit) { + m_freem(sc->sc_m_transmit); + sc->sc_m_transmit = NULL; + sc->sc_ifp->if_timer = 0; + /* + * the transmit abort will trigger a TX interrupt + * which will restart the queue or cleae OACTIVE, + * as appropriate + */ + sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_ABT_REQ); + return 1; + } + return 0; +} + +static void +sunxi_can_err_intr(struct sunxi_can_softc *sc, uint32_t irq, uint32_t sts) +{ + struct ifnet * const ifp = sc->sc_ifp; + KASSERT(mutex_owned(&sc->sc_intr_lock)); + int txerr = 0; + uint32_t reg; + + if (irq & SUNXI_CAN_INT_DATA_OR) { + ifp->if_ierrors++; + sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_CLR_OR); + } + if (irq & SUNXI_CAN_INT_ERR) { + reg = sunxi_can_read(sc, SUNXI_CAN_REC_REG); + printf("%s: ERR interrupt status 0x%x counters 0x%x\n", + device_xname(sc->sc_dev), sts, reg); + + } + if (irq & SUNXI_CAN_INT_BERR) { + if (sts & SUNXI_CAN_STA_TX) + txerr++; + if (sts & SUNXI_CAN_STA_RX) + ifp->if_ierrors++; + } + if (irq & SUNXI_CAN_INT_ERR_PASSIVE) { + printf("%s: PASSV interrupt status 0x%x\n", + device_xname(sc->sc_dev), sts); + } + if (irq & SUNXI_CAN_INT_ARB_LOST) { + txerr++; + } + if (txerr) { + ifp->if_oerrors += txerr; + (void) sunxi_can_tx_abort(sc); + } +} + +int +sunxi_can_intr(void *arg) +{ + struct sunxi_can_softc * const sc = arg; + int rv = 0; + int irq; + + mutex_enter(&sc->sc_intr_lock); + + while ((irq = sunxi_can_read(sc, SUNXI_CAN_INT_REG)) != 0) { + uint32_t sts = sunxi_can_read(sc, SUNXI_CAN_STA_REG); + rv = 1; + + if (irq & SUNXI_CAN_INT_TX_FLAG) { + sunxi_can_tx_intr(sc); + } + if (irq & SUNXI_CAN_INT_RX_FLAG) { + while (sts & SUNXI_CAN_STA_RX_RDY) { + sunxi_can_rx_intr(sc); + sts = sunxi_can_read(sc, SUNXI_CAN_STA_REG); + } + } + if (irq & SUNXI_CAN_INT_ALLERRS) { + sunxi_can_err_intr(sc, irq, sts); + } + sunxi_can_write(sc, SUNXI_CAN_INT_REG, irq); + rnd_add_uint32(&sc->sc_rnd_source, irq); + + } + mutex_exit(&sc->sc_intr_lock); + + return rv; +} + +void +sunxi_can_ifstart(struct ifnet *ifp) +{ + struct sunxi_can_softc * const sc = ifp->if_softc; + struct mbuf *m; + struct can_frame *cf; + int regd; + uint32_t reg0val; + int i; + + mutex_enter(&sc->sc_intr_lock); + if (ifp->if_flags & IFF_OACTIVE) + goto out; + + IF_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) + goto out; + + MCLAIM(m, ifp->if_mowner); + sc->sc_m_transmit = m; + + KASSERT((m->m_flags & M_PKTHDR) != 0); + KASSERT(m->m_len == m->m_pkthdr.len); + + cf = mtod(m, struct can_frame *); + reg0val = cf->can_dlc & SUNXI_CAN_TXBUF0_DL; + if (cf->can_id & CAN_RTR_FLAG) + reg0val |= SUNXI_CAN_TXBUF0_RTR; + + if (cf->can_id & CAN_EFF_FLAG) { + reg0val |= SUNXI_CAN_TXBUF0_EFF; + sunxi_can_write(sc, SUNXI_CAN_TXBUF1_REG, + (cf->can_id >> 21) & 0xff); + sunxi_can_write(sc, SUNXI_CAN_TXBUF2_REG, + (cf->can_id >> 13) & 0xff); + sunxi_can_write(sc, SUNXI_CAN_TXBUF3_REG, + (cf->can_id >> 5) & 0xff); + sunxi_can_write(sc, SUNXI_CAN_TXBUF4_REG, + (cf->can_id << 3) & 0xf8); + regd = SUNXI_CAN_TXBUF5_REG; + } else { + sunxi_can_write(sc, SUNXI_CAN_TXBUF1_REG, + (cf->can_id >> 3) & 0xff); + sunxi_can_write(sc, SUNXI_CAN_TXBUF2_REG, + (cf->can_id << 5) & 0xe0); + regd = SUNXI_CAN_TXBUF3_REG; + } + + for (i = 0; i < cf->can_dlc; i++) { + sunxi_can_write(sc, regd + i * 4, cf->data[i]); + } + sunxi_can_write(sc, SUNXI_CAN_TXBUF0_REG, reg0val); + + if (sc->sc_linkmodes & CAN_LINKMODE_LOOPBACK) { + sunxi_can_write(sc, SUNXI_CAN_CMD_REG, + SUNXI_CAN_CMD_TANS_REQ | SUNXI_CAN_CMD_SELF_REQ); + } else { + sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_TANS_REQ); + } + ifp->if_flags |= IFF_OACTIVE; + ifp->if_timer = 5; + can_bpf_mtap(ifp, m, 0); +out: + mutex_exit(&sc->sc_intr_lock); +} + +static int +sunxi_can_ifup(struct sunxi_can_softc * const sc) +{ + uint32_t reg; + + /* setup timings and mode - has to be done in reset */ + reg = SUNXI_CAN_MODSEL_RST; + if (sc->sc_linkmodes & CAN_LINKMODE_LISTENONLY) + reg |= SUNXI_CAN_MODSEL_LST_ONLY; + + if (sc->sc_linkmodes & CAN_LINKMODE_LOOPBACK) + reg |= SUNXI_CAN_MODSEL_LB_MOD; + + sunxi_can_write(sc, SUNXI_CAN_MODSEL_REG, reg); + + reg = 0; + if (sc->sc_timings.clt_prop != 0) + return EINVAL; + + if (sc->sc_timings.clt_brp > sc->sc_timecaps.cltc_brp_max || + sc->sc_timings.clt_brp < sc->sc_timecaps.cltc_brp_min) + return EINVAL; + reg |= (sc->sc_timings.clt_brp - 1) << 0; + + if (sc->sc_timings.clt_ps1 > sc->sc_timecaps.cltc_ps1_max || + sc->sc_timings.clt_ps1 < sc->sc_timecaps.cltc_ps1_min) + return EINVAL; + reg |= (sc->sc_timings.clt_ps1 - 1) << 16; + + if (sc->sc_timings.clt_ps2 > sc->sc_timecaps.cltc_ps2_max || + sc->sc_timings.clt_ps2 < sc->sc_timecaps.cltc_ps2_min) + return EINVAL; + reg |= (sc->sc_timings.clt_ps2 - 1) << 20; + + if (sc->sc_timings.clt_sjw > sc->sc_timecaps.cltc_sjw_max || + sc->sc_timings.clt_sjw < 1) + return EINVAL; + reg |= (sc->sc_timings.clt_sjw - 1) << 14; + + if (sc->sc_linkmodes & CAN_LINKMODE_3SAMPLES) + reg |= SUNXI_CAN_BUS_TIME_SAM; + + sunxi_can_write(sc, SUNXI_CAN_BUS_TIME_REG, reg); + + /* set filters to accept all frames */ + sunxi_can_write(sc, SUNXI_CAN_ACPC, 0x00000000); + sunxi_can_write(sc, SUNXI_CAN_ACPM, 0xffffffff); + + /* clear errors counter */ + sunxi_can_write(sc, SUNXI_CAN_REC_REG, 0); + + /* leave reset mode and enable interrupts */ + sunxi_can_exit_reset(sc); + sunxi_can_write(sc, SUNXI_CAN_INTE_REG, + SUNXI_CAN_INT_TX_FLAG | SUNXI_CAN_INT_RX_FLAG | SUNXI_CAN_INT_ALLERRS); + sc->sc_ifp->if_flags |= IFF_RUNNING; + return 0; +} + +static void +sunxi_can_ifdown(struct sunxi_can_softc * const sc) +{ + sc->sc_ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + sc->sc_ifp->if_timer = 0; + sunxi_can_enter_reset(sc); + sunxi_can_write(sc, SUNXI_CAN_INTE_REG, 0); + sunxi_can_write(sc, SUNXI_CAN_INT_REG, + sunxi_can_read(sc, SUNXI_CAN_INT_REG)); +} + +static int +sunxi_can_ifioctl(struct ifnet *ifp, u_long cmd, void *data) +{ + struct sunxi_can_softc * const sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + int error = 0; + + mutex_enter(&sc->sc_intr_lock); + + switch (cmd) { + case SIOCINITIFADDR: + error = EAFNOSUPPORT; + break; + case SIOCSIFMTU: + if ((unsigned)ifr->ifr_mtu != sizeof(struct can_frame)) + error = EINVAL; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + error = EAFNOSUPPORT; + break; + default: + error = ifioctl_common(ifp, cmd, data); + if (error == 0) { + if ((ifp->if_flags & IFF_UP) != 0 && + (ifp->if_flags & IFF_RUNNING) == 0) { + error = sunxi_can_ifup(sc); + if (error) { + ifp->if_flags &= ~IFF_UP; + } + } else if ((ifp->if_flags & IFF_UP) == 0 && + (ifp->if_flags & IFF_RUNNING) != 0) { + sunxi_can_ifdown(sc); + } + } + break; + } + + mutex_exit(&sc->sc_intr_lock); + return error; +} + +void +sunxi_can_ifwatchdog(struct ifnet *ifp) +{ + struct sunxi_can_softc * const sc = ifp->if_softc; + printf("%s: watchdog timeout\n", device_xname(sc->sc_dev)); + + mutex_enter(&sc->sc_intr_lock); + printf("irq 0x%x en 0x%x mode 0x%x status 0x%x timings 0x%x err 0x%x\n", + sunxi_can_read(sc, SUNXI_CAN_INT_REG), + sunxi_can_read(sc, SUNXI_CAN_INTE_REG), + sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG), + sunxi_can_read(sc, SUNXI_CAN_STA_REG), + sunxi_can_read(sc, SUNXI_CAN_BUS_TIME_REG), + sunxi_can_read(sc, SUNXI_CAN_REC_REG)); + /* if there is a transmit in progress abort */ + if (sunxi_can_tx_abort(sc)) { + ifp->if_oerrors++; + } + mutex_exit(&sc->sc_intr_lock); +} + +static void +sunxi_can_enter_reset(struct sunxi_can_softc *sc) +{ + int i; + uint32_t val; + + for (i = 0; i < 1000; i++) { + val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); + val |= SUNXI_CAN_MODSEL_RST; + sunxi_can_write(sc, SUNXI_CAN_MODSEL_REG, val); + val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); + if (val & SUNXI_CAN_MODSEL_RST) + return; + } + printf("%s: couldn't enter reset mode\n", device_xname(sc->sc_dev)); +} + +static void +sunxi_can_exit_reset(struct sunxi_can_softc *sc) +{ + int i; + uint32_t val; + + for (i = 0; i < 1000; i++) { + val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); + val &= ~SUNXI_CAN_MODSEL_RST; + sunxi_can_write(sc, SUNXI_CAN_MODSEL_REG, val); + val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); + if ((val & SUNXI_CAN_MODSEL_RST) == 0) + return; + } + printf("%s: couldn't leave reset mode\n", device_xname(sc->sc_dev)); +} Index: src/sys/arch/arm/sunxi/sunxi_can.h diff -u /dev/null src/sys/arch/arm/sunxi/sunxi_can.h:1.1 --- /dev/null Wed Mar 7 20:55:31 2018 +++ src/sys/arch/arm/sunxi/sunxi_can.h Wed Mar 7 20:55:31 2018 @@ -0,0 +1,146 @@ +/* $NetBSD: sunxi_can.h,v 1.1 2018/03/07 20:55:31 bouyer Exp $ */ + +/*- + * Copyright (c) 2017,2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Manuel Bouyer. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* CAN mode select register */ +#define SUNXI_CAN_MODSEL_REG 0x00 +#define SUNXI_CAN_MODSEL_SLEEP __BIT(4) +#define SUNXI_CAN_MODSEL_ACP_FLT_MOD __BIT(3) +#define SUNXI_CAN_MODSEL_LB_MOD __BIT(2) +#define SUNXI_CAN_MODSEL_LST_ONLY __BIT(1) +#define SUNXI_CAN_MODSEL_RST __BIT(0) + +/* CAN command register */ +#define SUNXI_CAN_CMD_REG 0x04 +#define SUNXI_CAN_CMD_BUS_OFF __BIT(5) +#define SUNXI_CAN_CMD_SELF_REQ __BIT(4) +#define SUNXI_CAN_CMD_CLR_OR __BIT(3) +#define SUNXI_CAN_CMD_REL_RX_BUF __BIT(2) +#define SUNXI_CAN_CMD_ABT_REQ __BIT(1) +#define SUNXI_CAN_CMD_TANS_REQ __BIT(0) + +/* CAN status register */ +#define SUNXI_CAN_STA_REG 0x08 +#define SUNXI_CAN_STA_ERR_CODE __BITS(23,22) +#define SUNXI_CAN_STA_ERR_CODE_BIT 0 +#define SUNXI_CAN_STA_ERR_CODE_FORM 1 +#define SUNXI_CAN_STA_ERR_CODE_STUFF 2 +#define SUNXI_CAN_STA_ERR_CODE_OTHER 3 +#define SUNXI_CAN_STA_ERR_DIR _BIT(21) +#define SUNXI_CAN_STA_ERR_SEG_CODE __BITS(20,16) +#define SUNXI_CAN_STA_ARB_LOST __BITS(12,8) +#define SUNXI_CAN_STA_BUS __BIT(7) +#define SUNXI_CAN_STA_ERR __BIT(6) +#define SUNXI_CAN_STA_TX __BIT(5) +#define SUNXI_CAN_STA_RX __BIT(4) +#define SUNXI_CAN_STA_TX_OVER __BIT(3) +#define SUNXI_CAN_STA_TX_RDY __BIT(2) +#define SUNXI_CAN_STA_DATA_OR __BIT(1) +#define SUNXI_CAN_STA_RX_RDY __BIT(0) + +/* CAN interrupt register */ +#define SUNXI_CAN_INT_REG 0x0c +#define SUNXI_CAN_INT_BERR __BIT(7) +#define SUNXI_CAN_INT_ARB_LOST __BIT(6) +#define SUNXI_CAN_INT_ERR_PASSIVE __BIT(5) +#define SUNXI_CAN_INT_WAKEUP __BIT(4) +#define SUNXI_CAN_INT_DATA_OR __BIT(3) +#define SUNXI_CAN_INT_ERR __BIT(2) +#define SUNXI_CAN_INT_TX_FLAG __BIT(1) +#define SUNXI_CAN_INT_RX_FLAG __BIT(0) + +/* CAN interrupt enable register */ +#define SUNXI_CAN_INTE_REG 0x10 + +/* CAN bus timing register */ +#define SUNXI_CAN_BUS_TIME_REG 0x14 +#define SUNXI_CAN_BUS_TIME_SAM __BIT(23) +#define SUNXI_CAN_BUS_TIME_PHSEG2 __BITS(22,20) +#define SUNXI_CAN_BUS_TIME_PHSEG1 __BITS(19,16) +#define SUNXI_CAN_BUS_TIME_SJW __BITS(15,14) +#define SUNXI_CAN_BUS_TIME_TQ_BRP __BITS(9,0) + +/* CAN tx error warning limit register */ +#define SUNXI_CAN_EWL_REG 0x18 +#define SUNXI_CAN_EWL_ERR_WRN_LMT __BITS(7,0) + +/* CAN error counter register */ +#define SUNXI_CAN_REC_REG 0x1c +#define SUNXI_CAN_REC_RX_ERR_CNT __BITS(23,16) +#define SUNXI_CAN_REC_TX_ERR_CNT __BITS(7,0) + +/* CAN receive message register */ +#define SUNXI_CAN_RMSGC_REG 0x20 +#define SUNXI_CAN_RMSGC_RX_MSG_CNT __BITS(7,0) + +/* CAN receive buffer start address register */ +#define SUNXI_CAN_RSADDR_REG 0x24 +#define SUNXI_CAN_RSADDR_RX_BUF_SADDR __BITS(5,0) + +/* CAN rx/tx message buffer 0 register */ +#define SUNXI_CAN_TXBUF0_REG 0x40 +#define SUNXI_CAN_TXBUF0_EFF __BIT(7) +#define SUNXI_CAN_TXBUF0_RTR __BIT(6) +#define SUNXI_CAN_TXBUF0_DL __BITS(3,0) + +/* CAN rx/tx message buffer registers */ +#define SUNXI_CAN_TXBUF1_REG 0x44 +#define SUNXI_CAN_TXBUF2_REG 0x48 +#define SUNXI_CAN_TXBUF3_REG 0x4c +#define SUNXI_CAN_TXBUF4_REG 0x50 +#define SUNXI_CAN_TXBUF5_REG 0x54 +#define SUNXI_CAN_TXBUF6_REG 0x58 +#define SUNXI_CAN_TXBUF7_REG 0x5c +#define SUNXI_CAN_TXBUF8_REG 0x60 +#define SUNXI_CAN_TXBUF9_REG 0x64 +#define SUNXI_CAN_TXBUF10_REG 0x68 +#define SUNXI_CAN_TXBUF11_REG 0x6c +#define SUNXI_CAN_TXBUF12_REG 0x70 + +/* CAN acceptance code 0 register */ +#define SUNXI_CAN_ACPC 0x40 + +/* CAN acceptance mask 0 register */ +#define SUNXI_CAN_ACPM 0x44 + +/* CAN transmit buffer for read back registers */ +#define SUNXI_CAN_RBUF_RBACK0 0x180 +#define SUNXI_CAN_RBUF_RBACK1 0x184 +#define SUNXI_CAN_RBUF_RBACK2 0x188 +#define SUNXI_CAN_RBUF_RBACK3 0x18c +#define SUNXI_CAN_RBUF_RBACK4 0x190 +#define SUNXI_CAN_RBUF_RBACK5 0x194 +#define SUNXI_CAN_RBUF_RBACK6 0x198 +#define SUNXI_CAN_RBUF_RBACK7 0x19c +#define SUNXI_CAN_RBUF_RBACK8 0x1a0 +#define SUNXI_CAN_RBUF_RBACK9 0x1a4 +#define SUNXI_CAN_RBUF_RBACK10 0x1a8 +#define SUNXI_CAN_RBUF_RBACK11 0x1ac +#define SUNXI_CAN_RBUF_RBACK12 0x1b0 Index: src/sys/arch/arm/sunxi/sunxi_lradc.c diff -u /dev/null src/sys/arch/arm/sunxi/sunxi_lradc.c:1.1 --- /dev/null Wed Mar 7 20:55:31 2018 +++ src/sys/arch/arm/sunxi/sunxi_lradc.c Wed Mar 7 20:55:31 2018 @@ -0,0 +1,388 @@ +/* $NetBSD: sunxi_lradc.c,v 1.1 2018/03/07 20:55:31 bouyer Exp $ */ + +/*- + * Copyright (c) 2016, 2018 Manuel Bouyer + * 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 <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: sunxi_lradc.c,v 1.1 2018/03/07 20:55:31 bouyer Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/intr.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/select.h> +#include <sys/mutex.h> +#include <sys/kmem.h> + +#include <dev/sysmon/sysmonvar.h> +#include <dev/sysmon/sysmon_taskq.h> + +#include <dev/fdt/fdtvar.h> + +#include <arm/sunxi/sunxi_lradc.h> + +struct sunxi_lradc_softc { + device_t sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + kmutex_t sc_lock; + void *sc_ih; + struct fdtbus_regulator *sc_supply; + int sc_vref; + uint8_t sc_chans; + uint8_t sc_level[2][32]; + const char *sc_name[2][32]; + int sc_nlevels[2]; + int sc_lastlevel[2]; + struct sysmon_pswitch *sc_switches[2]; + uint32_t sc_ints; /* pending interrupts */ +}; + +#define ADC_READ(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) +#define ADC_WRITE(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) + +static int sunxi_lradc_match(device_t, cfdata_t, void *); +static void sunxi_lradc_attach(device_t, device_t, void *); +static int sunxi_lradc_intr(void *); +static void sunxi_lradc_get_levels(struct sunxi_lradc_softc *, int, int); +static void sunxi_lradc_print_levels(struct sunxi_lradc_softc *, int); +static bool sunxi_lradc_register_switches(struct sunxi_lradc_softc *, int); + +static const struct of_compat_data compat_data[] = { + {"allwinner,sun4i-a10-lradc-keys", 0}, + {NULL} +}; + + +CFATTACH_DECL_NEW(sunxi_lradc, sizeof(struct sunxi_lradc_softc), + sunxi_lradc_match, sunxi_lradc_attach, NULL, NULL); + +static int +sunxi_lradc_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_match_compat_data(faa->faa_phandle, compat_data); +} + +static void +sunxi_lradc_attach(device_t parent, device_t self, void *aux) +{ + struct sunxi_lradc_softc *sc = device_private(self); + struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + int i, error; + uint32_t intc = 0; + bus_addr_t addr; + bus_size_t size; + char intrstr[128]; + struct clk *clk; + struct fdtbus_reset *rst; + + sc->sc_dev = self; + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM); + + sc->sc_bst = faa->faa_bst; + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + + if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + + if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { + aprint_error(": failed to decode interrupt\n"); + return; + } + + if ((clk = fdtbus_clock_get_index(phandle, 0)) != NULL) { + if (clk_enable(clk) != 0) { + aprint_error(": couldn't enable clock\n"); + return; + } + } + + if ((rst = fdtbus_reset_get_index(phandle, 0)) != NULL) { + if (fdtbus_reset_deassert(rst) != 0) { + aprint_error(": couldn't de-assert reset\n"); + return; + } + } + + sc->sc_chans = -1; + aprint_naive("\n"); + aprint_normal(": LRADC, "); + if (of_hasprop(phandle, "vref-supply")) { + sc->sc_supply = + fdtbus_regulator_acquire(phandle, "vref-supply"); + if (sc->sc_supply == NULL) { + aprint_error(": couldn't acquire vref-supply\n"); + return; + } + } else { + aprint_normal("disabled (no vref-supply)\n"); + return; + } + error = fdtbus_regulator_get_voltage(sc->sc_supply, &sc->sc_vref); + if (error) { + aprint_error(": couldn't get vref (%d)\n", error); + return; + } + + for (i = 0; i < 2; i++) + sunxi_lradc_get_levels(sc, phandle, i); + + switch (sc->sc_chans) { + case 0: + aprint_normal("channel 0 enabled\n"); + break; + case 1: + aprint_normal("channel 1 enabled\n"); + break; + case 2: + aprint_normal("channel 0 & 1 enabled\n"); + break; + default: + aprint_normal("no channel enabled\n"); + break; + } + + if (sc->sc_chans == 0 || sc->sc_chans == 2) { + sunxi_lradc_print_levels(sc, 0); + } + if (sc->sc_chans == 1 || sc->sc_chans == 2) { + sunxi_lradc_print_levels(sc, 1); + } + + sc->sc_ih = fdtbus_intr_establish(phandle, 0, IPL_VM, + FDT_INTR_MPSAFE, sunxi_lradc_intr, sc); + if (sc->sc_ih == NULL) { + aprint_error_dev(self, "couldn't establish interrupt on %s\n", + intrstr); + return; + } + aprint_normal_dev(self, ": interrupting on %s\n", intrstr); + if (sc->sc_chans == 0 || sc->sc_chans == 2) { + if (!sunxi_lradc_register_switches(sc, 0)) { + aprint_error_dev(self, ": can't register switches\n"); + return; + } + } + if (sc->sc_chans == 1 || sc->sc_chans == 2) { + if (!sunxi_lradc_register_switches(sc, 1)) { + aprint_error_dev(self, ": can't register switches\n"); + return; + } + } + + /* + * init and enable LRADC + * 250Hz, wait 2 cycles (8ms) on key press and release + */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_LRADC_CTRL_REG, + (2 << AWIN_LRADC_CTRL_FIRSTCONV_SHIFT) | + (1 << AWIN_LRADC_CTRL_LV_A_B_CNT_SHIFT) | + AWIN_LRADC_CTRL_HOLD_EN | + AWIN_LRADC_CTRL_RATE_250 | + (sc->sc_chans << AWIN_LRADC_CTRL_CHAN_SHIFT) | + AWIN_LRADC_CTRL_EN); + switch(sc->sc_chans) { + case 0: + intc = AWIN_LRADC_INT_KEY0 | AWIN_LRADC_INT_KEYUP0; + break; + case 1: + intc = AWIN_LRADC_INT_KEY1 | AWIN_LRADC_INT_KEYUP1; + break; + case 2: + intc = AWIN_LRADC_INT_KEY0 | AWIN_LRADC_INT_KEYUP0 | + AWIN_LRADC_INT_KEY1 | AWIN_LRADC_INT_KEYUP1; + break; + } + bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_LRADC_INTC_REG, intc); +} + +static void +sunxi_lradc_get_levels(struct sunxi_lradc_softc *sc, int phandle, int chan) +{ + int i; + int this_chan; + int child; + int32_t level; + const char *name; + const int vref = sc->sc_vref * 2 / 3; + + for (child = OF_child(phandle), i = 0; child; child = OF_peer(child)) { + if (of_getprop_uint32(child, "channel", &this_chan) != 0) + continue; + if (this_chan != chan) + continue; + if (of_getprop_uint32(child, "voltage", &level) != 0) + continue; + name = fdtbus_get_string(child, "label"); + if (name == NULL) + continue; + sc->sc_level[chan][i] = level * 63 / vref; + sc->sc_name[chan][i] = name; + i++; + } + if (i > 0) { + switch(chan) { + case 0: + if (sc->sc_chans == 1) + sc->sc_chans = 2; + else + sc->sc_chans = 0; + break; + case 1: + if (sc->sc_chans == 0) + sc->sc_chans = 2; + else + sc->sc_chans = 1; + break; + default: + panic("lradc: chan %d", chan); + } + sc->sc_nlevels[chan] = i; + } +} + +static void +sunxi_lradc_print_levels(struct sunxi_lradc_softc *sc, int chan) +{ + int i; + + aprint_verbose_dev(sc->sc_dev, ": channel %d levels", chan); + for (i = 0; i < 32; i++) { + if (sc->sc_name[chan][i] == NULL) + break; + aprint_verbose(" %d(%s)", + sc->sc_level[chan][i], sc->sc_name[chan][i]); + } + aprint_verbose("\n"); +} + +static bool +sunxi_lradc_register_switches(struct sunxi_lradc_softc *sc, int chan) +{ + + KASSERT(sc->sc_nlevels[chan] > 0); + sc->sc_switches[chan] = kmem_zalloc( + sizeof(struct sysmon_pswitch) * sc->sc_nlevels[chan] , KM_SLEEP); + + if (sc->sc_switches[chan] == NULL) + return false; + + for (int i = 0; i < sc->sc_nlevels[chan]; i++) { + struct sysmon_pswitch *sw = &sc->sc_switches[chan][i]; + sw->smpsw_name = sc->sc_name[chan][i]; + sw->smpsw_type = PSWITCH_TYPE_HOTKEY; + sysmon_pswitch_register(sw); + } + return true; +} + +static void +sunxi_lradc_intr_ev(struct sunxi_lradc_softc *sc, int chan, int event) +{ + int32_t val; + int diff = 64; + + + if (event == PSWITCH_EVENT_RELEASED) { + sysmon_pswitch_event( + &sc->sc_switches[chan][sc->sc_lastlevel[chan]], event); + return; + } + + val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, + chan == 0 ? AWIN_LRADC_DATA0_REG : AWIN_LRADC_DATA1_REG); + + KASSERT(sc->sc_nlevels[chan] > 0); + for (int i = 0; i < sc->sc_nlevels[chan]; i++) { + int curdiff; + curdiff = val - sc->sc_level[chan][i]; + if (curdiff < 0) + curdiff = -curdiff; + if (diff > curdiff) { + diff = curdiff; + sc->sc_lastlevel[chan] = i; + } + } + sysmon_pswitch_event( + &sc->sc_switches[chan][sc->sc_lastlevel[chan]], event); +} + +static void +sunxi_lradc_intr_task(void *arg) +{ + struct sunxi_lradc_softc *sc = arg; + mutex_enter(&sc->sc_lock); + if (sc->sc_chans == 0 || sc->sc_chans == 2) { + if (sc->sc_ints & AWIN_LRADC_INT_KEY0) { + sunxi_lradc_intr_ev(sc, 0, PSWITCH_EVENT_PRESSED); + } + if (sc->sc_ints & AWIN_LRADC_INT_KEYUP0) { + sunxi_lradc_intr_ev(sc, 0, PSWITCH_EVENT_RELEASED); + } + } + if (sc->sc_chans == 1 || sc->sc_chans == 2) { + if (sc->sc_ints & AWIN_LRADC_INT_KEY1) { + sunxi_lradc_intr_ev(sc, 1, PSWITCH_EVENT_PRESSED); + } + if (sc->sc_ints & AWIN_LRADC_INT_KEYUP1) { + sunxi_lradc_intr_ev(sc, 1, PSWITCH_EVENT_RELEASED); + } + } + sc->sc_ints = 0; + mutex_exit(&sc->sc_lock); +} + +static int +sunxi_lradc_intr(void *arg) +{ + struct sunxi_lradc_softc *sc = arg; + int error; + + mutex_enter(&sc->sc_lock); + sc->sc_ints = bus_space_read_4(sc->sc_bst, sc->sc_bsh, + AWIN_LRADC_INTS_REG); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_LRADC_INTS_REG, + sc->sc_ints); + mutex_exit(&sc->sc_lock); + error = sysmon_task_queue_sched(0, sunxi_lradc_intr_task, sc); + if (error != 0) { + printf("%s: sysmon_task_queue_sched failed (%d)\n", + device_xname(sc->sc_dev), error); + } + return 1; +} Index: src/sys/arch/arm/sunxi/sunxi_lradc.h diff -u /dev/null src/sys/arch/arm/sunxi/sunxi_lradc.h:1.1 --- /dev/null Wed Mar 7 20:55:31 2018 +++ src/sys/arch/arm/sunxi/sunxi_lradc.h Wed Mar 7 20:55:31 2018 @@ -0,0 +1,68 @@ +/* $NetBSD: sunxi_lradc.h,v 1.1 2018/03/07 20:55:31 bouyer Exp $ */ + +/*- + * Copyright (c) 2016 Manuel Bouyer + * 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. + */ + +#define AWIN_LRADC_CTRL_REG 0x00 +#define AWIN_LRADC_CTRL_FIRSTCONV_MASK __BITS(31,24) +#define AWIN_LRADC_CTRL_FIRSTCONV_SHIFT 24 +#define AWIN_LRADC_CTRL_CHAN_MASK __BITS(23,22) +#define AWIN_LRADC_CTRL_CHAN_SHIFT 22 +#define AWIN_LRADC_CTRL_CONT_MASK __BITS(19,16) +#define AWIN_LRADC_CTRL_CONT_SHIFT 16 +#define AWIN_LRADC_CTRL_KMODE_MASK __BITS(13,12) +#define AWIN_LRADC_CTRL_KMODE_NORMAL (0 << 12) +#define AWIN_LRADC_CTRL_KMODE_SINGLE (1 << 12) +#define AWIN_LRADC_CTRL_KMODE_CONTINUE (2 << 12) +#define AWIN_LRADC_CTRL_LV_A_B_CNT_MASK __BITS(11,8) +#define AWIN_LRADC_CTRL_LV_A_B_CNT_SHIFT 8 +#define AWIN_LRADC_CTRL_HOLD_EN __BIT(6) +#define AWIN_LRADC_CTRL_LEVEL_B_MASK __BITS(5,4) +#define AWIN_LRADC_CTRL_LEVEL_B_3C (0 << 4) +#define AWIN_LRADC_CTRL_LEVEL_B_39 (1 << 4) +#define AWIN_LRADC_CTRL_LEVEL_B_36 (2 << 4) +#define AWIN_LRADC_CTRL_LEVEL_B_33 (3 << 4) +#define AWIN_LRADC_CTRL_RATE_MASK __BITS(3,2) +#define AWIN_LRADC_CTRL_RATE_250 (0 << 2) +#define AWIN_LRADC_CTRL_RATE_125 (1 << 2) +#define AWIN_LRADC_CTRL_RATE_62 (2 << 2) +#define AWIN_LRADC_CTRL_RATE_31 (3 << 2) +#define AWIN_LRADC_CTRL_EN __BIT(0) +#define AWIN_LRADC_INTC_REG 0x04 +#define AWIN_LRADC_INTS_REG 0x08 +#define AWIN_LRADC_INT_KEYUP1 __BIT(12) +#define AWIN_LRADC_INT_ALREADYHOLD1 __BIT(11) +#define AWIN_LRADC_INT_HOLD1 __BIT(10) +#define AWIN_LRADC_INT_KEY1 __BIT(9) +#define AWIN_LRADC_INT_DATA1 __BIT(8) +#define AWIN_LRADC_INT_KEYUP0 __BIT(4) +#define AWIN_LRADC_INT_ALREADYHOLD0 __BIT(3) +#define AWIN_LRADC_INT_HOLD0 __BIT(2) +#define AWIN_LRADC_INT_KEY0 __BIT(1) +#define AWIN_LRADC_INT_DATA0 __BIT(0) +#define AWIN_LRADC_DATA0_REG 0x0c +#define AWIN_LRADC_DATA1_REG 0x10 +