1 /* $NetBSD: sunxi_can.c,v 1.12 2022/09/27 06:14:13 skrll 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.12 2022/09/27 06:14:13 skrll 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 = kmem_zalloc(sizeof(*ifp->if_mowner), KM_SLEEP); 236 strlcpy(ifp->if_mowner->mo_name, ifp->if_xname, 237 sizeof(ifp->if_mowner->mo_name)); 238 MOWNER_ATTACH(ifp->if_mowner); 239 #endif 240 } 241 242 static void 243 sunxi_can_rx_intr(struct sunxi_can_softc *sc) 244 { 245 uint32_t reg0v; 246 struct mbuf *m; 247 struct ifnet *ifp = sc->sc_ifp; 248 struct can_frame *cf; 249 int dlc; 250 int regd, i; 251 252 KASSERT(mutex_owned(&sc->sc_intr_lock)); 253 reg0v = sunxi_can_read(sc, SUNXI_CAN_TXBUF0_REG); 254 dlc = reg0v & SUNXI_CAN_TXBUF0_DL; 255 256 if (dlc > CAN_MAX_DLC) { 257 if_statinc(ifp, if_ierrors); 258 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_REL_RX_BUF); 259 return; 260 } 261 262 m = m_gethdr(M_NOWAIT, MT_HEADER); 263 if (m == NULL) { 264 if_statinc(ifp, if_ierrors); 265 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_REL_RX_BUF); 266 return; 267 } 268 cf = mtod(m, struct can_frame *); 269 memset(cf, 0, sizeof(struct can_frame)); 270 271 cf->can_dlc = dlc; 272 273 if (reg0v & SUNXI_CAN_TXBUF0_EFF) { 274 cf->can_id = 275 (sunxi_can_read(sc, SUNXI_CAN_TXBUF1_REG) << 21) | 276 (sunxi_can_read(sc, SUNXI_CAN_TXBUF2_REG) << 13) | 277 (sunxi_can_read(sc, SUNXI_CAN_TXBUF3_REG) << 5) | 278 ((sunxi_can_read(sc, SUNXI_CAN_TXBUF4_REG) >> 3) & 0x1f); 279 cf->can_id |= CAN_EFF_FLAG; 280 regd = SUNXI_CAN_TXBUF5_REG; 281 } else { 282 cf->can_id = 283 (sunxi_can_read(sc, SUNXI_CAN_TXBUF1_REG) << 3) | 284 ((sunxi_can_read(sc, SUNXI_CAN_TXBUF2_REG) << 5) & 0x7); 285 regd = SUNXI_CAN_TXBUF3_REG; 286 } 287 if (reg0v & SUNXI_CAN_TXBUF0_RTR) { 288 cf->can_id |= CAN_RTR_FLAG; 289 } else { 290 for (i = 0; i < cf->can_dlc; i++) { 291 cf->data[i] = sunxi_can_read(sc, regd + i * 4); 292 } 293 } 294 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_REL_RX_BUF); 295 m->m_len = m->m_pkthdr.len = CAN_MTU; 296 if_statadd(ifp, if_ibytes, m->m_len); 297 m_set_rcvif(m, ifp); 298 can_bpf_mtap(ifp, m, 1); 299 can_input(ifp, m); 300 } 301 302 static void 303 sunxi_can_tx_intr(struct sunxi_can_softc *sc) 304 { 305 struct ifnet * const ifp = sc->sc_ifp; 306 struct mbuf *m; 307 308 KASSERT(mutex_owned(&sc->sc_intr_lock)); 309 if ((m = sc->sc_m_transmit) != NULL) { 310 if_statadd2(ifp, if_obytes, m->m_len, if_opackets, 1); 311 can_mbuf_tag_clean(m); 312 m_set_rcvif(m, ifp); 313 can_input(ifp, m); /* loopback */ 314 sc->sc_m_transmit = NULL; 315 ifp->if_timer = 0; 316 } 317 if_schedule_deferred_start(ifp); 318 } 319 320 static int 321 sunxi_can_tx_abort(struct sunxi_can_softc *sc) 322 { 323 KASSERT(mutex_owned(&sc->sc_intr_lock)); 324 if (sc->sc_m_transmit) { 325 m_freem(sc->sc_m_transmit); 326 sc->sc_m_transmit = NULL; 327 sc->sc_ifp->if_timer = 0; 328 /* 329 * the transmit abort will trigger a TX interrupt 330 * which will restart the queue as appropriate. 331 */ 332 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_ABT_REQ); 333 return 1; 334 } 335 return 0; 336 } 337 338 static void 339 sunxi_can_err_intr(struct sunxi_can_softc *sc, uint32_t irq, uint32_t sts) 340 { 341 struct ifnet * const ifp = sc->sc_ifp; 342 KASSERT(mutex_owned(&sc->sc_intr_lock)); 343 int txerr = 0; 344 uint32_t reg; 345 346 if (irq & SUNXI_CAN_INT_DATA_OR) { 347 if_statinc(ifp, if_ierrors); 348 sunxi_can_ifdown(sc); 349 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_CLR_OR); 350 sunxi_can_ifup(sc); 351 } 352 if (irq & SUNXI_CAN_INT_ERR) { 353 reg = sunxi_can_read(sc, SUNXI_CAN_REC_REG); 354 printf("%s: ERR interrupt status 0x%x counters 0x%x\n", 355 device_xname(sc->sc_dev), sts, reg); 356 357 } 358 if (irq & SUNXI_CAN_INT_BERR) { 359 if (sts & SUNXI_CAN_STA_TX) 360 txerr++; 361 if (sts & SUNXI_CAN_STA_RX) 362 if_statinc(ifp, if_ierrors); 363 } 364 if (irq & SUNXI_CAN_INT_ERR_PASSIVE) { 365 printf("%s: PASSV interrupt status 0x%x\n", 366 device_xname(sc->sc_dev), sts); 367 } 368 if (irq & SUNXI_CAN_INT_ARB_LOST) { 369 txerr++; 370 } 371 if (txerr) { 372 if_statadd(ifp, if_oerrors, txerr); 373 (void) sunxi_can_tx_abort(sc); 374 } 375 } 376 377 int 378 sunxi_can_intr(void *arg) 379 { 380 struct sunxi_can_softc * const sc = arg; 381 int rv = 0; 382 int irq; 383 384 mutex_enter(&sc->sc_intr_lock); 385 386 while ((irq = sunxi_can_read(sc, SUNXI_CAN_INT_REG)) != 0) { 387 uint32_t sts = sunxi_can_read(sc, SUNXI_CAN_STA_REG); 388 rv = 1; 389 rnd_add_uint32(&sc->sc_rnd_source, irq); 390 391 if ((irq & (SUNXI_CAN_INT_RX_FLAG | SUNXI_CAN_INT_DATA_OR)) == 392 SUNXI_CAN_INT_RX_FLAG) { 393 while (sts & SUNXI_CAN_STA_RX_RDY) { 394 sunxi_can_rx_intr(sc); 395 sts = sunxi_can_read(sc, SUNXI_CAN_STA_REG); 396 } 397 /* 398 * Don't write SUNXI_CAN_INT_RX_FLAG to the interrupt 399 * register, this may clear the RX pending flag 400 * while there is indeed a packet pending. 401 * Reading packets should have cleared the RX interrupt, 402 * so just restart the loop and re-read the interrupt 403 * register. In the common case irq will now be 0. 404 */ 405 continue; 406 } 407 if (irq & SUNXI_CAN_INT_TX_FLAG) { 408 sunxi_can_tx_intr(sc); 409 } 410 if (irq & SUNXI_CAN_INT_ALLERRS) { 411 sunxi_can_err_intr(sc, irq, sts); 412 } 413 sunxi_can_write(sc, SUNXI_CAN_INT_REG, irq); 414 } 415 mutex_exit(&sc->sc_intr_lock); 416 417 return rv; 418 } 419 420 void 421 sunxi_can_ifstart(struct ifnet *ifp) 422 { 423 struct sunxi_can_softc * const sc = ifp->if_softc; 424 struct mbuf *m; 425 struct can_frame *cf; 426 int regd; 427 uint32_t reg0val; 428 int i; 429 430 mutex_enter(&sc->sc_intr_lock); 431 if (sc->sc_m_transmit != NULL) 432 goto out; 433 434 IF_DEQUEUE(&ifp->if_snd, m); 435 436 if (m == NULL) 437 goto out; 438 439 MCLAIM(m, ifp->if_mowner); 440 sc->sc_m_transmit = m; 441 442 KASSERT((m->m_flags & M_PKTHDR) != 0); 443 KASSERT(m->m_len == m->m_pkthdr.len); 444 445 cf = mtod(m, struct can_frame *); 446 reg0val = cf->can_dlc & SUNXI_CAN_TXBUF0_DL; 447 if (cf->can_id & CAN_RTR_FLAG) 448 reg0val |= SUNXI_CAN_TXBUF0_RTR; 449 450 if (cf->can_id & CAN_EFF_FLAG) { 451 reg0val |= SUNXI_CAN_TXBUF0_EFF; 452 sunxi_can_write(sc, SUNXI_CAN_TXBUF1_REG, 453 (cf->can_id >> 21) & 0xff); 454 sunxi_can_write(sc, SUNXI_CAN_TXBUF2_REG, 455 (cf->can_id >> 13) & 0xff); 456 sunxi_can_write(sc, SUNXI_CAN_TXBUF3_REG, 457 (cf->can_id >> 5) & 0xff); 458 sunxi_can_write(sc, SUNXI_CAN_TXBUF4_REG, 459 (cf->can_id << 3) & 0xf8); 460 regd = SUNXI_CAN_TXBUF5_REG; 461 } else { 462 sunxi_can_write(sc, SUNXI_CAN_TXBUF1_REG, 463 (cf->can_id >> 3) & 0xff); 464 sunxi_can_write(sc, SUNXI_CAN_TXBUF2_REG, 465 (cf->can_id << 5) & 0xe0); 466 regd = SUNXI_CAN_TXBUF3_REG; 467 } 468 469 for (i = 0; i < cf->can_dlc; i++) { 470 sunxi_can_write(sc, regd + i * 4, cf->data[i]); 471 } 472 sunxi_can_write(sc, SUNXI_CAN_TXBUF0_REG, reg0val); 473 474 if (sc->sc_linkmodes & CAN_LINKMODE_LOOPBACK) { 475 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, 476 SUNXI_CAN_CMD_TANS_REQ | SUNXI_CAN_CMD_SELF_REQ); 477 } else { 478 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_TANS_REQ); 479 } 480 ifp->if_timer = 5; 481 can_bpf_mtap(ifp, m, 0); 482 out: 483 mutex_exit(&sc->sc_intr_lock); 484 } 485 486 static int 487 sunxi_can_ifup(struct sunxi_can_softc * const sc) 488 { 489 uint32_t reg; 490 491 /* setup timings and mode - has to be done in reset */ 492 reg = SUNXI_CAN_MODSEL_RST; 493 if (sc->sc_linkmodes & CAN_LINKMODE_LISTENONLY) 494 reg |= SUNXI_CAN_MODSEL_LST_ONLY; 495 496 if (sc->sc_linkmodes & CAN_LINKMODE_LOOPBACK) 497 reg |= SUNXI_CAN_MODSEL_LB_MOD; 498 499 sunxi_can_write(sc, SUNXI_CAN_MODSEL_REG, reg); 500 501 reg = 0; 502 if (sc->sc_timings.clt_prop != 0) 503 return EINVAL; 504 505 if (sc->sc_timings.clt_brp > sc->sc_timecaps.cltc_brp_max || 506 sc->sc_timings.clt_brp < sc->sc_timecaps.cltc_brp_min) 507 return EINVAL; 508 reg |= (sc->sc_timings.clt_brp - 1) << 0; 509 510 if (sc->sc_timings.clt_ps1 > sc->sc_timecaps.cltc_ps1_max || 511 sc->sc_timings.clt_ps1 < sc->sc_timecaps.cltc_ps1_min) 512 return EINVAL; 513 reg |= (sc->sc_timings.clt_ps1 - 1) << 16; 514 515 if (sc->sc_timings.clt_ps2 > sc->sc_timecaps.cltc_ps2_max || 516 sc->sc_timings.clt_ps2 < sc->sc_timecaps.cltc_ps2_min) 517 return EINVAL; 518 reg |= (sc->sc_timings.clt_ps2 - 1) << 20; 519 520 if (sc->sc_timings.clt_sjw > sc->sc_timecaps.cltc_sjw_max || 521 sc->sc_timings.clt_sjw < 1) 522 return EINVAL; 523 reg |= (sc->sc_timings.clt_sjw - 1) << 14; 524 525 if (sc->sc_linkmodes & CAN_LINKMODE_3SAMPLES) 526 reg |= SUNXI_CAN_BUS_TIME_SAM; 527 528 sunxi_can_write(sc, SUNXI_CAN_BUS_TIME_REG, reg); 529 530 /* set filters to accept all frames */ 531 sunxi_can_write(sc, SUNXI_CAN_ACPC, 0x00000000); 532 sunxi_can_write(sc, SUNXI_CAN_ACPM, 0xffffffff); 533 534 /* clear errors counter */ 535 sunxi_can_write(sc, SUNXI_CAN_REC_REG, 0); 536 537 /* leave reset mode and enable interrupts */ 538 sunxi_can_exit_reset(sc); 539 sunxi_can_write(sc, SUNXI_CAN_INTE_REG, 540 SUNXI_CAN_INT_TX_FLAG | SUNXI_CAN_INT_RX_FLAG | SUNXI_CAN_INT_ALLERRS); 541 sc->sc_ifp->if_flags |= IFF_RUNNING; 542 return 0; 543 } 544 545 static void 546 sunxi_can_ifdown(struct sunxi_can_softc * const sc) 547 { 548 sc->sc_ifp->if_flags &= ~IFF_RUNNING; 549 sc->sc_ifp->if_timer = 0; 550 sunxi_can_enter_reset(sc); 551 sunxi_can_write(sc, SUNXI_CAN_INTE_REG, 0); 552 sunxi_can_write(sc, SUNXI_CAN_INT_REG, 553 sunxi_can_read(sc, SUNXI_CAN_INT_REG)); 554 } 555 556 static int 557 sunxi_can_ifioctl(struct ifnet *ifp, u_long cmd, void *data) 558 { 559 struct sunxi_can_softc * const sc = ifp->if_softc; 560 struct ifreq *ifr = (struct ifreq *)data; 561 int error = 0; 562 563 mutex_enter(&sc->sc_intr_lock); 564 565 switch (cmd) { 566 case SIOCINITIFADDR: 567 error = EAFNOSUPPORT; 568 break; 569 case SIOCSIFMTU: 570 if ((unsigned)ifr->ifr_mtu != sizeof(struct can_frame)) 571 error = EINVAL; 572 break; 573 case SIOCADDMULTI: 574 case SIOCDELMULTI: 575 error = EAFNOSUPPORT; 576 break; 577 default: 578 error = ifioctl_common(ifp, cmd, data); 579 if (error == 0) { 580 if ((ifp->if_flags & IFF_UP) != 0 && 581 (ifp->if_flags & IFF_RUNNING) == 0) { 582 error = sunxi_can_ifup(sc); 583 if (error) { 584 ifp->if_flags &= ~IFF_UP; 585 } 586 } else if ((ifp->if_flags & IFF_UP) == 0 && 587 (ifp->if_flags & IFF_RUNNING) != 0) { 588 sunxi_can_ifdown(sc); 589 } 590 } 591 break; 592 } 593 594 mutex_exit(&sc->sc_intr_lock); 595 return error; 596 } 597 598 void 599 sunxi_can_ifwatchdog(struct ifnet *ifp) 600 { 601 struct sunxi_can_softc * const sc = ifp->if_softc; 602 printf("%s: watchdog timeout\n", device_xname(sc->sc_dev)); 603 604 mutex_enter(&sc->sc_intr_lock); 605 printf("irq 0x%x en 0x%x mode 0x%x status 0x%x timings 0x%x err 0x%x\n", 606 sunxi_can_read(sc, SUNXI_CAN_INT_REG), 607 sunxi_can_read(sc, SUNXI_CAN_INTE_REG), 608 sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG), 609 sunxi_can_read(sc, SUNXI_CAN_STA_REG), 610 sunxi_can_read(sc, SUNXI_CAN_BUS_TIME_REG), 611 sunxi_can_read(sc, SUNXI_CAN_REC_REG)); 612 /* if there is a transmit in progress abort */ 613 if (sunxi_can_tx_abort(sc)) { 614 if_statinc(ifp, if_oerrors); 615 } 616 mutex_exit(&sc->sc_intr_lock); 617 } 618 619 static void 620 sunxi_can_enter_reset(struct sunxi_can_softc *sc) 621 { 622 int i; 623 uint32_t val; 624 625 for (i = 0; i < 1000; i++) { 626 val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); 627 val |= SUNXI_CAN_MODSEL_RST; 628 sunxi_can_write(sc, SUNXI_CAN_MODSEL_REG, val); 629 val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); 630 if (val & SUNXI_CAN_MODSEL_RST) 631 return; 632 } 633 printf("%s: couldn't enter reset mode\n", device_xname(sc->sc_dev)); 634 } 635 636 static void 637 sunxi_can_exit_reset(struct sunxi_can_softc *sc) 638 { 639 int i; 640 uint32_t val; 641 642 for (i = 0; i < 1000; i++) { 643 val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); 644 val &= ~SUNXI_CAN_MODSEL_RST; 645 sunxi_can_write(sc, SUNXI_CAN_MODSEL_REG, val); 646 val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); 647 if ((val & SUNXI_CAN_MODSEL_RST) == 0) 648 return; 649 } 650 printf("%s: couldn't leave reset mode\n", device_xname(sc->sc_dev)); 651 } 652