Home | History | Annotate | Line # | Download | only in sunxi
sunxi_can.c revision 1.11
      1 /*	$NetBSD: sunxi_can.c,v 1.11 2022/09/21 20:21:16 bouyer Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2017,2018 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Manuel Bouyer.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 
     33 #include "locators.h"
     34 #include "opt_can.h"
     35 
     36 
     37 #include <sys/cdefs.h>
     38 
     39 __KERNEL_RCSID(1, "$NetBSD: sunxi_can.c,v 1.11 2022/09/21 20:21:16 bouyer Exp $");
     40 
     41 #include <sys/param.h>
     42 #include <sys/bus.h>
     43 #include <sys/device.h>
     44 #include <sys/intr.h>
     45 #include <sys/ioctl.h>
     46 #include <sys/mutex.h>
     47 #include <sys/rndsource.h>
     48 #include <sys/mbuf.h>
     49 #include <sys/systm.h>
     50 
     51 #include <net/if.h>
     52 #include <net/if_types.h>
     53 #include <net/bpf.h>
     54 
     55 #ifdef CAN
     56 #include <netcan/can.h>
     57 #include <netcan/can_var.h>
     58 #endif
     59 
     60 #include <dev/fdt/fdtvar.h>
     61 
     62 #include <arm/sunxi/sunxi_can.h>
     63 
     64 /* shortcut for all error interrupts */
     65 #define SUNXI_CAN_INT_ALLERRS (\
     66 	SUNXI_CAN_INT_BERR | \
     67 	SUNXI_CAN_INT_ARB_LOST | \
     68 	SUNXI_CAN_INT_ERR_PASSIVE | \
     69 	SUNXI_CAN_INT_DATA_OR | \
     70 	SUNXI_CAN_INT_ERR \
     71     )
     72 
     73 struct sunxi_can_softc {
     74 	struct canif_softc sc_cansc;
     75 	bus_space_tag_t sc_bst;
     76 	bus_space_handle_t sc_bsh;
     77 	kmutex_t sc_intr_lock;
     78 	void *sc_ih;
     79 	struct ifnet *sc_ifp;
     80 	krndsource_t sc_rnd_source;	/* random source */
     81 	struct mbuf *sc_m_transmit; /* mbuf being transmitted */
     82 };
     83 #define sc_dev		sc_cansc.csc_dev
     84 #define sc_timecaps	sc_cansc.csc_timecaps
     85 #define sc_timings	sc_cansc.csc_timings
     86 #define sc_linkmodes	sc_cansc.csc_linkmodes
     87 
     88 static const struct device_compatible_entry compat_data[] = {
     89 	{ .compat = "allwinner,sun4i-a10-can" },
     90 	DEVICE_COMPAT_EOL
     91 };
     92 
     93 static int sunxi_can_match(device_t, cfdata_t, void *);
     94 static void sunxi_can_attach(device_t, device_t, void *);
     95 
     96 static int sunxi_can_intr(void *);
     97 
     98 static void sunxi_can_ifstart(struct ifnet *);
     99 static int sunxi_can_ifioctl(struct ifnet *, u_long, void *);
    100 static void sunxi_can_ifwatchdog(struct ifnet *);
    101 
    102 static void sunxi_can_enter_reset(struct sunxi_can_softc *);
    103 static void sunxi_can_exit_reset(struct sunxi_can_softc *);
    104 static void sunxi_can_ifdown(struct sunxi_can_softc * const);
    105 static int sunxi_can_ifup(struct sunxi_can_softc * const);
    106 
    107 CFATTACH_DECL_NEW(sunxi_can, sizeof(struct sunxi_can_softc),
    108 	sunxi_can_match, sunxi_can_attach, NULL, NULL);
    109 
    110 static inline uint32_t
    111 sunxi_can_read(struct sunxi_can_softc *sc, bus_size_t o)
    112 {
    113 	return bus_space_read_4(sc->sc_bst, sc->sc_bsh, o);
    114 }
    115 
    116 static inline void
    117 sunxi_can_write(struct sunxi_can_softc *sc, bus_size_t o, uint32_t v)
    118 {
    119 	return bus_space_write_4(sc->sc_bst, sc->sc_bsh, o, v);
    120 }
    121 
    122 static int
    123 sunxi_can_match(device_t parent, cfdata_t cf, void *aux)
    124 {
    125 	struct fdt_attach_args * const faa = aux;
    126 
    127 	return of_compatible_match(faa->faa_phandle, compat_data);
    128 }
    129 
    130 static void
    131 sunxi_can_attach(device_t parent, device_t self, void *aux)
    132 {
    133 	struct sunxi_can_softc * const sc = device_private(self);
    134 	struct fdt_attach_args * const faa = aux;
    135 	struct ifnet *ifp;
    136 	const int phandle = faa->faa_phandle;
    137 	bus_addr_t addr;
    138 	bus_size_t size;
    139 	char intrstr[128];
    140 	struct clk *clk;
    141 	struct fdtbus_reset *rst;
    142 
    143 	sc->sc_dev = self;
    144 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_NET);
    145 
    146 	sc->sc_bst = faa->faa_bst;
    147 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
    148 		aprint_error(": couldn't get registers\n");
    149 		return;
    150 	}
    151 
    152 	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
    153 		aprint_error(": couldn't map registers\n");
    154 		return;
    155 	}
    156 
    157 	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
    158 		aprint_error(": failed to decode interrupt\n");
    159 		return;
    160 	}
    161 
    162 	if ((clk = fdtbus_clock_get_index(phandle, 0)) != NULL) {
    163 		if (clk_enable(clk) != 0) {
    164 			aprint_error(": couldn't enable clock\n");
    165 			return;
    166 		}
    167 	}
    168 
    169 	if ((rst = fdtbus_reset_get_index(phandle, 0)) != NULL) {
    170 		if (fdtbus_reset_deassert(rst) != 0) {
    171 			aprint_error(": couldn't de-assert reset\n");
    172 			return;
    173 		}
    174 	}
    175 
    176 	sc->sc_timecaps.cltc_prop_min = 0;
    177 	sc->sc_timecaps.cltc_prop_max = 0;
    178 	sc->sc_timecaps.cltc_ps1_min = 1;
    179 	sc->sc_timecaps.cltc_ps1_max = 16;
    180 	sc->sc_timecaps.cltc_ps2_min = 1;
    181 	sc->sc_timecaps.cltc_ps2_max = 8;
    182 	sc->sc_timecaps.cltc_sjw_max = 4;
    183 	sc->sc_timecaps.cltc_brp_min = 1;
    184 	sc->sc_timecaps.cltc_brp_max = 64;
    185 	sc->sc_timecaps.cltc_brp_inc = 1;
    186 	sc->sc_timecaps.cltc_clock_freq = clk_get_rate(clk);
    187 	sc->sc_timecaps.cltc_linkmode_caps =
    188 	    CAN_LINKMODE_3SAMPLES | CAN_LINKMODE_LISTENONLY |
    189 	    CAN_LINKMODE_LOOPBACK;
    190 	can_ifinit_timings(&sc->sc_cansc);
    191 	sc->sc_timings.clt_prop = 0;
    192 	sc->sc_timings.clt_sjw = 1;
    193 
    194 	aprint_naive("\n");
    195 	aprint_normal(": CAN bus controller\n");
    196 	aprint_debug_dev(self, ": clock freq %d\n",
    197 	    sc->sc_timecaps.cltc_clock_freq);
    198 
    199 	sunxi_can_enter_reset(sc);
    200 	/*
    201 	 * Disable and then clear all interrupts
    202 	 */
    203 	sunxi_can_write(sc, SUNXI_CAN_INTE_REG, 0);
    204 	sunxi_can_write(sc, SUNXI_CAN_INT_REG,
    205 	    sunxi_can_read(sc, SUNXI_CAN_INT_REG));
    206 
    207 	sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_NET, 0,
    208 	    sunxi_can_intr, sc, device_xname(self));
    209 	if (sc->sc_ih == NULL) {
    210 		aprint_error_dev(self, "failed to establish interrupt on %s\n",
    211 		    intrstr);
    212 		return;
    213 	}
    214 	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
    215 
    216 	ifp = if_alloc(IFT_OTHER);
    217 	sc->sc_ifp = ifp;
    218 	strlcpy(ifp->if_xname, device_xname(self), IFNAMSIZ);
    219 	ifp->if_softc = sc;
    220 	ifp->if_capabilities = 0;
    221 	ifp->if_flags = 0;
    222 	ifp->if_start = sunxi_can_ifstart;
    223 	ifp->if_ioctl = sunxi_can_ifioctl;
    224 	ifp->if_watchdog = sunxi_can_ifwatchdog;
    225 
    226 	/*
    227 	 * Attach the interface.
    228 	 */
    229 	can_ifattach(ifp);
    230 	if_deferred_start_init(ifp, NULL);
    231 	bpf_mtap_softint_init(ifp);
    232 	rnd_attach_source(&sc->sc_rnd_source, device_xname(self),
    233 	    RND_TYPE_NET, RND_FLAG_DEFAULT);
    234 #ifdef MBUFTRACE
    235 	ifp->if_mowner = malloc(sizeof(struct mowner), M_DEVBUF,
    236 	    M_WAITOK | M_ZERO);
    237 	strlcpy(ifp->if_mowner->mo_name, ifp->if_xname,
    238 		sizeof(ifp->if_mowner->mo_name));
    239 	MOWNER_ATTACH(ifp->if_mowner);
    240 #endif
    241 }
    242 
    243 static void
    244 sunxi_can_rx_intr(struct sunxi_can_softc *sc)
    245 {
    246 	uint32_t reg0v;
    247 	struct mbuf *m;
    248 	struct ifnet  *ifp = sc->sc_ifp;
    249 	struct can_frame *cf;
    250 	int dlc;
    251 	int regd, i;
    252 
    253 	KASSERT(mutex_owned(&sc->sc_intr_lock));
    254 	reg0v = sunxi_can_read(sc, SUNXI_CAN_TXBUF0_REG);
    255 	dlc = reg0v & SUNXI_CAN_TXBUF0_DL;
    256 
    257 	if (dlc > CAN_MAX_DLC) {
    258 		if_statinc(ifp, if_ierrors);
    259 		sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_REL_RX_BUF);
    260 		return;
    261 	}
    262 
    263 	m = m_gethdr(M_NOWAIT, MT_HEADER);
    264 	if (m == NULL) {
    265 		if_statinc(ifp, if_ierrors);
    266 		sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_REL_RX_BUF);
    267 		return;
    268 	}
    269 	cf = mtod(m, struct can_frame *);
    270 	memset(cf, 0, sizeof(struct can_frame));
    271 
    272 	cf->can_dlc = dlc;
    273 
    274 	if (reg0v & SUNXI_CAN_TXBUF0_EFF) {
    275 		cf->can_id =
    276 		    (sunxi_can_read(sc, SUNXI_CAN_TXBUF1_REG) << 21) |
    277 		    (sunxi_can_read(sc, SUNXI_CAN_TXBUF2_REG) << 13) |
    278 		    (sunxi_can_read(sc, SUNXI_CAN_TXBUF3_REG) << 5) |
    279 		    ((sunxi_can_read(sc, SUNXI_CAN_TXBUF4_REG) >> 3) & 0x1f);
    280 		cf->can_id |= CAN_EFF_FLAG;
    281 		regd = SUNXI_CAN_TXBUF5_REG;
    282 	} else {
    283 		cf->can_id =
    284 		    (sunxi_can_read(sc, SUNXI_CAN_TXBUF1_REG) << 3) |
    285 		    ((sunxi_can_read(sc, SUNXI_CAN_TXBUF2_REG) << 5) & 0x7);
    286 		regd = SUNXI_CAN_TXBUF3_REG;
    287 	}
    288 	if (reg0v & SUNXI_CAN_TXBUF0_RTR) {
    289 		cf->can_id |= CAN_RTR_FLAG;
    290 	} else {
    291 		for (i = 0; i < cf->can_dlc; i++) {
    292 			cf->data[i] = sunxi_can_read(sc, regd + i * 4);
    293 		}
    294 	}
    295 	sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_REL_RX_BUF);
    296 	m->m_len = m->m_pkthdr.len = CAN_MTU;
    297 	if_statadd(ifp, if_ibytes, m->m_len);
    298 	m_set_rcvif(m, ifp);
    299 	can_bpf_mtap(ifp, m, 1);
    300 	can_input(ifp, m);
    301 }
    302 
    303 static void
    304 sunxi_can_tx_intr(struct sunxi_can_softc *sc)
    305 {
    306 	struct ifnet * const ifp = sc->sc_ifp;
    307 	struct mbuf *m;
    308 
    309 	KASSERT(mutex_owned(&sc->sc_intr_lock));
    310 	if ((m = sc->sc_m_transmit) != NULL) {
    311 		if_statadd2(ifp, if_obytes, m->m_len, if_opackets, 1);
    312 		can_mbuf_tag_clean(m);
    313 		m_set_rcvif(m, ifp);
    314 		can_input(ifp, m); /* loopback */
    315 		sc->sc_m_transmit = NULL;
    316 		ifp->if_timer = 0;
    317 	}
    318 	if_schedule_deferred_start(ifp);
    319 }
    320 
    321 static int
    322 sunxi_can_tx_abort(struct sunxi_can_softc *sc)
    323 {
    324 	KASSERT(mutex_owned(&sc->sc_intr_lock));
    325 	if (sc->sc_m_transmit) {
    326 		m_freem(sc->sc_m_transmit);
    327 		sc->sc_m_transmit = NULL;
    328 		sc->sc_ifp->if_timer = 0;
    329 		/*
    330 		 * the transmit abort will trigger a TX interrupt
    331 		 * which will restart the queue as appropriate.
    332 		 */
    333 		sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_ABT_REQ);
    334 		return 1;
    335 	}
    336 	return 0;
    337 }
    338 
    339 static void
    340 sunxi_can_err_intr(struct sunxi_can_softc *sc, uint32_t irq, uint32_t sts)
    341 {
    342 	struct ifnet * const ifp = sc->sc_ifp;
    343 	KASSERT(mutex_owned(&sc->sc_intr_lock));
    344 	int txerr = 0;
    345 	uint32_t reg;
    346 
    347 	if (irq & SUNXI_CAN_INT_DATA_OR) {
    348 		if_statinc(ifp, if_ierrors);
    349 		sunxi_can_ifdown(sc);
    350 		sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_CLR_OR);
    351 		sunxi_can_ifup(sc);
    352 	}
    353 	if (irq & SUNXI_CAN_INT_ERR) {
    354 		reg = sunxi_can_read(sc, SUNXI_CAN_REC_REG);
    355 		printf("%s: ERR interrupt status 0x%x counters 0x%x\n",
    356 		    device_xname(sc->sc_dev), sts, reg);
    357 
    358 	}
    359 	if (irq & SUNXI_CAN_INT_BERR) {
    360 		if (sts & SUNXI_CAN_STA_TX)
    361 			txerr++;
    362 		if (sts & SUNXI_CAN_STA_RX)
    363 			if_statinc(ifp, if_ierrors);
    364 	}
    365 	if (irq & SUNXI_CAN_INT_ERR_PASSIVE) {
    366 		printf("%s: PASSV interrupt status 0x%x\n",
    367 		    device_xname(sc->sc_dev), sts);
    368 	}
    369 	if (irq & SUNXI_CAN_INT_ARB_LOST) {
    370 		txerr++;
    371 	}
    372 	if (txerr) {
    373 		if_statadd(ifp, if_oerrors, txerr);
    374 		(void) sunxi_can_tx_abort(sc);
    375 	}
    376 }
    377 
    378 int
    379 sunxi_can_intr(void *arg)
    380 {
    381 	struct sunxi_can_softc * const sc = arg;
    382 	int rv = 0;
    383 	int irq;
    384 
    385 	mutex_enter(&sc->sc_intr_lock);
    386 
    387 	while ((irq = sunxi_can_read(sc, SUNXI_CAN_INT_REG)) != 0) {
    388 		uint32_t sts = sunxi_can_read(sc, SUNXI_CAN_STA_REG);
    389 		rv = 1;
    390                 rnd_add_uint32(&sc->sc_rnd_source, irq);
    391 
    392 		if ((irq & (SUNXI_CAN_INT_RX_FLAG | SUNXI_CAN_INT_DATA_OR)) ==
    393 		    SUNXI_CAN_INT_RX_FLAG) {
    394 			while (sts & SUNXI_CAN_STA_RX_RDY) {
    395 				sunxi_can_rx_intr(sc);
    396 				sts = sunxi_can_read(sc, SUNXI_CAN_STA_REG);
    397 			}
    398 			/*
    399 			 * Don't write SUNXI_CAN_INT_RX_FLAG to the interrupt
    400 			 * register, this may clear the RX pending flag
    401 			 * while there is indeed a packet pending.
    402 			 * Reading packets should have cleared the RX interrupt,
    403 			 * so just restart the loop and re-read the interrupt
    404 			 * register. In the common case irq will now be 0.
    405 			 */
    406 			continue;
    407 		}
    408 		if (irq & SUNXI_CAN_INT_TX_FLAG) {
    409 			sunxi_can_tx_intr(sc);
    410 		}
    411 		if (irq & SUNXI_CAN_INT_ALLERRS) {
    412 			sunxi_can_err_intr(sc, irq, sts);
    413 		}
    414 		sunxi_can_write(sc, SUNXI_CAN_INT_REG, irq);
    415 	}
    416 	mutex_exit(&sc->sc_intr_lock);
    417 
    418 	return rv;
    419 }
    420 
    421 void
    422 sunxi_can_ifstart(struct ifnet *ifp)
    423 {
    424 	struct sunxi_can_softc * const sc = ifp->if_softc;
    425 	struct mbuf *m;
    426 	struct can_frame *cf;
    427 	int regd;
    428 	uint32_t reg0val;
    429 	int i;
    430 
    431 	mutex_enter(&sc->sc_intr_lock);
    432 	if (sc->sc_m_transmit != NULL)
    433 		goto out;
    434 
    435 	IF_DEQUEUE(&ifp->if_snd, m);
    436 
    437 	if (m == NULL)
    438 		goto out;
    439 
    440 	MCLAIM(m, ifp->if_mowner);
    441 	sc->sc_m_transmit = m;
    442 
    443 	KASSERT((m->m_flags & M_PKTHDR) != 0);
    444 	KASSERT(m->m_len == m->m_pkthdr.len);
    445 
    446 	cf = mtod(m, struct can_frame *);
    447 	reg0val = cf->can_dlc & SUNXI_CAN_TXBUF0_DL;
    448 	if (cf->can_id & CAN_RTR_FLAG)
    449 		reg0val |= SUNXI_CAN_TXBUF0_RTR;
    450 
    451 	if (cf->can_id & CAN_EFF_FLAG) {
    452 		reg0val |= SUNXI_CAN_TXBUF0_EFF;
    453 		sunxi_can_write(sc, SUNXI_CAN_TXBUF1_REG,
    454 		    (cf->can_id >> 21) & 0xff);
    455 		sunxi_can_write(sc, SUNXI_CAN_TXBUF2_REG,
    456 		    (cf->can_id >> 13) & 0xff);
    457 		sunxi_can_write(sc, SUNXI_CAN_TXBUF3_REG,
    458 		    (cf->can_id >> 5) & 0xff);
    459 		sunxi_can_write(sc, SUNXI_CAN_TXBUF4_REG,
    460 		    (cf->can_id << 3) & 0xf8);
    461 		regd = SUNXI_CAN_TXBUF5_REG;
    462 	} else {
    463 		sunxi_can_write(sc, SUNXI_CAN_TXBUF1_REG,
    464 		    (cf->can_id >> 3) & 0xff);
    465 		sunxi_can_write(sc, SUNXI_CAN_TXBUF2_REG,
    466 		    (cf->can_id << 5) & 0xe0);
    467 		regd = SUNXI_CAN_TXBUF3_REG;
    468 	}
    469 
    470 	for (i = 0; i < cf->can_dlc; i++) {
    471 		sunxi_can_write(sc, regd + i * 4, cf->data[i]);
    472 	}
    473 	sunxi_can_write(sc, SUNXI_CAN_TXBUF0_REG, reg0val);
    474 
    475 	if (sc->sc_linkmodes & CAN_LINKMODE_LOOPBACK) {
    476 		sunxi_can_write(sc, SUNXI_CAN_CMD_REG,
    477 			SUNXI_CAN_CMD_TANS_REQ | SUNXI_CAN_CMD_SELF_REQ);
    478 	} else {
    479 		sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_TANS_REQ);
    480 	}
    481 	ifp->if_timer = 5;
    482 	can_bpf_mtap(ifp, m, 0);
    483 out:
    484 	mutex_exit(&sc->sc_intr_lock);
    485 }
    486 
    487 static int
    488 sunxi_can_ifup(struct sunxi_can_softc * const sc)
    489 {
    490 	uint32_t reg;
    491 
    492 	/* setup timings and mode - has to be done in reset */
    493 	reg = SUNXI_CAN_MODSEL_RST;
    494 	if (sc->sc_linkmodes & CAN_LINKMODE_LISTENONLY)
    495 		reg |= SUNXI_CAN_MODSEL_LST_ONLY;
    496 
    497 	if (sc->sc_linkmodes & CAN_LINKMODE_LOOPBACK)
    498 		reg |= SUNXI_CAN_MODSEL_LB_MOD;
    499 
    500 	sunxi_can_write(sc, SUNXI_CAN_MODSEL_REG, reg);
    501 
    502 	reg = 0;
    503 	if (sc->sc_timings.clt_prop != 0)
    504 		return EINVAL;
    505 
    506 	if (sc->sc_timings.clt_brp > sc->sc_timecaps.cltc_brp_max ||
    507 	   sc->sc_timings.clt_brp < sc->sc_timecaps.cltc_brp_min)
    508 		return EINVAL;
    509 	reg |= (sc->sc_timings.clt_brp - 1) << 0;
    510 
    511 	if (sc->sc_timings.clt_ps1 > sc->sc_timecaps.cltc_ps1_max ||
    512 	   sc->sc_timings.clt_ps1 < sc->sc_timecaps.cltc_ps1_min)
    513 		return EINVAL;
    514 	reg |= (sc->sc_timings.clt_ps1 - 1) << 16;
    515 
    516 	if (sc->sc_timings.clt_ps2 > sc->sc_timecaps.cltc_ps2_max ||
    517 	   sc->sc_timings.clt_ps2 < sc->sc_timecaps.cltc_ps2_min)
    518 		return EINVAL;
    519 	reg |= (sc->sc_timings.clt_ps2 - 1) << 20;
    520 
    521 	if (sc->sc_timings.clt_sjw > sc->sc_timecaps.cltc_sjw_max ||
    522 	   sc->sc_timings.clt_sjw < 1)
    523 		return EINVAL;
    524 	reg |= (sc->sc_timings.clt_sjw - 1) << 14;
    525 
    526 	if (sc->sc_linkmodes & CAN_LINKMODE_3SAMPLES)
    527 		reg |= SUNXI_CAN_BUS_TIME_SAM;
    528 
    529 	sunxi_can_write(sc, SUNXI_CAN_BUS_TIME_REG, reg);
    530 
    531 	/* set filters to accept all frames */
    532 	sunxi_can_write(sc, SUNXI_CAN_ACPC, 0x00000000);
    533 	sunxi_can_write(sc, SUNXI_CAN_ACPM, 0xffffffff);
    534 
    535 	/* clear errors counter */
    536 	sunxi_can_write(sc, SUNXI_CAN_REC_REG, 0);
    537 
    538 	/* leave reset mode and enable interrupts */
    539 	sunxi_can_exit_reset(sc);
    540 	sunxi_can_write(sc, SUNXI_CAN_INTE_REG,
    541 	    SUNXI_CAN_INT_TX_FLAG | SUNXI_CAN_INT_RX_FLAG | SUNXI_CAN_INT_ALLERRS);
    542 	sc->sc_ifp->if_flags |= IFF_RUNNING;
    543 	return 0;
    544 }
    545 
    546 static void
    547 sunxi_can_ifdown(struct sunxi_can_softc * const sc)
    548 {
    549 	sc->sc_ifp->if_flags &= ~IFF_RUNNING;
    550 	sc->sc_ifp->if_timer = 0;
    551 	sunxi_can_enter_reset(sc);
    552 	sunxi_can_write(sc, SUNXI_CAN_INTE_REG, 0);
    553 	sunxi_can_write(sc, SUNXI_CAN_INT_REG,
    554 	    sunxi_can_read(sc, SUNXI_CAN_INT_REG));
    555 }
    556 
    557 static int
    558 sunxi_can_ifioctl(struct ifnet *ifp, u_long cmd, void *data)
    559 {
    560 	struct sunxi_can_softc * const sc = ifp->if_softc;
    561 	struct ifreq *ifr = (struct ifreq *)data;
    562 	int error = 0;
    563 
    564 	mutex_enter(&sc->sc_intr_lock);
    565 
    566 	switch (cmd) {
    567 	case SIOCINITIFADDR:
    568 		error = EAFNOSUPPORT;
    569 		break;
    570 	case SIOCSIFMTU:
    571 		if ((unsigned)ifr->ifr_mtu != sizeof(struct can_frame))
    572 			error = EINVAL;
    573 		break;
    574 	case SIOCADDMULTI:
    575 	case SIOCDELMULTI:
    576 		error = EAFNOSUPPORT;
    577 		break;
    578 	default:
    579 		error = ifioctl_common(ifp, cmd, data);
    580 		if (error == 0) {
    581 			if ((ifp->if_flags & IFF_UP) != 0 &&
    582 			    (ifp->if_flags & IFF_RUNNING) == 0) {
    583 				error = sunxi_can_ifup(sc);
    584 				if (error) {
    585 					ifp->if_flags &= ~IFF_UP;
    586 				}
    587 			} else if ((ifp->if_flags & IFF_UP) == 0 &&
    588 			    (ifp->if_flags & IFF_RUNNING) != 0) {
    589 				sunxi_can_ifdown(sc);
    590 			}
    591 		}
    592 		break;
    593 	}
    594 
    595 	mutex_exit(&sc->sc_intr_lock);
    596 	return error;
    597 }
    598 
    599 void
    600 sunxi_can_ifwatchdog(struct ifnet *ifp)
    601 {
    602 	struct sunxi_can_softc * const sc = ifp->if_softc;
    603 	printf("%s: watchdog timeout\n", device_xname(sc->sc_dev));
    604 
    605 	mutex_enter(&sc->sc_intr_lock);
    606 	printf("irq 0x%x en 0x%x mode 0x%x status 0x%x timings 0x%x err 0x%x\n",
    607 	    sunxi_can_read(sc, SUNXI_CAN_INT_REG),
    608 	    sunxi_can_read(sc, SUNXI_CAN_INTE_REG),
    609 	    sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG),
    610 	    sunxi_can_read(sc, SUNXI_CAN_STA_REG),
    611 	    sunxi_can_read(sc, SUNXI_CAN_BUS_TIME_REG),
    612 	    sunxi_can_read(sc, SUNXI_CAN_REC_REG));
    613 	/* if there is a transmit in progress abort */
    614 	if (sunxi_can_tx_abort(sc)) {
    615 		if_statinc(ifp, if_oerrors);
    616 	}
    617 	mutex_exit(&sc->sc_intr_lock);
    618 }
    619 
    620 static void
    621 sunxi_can_enter_reset(struct sunxi_can_softc *sc)
    622 {
    623 	int i;
    624 	uint32_t val;
    625 
    626 	for (i = 0; i < 1000; i++) {
    627 		val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG);
    628 		val |= SUNXI_CAN_MODSEL_RST;
    629 		sunxi_can_write(sc, SUNXI_CAN_MODSEL_REG, val);
    630 		val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG);
    631 		if (val & SUNXI_CAN_MODSEL_RST)
    632 			return;
    633 	}
    634 	printf("%s: couldn't enter reset mode\n", device_xname(sc->sc_dev));
    635 }
    636 
    637 static void
    638 sunxi_can_exit_reset(struct sunxi_can_softc *sc)
    639 {
    640 	int i;
    641 	uint32_t val;
    642 
    643 	for (i = 0; i < 1000; i++) {
    644 		val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG);
    645 		val &= ~SUNXI_CAN_MODSEL_RST;
    646 		sunxi_can_write(sc, SUNXI_CAN_MODSEL_REG, val);
    647 		val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG);
    648 		if ((val & SUNXI_CAN_MODSEL_RST) == 0)
    649 			return;
    650 	}
    651 	printf("%s: couldn't leave reset mode\n", device_xname(sc->sc_dev));
    652 }
    653