1 /* $NetBSD: sunxi_can.c,v 1.1 2018/03/07 20:55:31 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.1 2018/03/07 20:55:31 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 of_compat_data compat_data[] = { 89 {"allwinner,sun4i-a10-can", 0}, 90 {NULL} 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 105 CFATTACH_DECL_NEW(sunxi_can, sizeof(struct sunxi_can_softc), 106 sunxi_can_match, sunxi_can_attach, NULL, NULL); 107 108 static inline uint32_t 109 sunxi_can_read(struct sunxi_can_softc *sc, bus_size_t o) 110 { 111 return bus_space_read_4(sc->sc_bst, sc->sc_bsh, o); 112 } 113 114 static inline void 115 sunxi_can_write(struct sunxi_can_softc *sc, bus_size_t o, uint32_t v) 116 { 117 return bus_space_write_4(sc->sc_bst, sc->sc_bsh, o, v); 118 } 119 120 static int 121 sunxi_can_match(device_t parent, cfdata_t cf, void *aux) 122 { 123 struct fdt_attach_args * const faa = aux; 124 125 return of_match_compat_data(faa->faa_phandle, compat_data); 126 } 127 128 static void 129 sunxi_can_attach(device_t parent, device_t self, void *aux) 130 { 131 struct sunxi_can_softc * const sc = device_private(self); 132 struct fdt_attach_args * const faa = aux; 133 struct ifnet *ifp; 134 const int phandle = faa->faa_phandle; 135 bus_addr_t addr; 136 bus_size_t size; 137 char intrstr[128]; 138 struct clk *clk; 139 struct fdtbus_reset *rst; 140 141 sc->sc_dev = self; 142 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_NET); 143 144 sc->sc_bst = faa->faa_bst; 145 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 146 aprint_error(": couldn't get registers\n"); 147 return; 148 } 149 150 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 151 aprint_error(": couldn't map registers\n"); 152 return; 153 } 154 155 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 156 aprint_error(": failed to decode interrupt\n"); 157 return; 158 } 159 160 if ((clk = fdtbus_clock_get_index(phandle, 0)) != NULL) { 161 if (clk_enable(clk) != 0) { 162 aprint_error(": couldn't enable clock\n"); 163 return; 164 } 165 } 166 167 if ((rst = fdtbus_reset_get_index(phandle, 0)) != NULL) { 168 if (fdtbus_reset_deassert(rst) != 0) { 169 aprint_error(": couldn't de-assert reset\n"); 170 return; 171 } 172 } 173 174 sc->sc_timecaps.cltc_prop_min = 0; 175 sc->sc_timecaps.cltc_prop_max = 0; 176 sc->sc_timecaps.cltc_ps1_min = 1; 177 sc->sc_timecaps.cltc_ps1_max = 16; 178 sc->sc_timecaps.cltc_ps2_min = 1; 179 sc->sc_timecaps.cltc_ps2_max = 8; 180 sc->sc_timecaps.cltc_sjw_max = 4; 181 sc->sc_timecaps.cltc_brp_min = 1; 182 sc->sc_timecaps.cltc_brp_max = 64; 183 sc->sc_timecaps.cltc_brp_inc = 1; 184 sc->sc_timecaps.cltc_clock_freq = clk_get_rate(clk); 185 sc->sc_timecaps.cltc_linkmode_caps = 186 CAN_LINKMODE_3SAMPLES | CAN_LINKMODE_LISTENONLY | 187 CAN_LINKMODE_LOOPBACK; 188 can_ifinit_timings(&sc->sc_cansc); 189 sc->sc_timings.clt_prop = 0; 190 sc->sc_timings.clt_sjw = 1; 191 192 aprint_naive("\n"); 193 aprint_normal(": CAN bus controller\n"); 194 aprint_debug_dev(self, ": clock freq %d\n", 195 sc->sc_timecaps.cltc_clock_freq); 196 197 sunxi_can_enter_reset(sc); 198 /* 199 * Disable and then clear all interrupts 200 */ 201 sunxi_can_write(sc, SUNXI_CAN_INTE_REG, 0); 202 sunxi_can_write(sc, SUNXI_CAN_INT_REG, 203 sunxi_can_read(sc, SUNXI_CAN_INT_REG)); 204 205 sc->sc_ih = fdtbus_intr_establish(phandle, 0, IPL_NET, 0, 206 sunxi_can_intr, sc); 207 if (sc->sc_ih == NULL) { 208 aprint_error_dev(self, "failed to establish interrupt on %s\n", 209 intrstr); 210 return; 211 } 212 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 213 214 ifp = if_alloc(IFT_OTHER); 215 sc->sc_ifp = ifp; 216 strlcpy(ifp->if_xname, device_xname(self), IFNAMSIZ); 217 ifp->if_softc = sc; 218 ifp->if_capabilities = 0; 219 ifp->if_flags = 0; 220 ifp->if_start = sunxi_can_ifstart; 221 ifp->if_ioctl = sunxi_can_ifioctl; 222 ifp->if_watchdog = sunxi_can_ifwatchdog; 223 224 /* 225 * Attach the interface. 226 */ 227 can_ifattach(ifp); 228 if_deferred_start_init(ifp, NULL); 229 bpf_mtap_softint_init(ifp); 230 rnd_attach_source(&sc->sc_rnd_source, device_xname(self), 231 RND_TYPE_NET, RND_FLAG_DEFAULT); 232 #ifdef MBUFTRACE 233 ifp->if_mowner = malloc(sizeof(struct mowner), M_DEVBUF, 234 M_WAITOK | M_ZERO); 235 strlcpy(ifp->if_mowner->mo_name, ifp->if_xname, 236 sizeof(ifp->if_mowner->mo_name)); 237 MOWNER_ATTACH(ifp->if_mowner); 238 #endif 239 } 240 241 static void 242 sunxi_can_rx_intr(struct sunxi_can_softc *sc) 243 { 244 uint32_t reg0v; 245 struct mbuf *m; 246 struct ifnet *ifp = sc->sc_ifp; 247 struct can_frame *cf; 248 int dlc; 249 int regd, i; 250 251 KASSERT(mutex_owned(&sc->sc_intr_lock)); 252 reg0v = sunxi_can_read(sc, SUNXI_CAN_TXBUF0_REG); 253 dlc = reg0v & SUNXI_CAN_TXBUF0_DL; 254 255 if (dlc > CAN_MAX_DLC) { 256 ifp->if_ierrors++; 257 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_REL_RX_BUF); 258 return; 259 } 260 261 m = m_gethdr(M_NOWAIT, MT_HEADER); 262 if (m == NULL) { 263 ifp->if_ierrors++; 264 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_REL_RX_BUF); 265 return; 266 } 267 cf = mtod(m, struct can_frame *); 268 memset(cf, 0, sizeof(struct can_frame)); 269 270 cf->can_dlc = dlc; 271 272 if (reg0v & SUNXI_CAN_TXBUF0_EFF) { 273 cf->can_id = 274 (sunxi_can_read(sc, SUNXI_CAN_TXBUF1_REG) << 21) | 275 (sunxi_can_read(sc, SUNXI_CAN_TXBUF2_REG) << 13) | 276 (sunxi_can_read(sc, SUNXI_CAN_TXBUF3_REG) << 5) | 277 ((sunxi_can_read(sc, SUNXI_CAN_TXBUF4_REG) >> 3) & 0x1f); 278 cf->can_id |= CAN_EFF_FLAG; 279 regd = SUNXI_CAN_TXBUF5_REG; 280 } else { 281 cf->can_id = 282 (sunxi_can_read(sc, SUNXI_CAN_TXBUF1_REG) << 3) | 283 ((sunxi_can_read(sc, SUNXI_CAN_TXBUF2_REG) << 5) & 0x7); 284 regd = SUNXI_CAN_TXBUF3_REG; 285 } 286 if (reg0v & SUNXI_CAN_TXBUF0_RTR) { 287 cf->can_id |= CAN_RTR_FLAG; 288 } else { 289 for (i = 0; i < cf->can_dlc; i++) { 290 cf->data[i] = sunxi_can_read(sc, regd + i * 4); 291 } 292 } 293 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_REL_RX_BUF); 294 m->m_len = m->m_pkthdr.len = CAN_MTU; 295 ifp->if_ipackets++; 296 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 ifp->if_obytes += m->m_len; 311 ifp->if_opackets++; 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 ifp->if_flags &= ~IFF_OACTIVE; 319 if_schedule_deferred_start(ifp); 320 } 321 322 static int 323 sunxi_can_tx_abort(struct sunxi_can_softc *sc) 324 { 325 KASSERT(mutex_owned(&sc->sc_intr_lock)); 326 if (sc->sc_m_transmit) { 327 m_freem(sc->sc_m_transmit); 328 sc->sc_m_transmit = NULL; 329 sc->sc_ifp->if_timer = 0; 330 /* 331 * the transmit abort will trigger a TX interrupt 332 * which will restart the queue or cleae OACTIVE, 333 * as appropriate 334 */ 335 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_ABT_REQ); 336 return 1; 337 } 338 return 0; 339 } 340 341 static void 342 sunxi_can_err_intr(struct sunxi_can_softc *sc, uint32_t irq, uint32_t sts) 343 { 344 struct ifnet * const ifp = sc->sc_ifp; 345 KASSERT(mutex_owned(&sc->sc_intr_lock)); 346 int txerr = 0; 347 uint32_t reg; 348 349 if (irq & SUNXI_CAN_INT_DATA_OR) { 350 ifp->if_ierrors++; 351 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_CLR_OR); 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 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 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 391 if (irq & SUNXI_CAN_INT_TX_FLAG) { 392 sunxi_can_tx_intr(sc); 393 } 394 if (irq & SUNXI_CAN_INT_RX_FLAG) { 395 while (sts & SUNXI_CAN_STA_RX_RDY) { 396 sunxi_can_rx_intr(sc); 397 sts = sunxi_can_read(sc, SUNXI_CAN_STA_REG); 398 } 399 } 400 if (irq & SUNXI_CAN_INT_ALLERRS) { 401 sunxi_can_err_intr(sc, irq, sts); 402 } 403 sunxi_can_write(sc, SUNXI_CAN_INT_REG, irq); 404 rnd_add_uint32(&sc->sc_rnd_source, irq); 405 406 } 407 mutex_exit(&sc->sc_intr_lock); 408 409 return rv; 410 } 411 412 void 413 sunxi_can_ifstart(struct ifnet *ifp) 414 { 415 struct sunxi_can_softc * const sc = ifp->if_softc; 416 struct mbuf *m; 417 struct can_frame *cf; 418 int regd; 419 uint32_t reg0val; 420 int i; 421 422 mutex_enter(&sc->sc_intr_lock); 423 if (ifp->if_flags & IFF_OACTIVE) 424 goto out; 425 426 IF_DEQUEUE(&ifp->if_snd, m); 427 428 if (m == NULL) 429 goto out; 430 431 MCLAIM(m, ifp->if_mowner); 432 sc->sc_m_transmit = m; 433 434 KASSERT((m->m_flags & M_PKTHDR) != 0); 435 KASSERT(m->m_len == m->m_pkthdr.len); 436 437 cf = mtod(m, struct can_frame *); 438 reg0val = cf->can_dlc & SUNXI_CAN_TXBUF0_DL; 439 if (cf->can_id & CAN_RTR_FLAG) 440 reg0val |= SUNXI_CAN_TXBUF0_RTR; 441 442 if (cf->can_id & CAN_EFF_FLAG) { 443 reg0val |= SUNXI_CAN_TXBUF0_EFF; 444 sunxi_can_write(sc, SUNXI_CAN_TXBUF1_REG, 445 (cf->can_id >> 21) & 0xff); 446 sunxi_can_write(sc, SUNXI_CAN_TXBUF2_REG, 447 (cf->can_id >> 13) & 0xff); 448 sunxi_can_write(sc, SUNXI_CAN_TXBUF3_REG, 449 (cf->can_id >> 5) & 0xff); 450 sunxi_can_write(sc, SUNXI_CAN_TXBUF4_REG, 451 (cf->can_id << 3) & 0xf8); 452 regd = SUNXI_CAN_TXBUF5_REG; 453 } else { 454 sunxi_can_write(sc, SUNXI_CAN_TXBUF1_REG, 455 (cf->can_id >> 3) & 0xff); 456 sunxi_can_write(sc, SUNXI_CAN_TXBUF2_REG, 457 (cf->can_id << 5) & 0xe0); 458 regd = SUNXI_CAN_TXBUF3_REG; 459 } 460 461 for (i = 0; i < cf->can_dlc; i++) { 462 sunxi_can_write(sc, regd + i * 4, cf->data[i]); 463 } 464 sunxi_can_write(sc, SUNXI_CAN_TXBUF0_REG, reg0val); 465 466 if (sc->sc_linkmodes & CAN_LINKMODE_LOOPBACK) { 467 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, 468 SUNXI_CAN_CMD_TANS_REQ | SUNXI_CAN_CMD_SELF_REQ); 469 } else { 470 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_TANS_REQ); 471 } 472 ifp->if_flags |= IFF_OACTIVE; 473 ifp->if_timer = 5; 474 can_bpf_mtap(ifp, m, 0); 475 out: 476 mutex_exit(&sc->sc_intr_lock); 477 } 478 479 static int 480 sunxi_can_ifup(struct sunxi_can_softc * const sc) 481 { 482 uint32_t reg; 483 484 /* setup timings and mode - has to be done in reset */ 485 reg = SUNXI_CAN_MODSEL_RST; 486 if (sc->sc_linkmodes & CAN_LINKMODE_LISTENONLY) 487 reg |= SUNXI_CAN_MODSEL_LST_ONLY; 488 489 if (sc->sc_linkmodes & CAN_LINKMODE_LOOPBACK) 490 reg |= SUNXI_CAN_MODSEL_LB_MOD; 491 492 sunxi_can_write(sc, SUNXI_CAN_MODSEL_REG, reg); 493 494 reg = 0; 495 if (sc->sc_timings.clt_prop != 0) 496 return EINVAL; 497 498 if (sc->sc_timings.clt_brp > sc->sc_timecaps.cltc_brp_max || 499 sc->sc_timings.clt_brp < sc->sc_timecaps.cltc_brp_min) 500 return EINVAL; 501 reg |= (sc->sc_timings.clt_brp - 1) << 0; 502 503 if (sc->sc_timings.clt_ps1 > sc->sc_timecaps.cltc_ps1_max || 504 sc->sc_timings.clt_ps1 < sc->sc_timecaps.cltc_ps1_min) 505 return EINVAL; 506 reg |= (sc->sc_timings.clt_ps1 - 1) << 16; 507 508 if (sc->sc_timings.clt_ps2 > sc->sc_timecaps.cltc_ps2_max || 509 sc->sc_timings.clt_ps2 < sc->sc_timecaps.cltc_ps2_min) 510 return EINVAL; 511 reg |= (sc->sc_timings.clt_ps2 - 1) << 20; 512 513 if (sc->sc_timings.clt_sjw > sc->sc_timecaps.cltc_sjw_max || 514 sc->sc_timings.clt_sjw < 1) 515 return EINVAL; 516 reg |= (sc->sc_timings.clt_sjw - 1) << 14; 517 518 if (sc->sc_linkmodes & CAN_LINKMODE_3SAMPLES) 519 reg |= SUNXI_CAN_BUS_TIME_SAM; 520 521 sunxi_can_write(sc, SUNXI_CAN_BUS_TIME_REG, reg); 522 523 /* set filters to accept all frames */ 524 sunxi_can_write(sc, SUNXI_CAN_ACPC, 0x00000000); 525 sunxi_can_write(sc, SUNXI_CAN_ACPM, 0xffffffff); 526 527 /* clear errors counter */ 528 sunxi_can_write(sc, SUNXI_CAN_REC_REG, 0); 529 530 /* leave reset mode and enable interrupts */ 531 sunxi_can_exit_reset(sc); 532 sunxi_can_write(sc, SUNXI_CAN_INTE_REG, 533 SUNXI_CAN_INT_TX_FLAG | SUNXI_CAN_INT_RX_FLAG | SUNXI_CAN_INT_ALLERRS); 534 sc->sc_ifp->if_flags |= IFF_RUNNING; 535 return 0; 536 } 537 538 static void 539 sunxi_can_ifdown(struct sunxi_can_softc * const sc) 540 { 541 sc->sc_ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 542 sc->sc_ifp->if_timer = 0; 543 sunxi_can_enter_reset(sc); 544 sunxi_can_write(sc, SUNXI_CAN_INTE_REG, 0); 545 sunxi_can_write(sc, SUNXI_CAN_INT_REG, 546 sunxi_can_read(sc, SUNXI_CAN_INT_REG)); 547 } 548 549 static int 550 sunxi_can_ifioctl(struct ifnet *ifp, u_long cmd, void *data) 551 { 552 struct sunxi_can_softc * const sc = ifp->if_softc; 553 struct ifreq *ifr = (struct ifreq *)data; 554 int error = 0; 555 556 mutex_enter(&sc->sc_intr_lock); 557 558 switch (cmd) { 559 case SIOCINITIFADDR: 560 error = EAFNOSUPPORT; 561 break; 562 case SIOCSIFMTU: 563 if ((unsigned)ifr->ifr_mtu != sizeof(struct can_frame)) 564 error = EINVAL; 565 break; 566 case SIOCADDMULTI: 567 case SIOCDELMULTI: 568 error = EAFNOSUPPORT; 569 break; 570 default: 571 error = ifioctl_common(ifp, cmd, data); 572 if (error == 0) { 573 if ((ifp->if_flags & IFF_UP) != 0 && 574 (ifp->if_flags & IFF_RUNNING) == 0) { 575 error = sunxi_can_ifup(sc); 576 if (error) { 577 ifp->if_flags &= ~IFF_UP; 578 } 579 } else if ((ifp->if_flags & IFF_UP) == 0 && 580 (ifp->if_flags & IFF_RUNNING) != 0) { 581 sunxi_can_ifdown(sc); 582 } 583 } 584 break; 585 } 586 587 mutex_exit(&sc->sc_intr_lock); 588 return error; 589 } 590 591 void 592 sunxi_can_ifwatchdog(struct ifnet *ifp) 593 { 594 struct sunxi_can_softc * const sc = ifp->if_softc; 595 printf("%s: watchdog timeout\n", device_xname(sc->sc_dev)); 596 597 mutex_enter(&sc->sc_intr_lock); 598 printf("irq 0x%x en 0x%x mode 0x%x status 0x%x timings 0x%x err 0x%x\n", 599 sunxi_can_read(sc, SUNXI_CAN_INT_REG), 600 sunxi_can_read(sc, SUNXI_CAN_INTE_REG), 601 sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG), 602 sunxi_can_read(sc, SUNXI_CAN_STA_REG), 603 sunxi_can_read(sc, SUNXI_CAN_BUS_TIME_REG), 604 sunxi_can_read(sc, SUNXI_CAN_REC_REG)); 605 /* if there is a transmit in progress abort */ 606 if (sunxi_can_tx_abort(sc)) { 607 ifp->if_oerrors++; 608 } 609 mutex_exit(&sc->sc_intr_lock); 610 } 611 612 static void 613 sunxi_can_enter_reset(struct sunxi_can_softc *sc) 614 { 615 int i; 616 uint32_t val; 617 618 for (i = 0; i < 1000; i++) { 619 val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); 620 val |= SUNXI_CAN_MODSEL_RST; 621 sunxi_can_write(sc, SUNXI_CAN_MODSEL_REG, val); 622 val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); 623 if (val & SUNXI_CAN_MODSEL_RST) 624 return; 625 } 626 printf("%s: couldn't enter reset mode\n", device_xname(sc->sc_dev)); 627 } 628 629 static void 630 sunxi_can_exit_reset(struct sunxi_can_softc *sc) 631 { 632 int i; 633 uint32_t val; 634 635 for (i = 0; i < 1000; i++) { 636 val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); 637 val &= ~SUNXI_CAN_MODSEL_RST; 638 sunxi_can_write(sc, SUNXI_CAN_MODSEL_REG, val); 639 val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); 640 if ((val & SUNXI_CAN_MODSEL_RST) == 0) 641 return; 642 } 643 printf("%s: couldn't leave reset mode\n", device_xname(sc->sc_dev)); 644 } 645