1 /* $NetBSD: sunxi_can.c,v 1.8 2021/01/27 03:10:20 thorpej 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.8 2021/01/27 03:10:20 thorpej 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 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_compatible_match(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_xname(phandle, 0, IPL_NET, 0, 206 sunxi_can_intr, sc, device_xname(self)); 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 if_statinc(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 if_statinc(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 if_statadd(ifp, if_ibytes, m->m_len); 296 m_set_rcvif(m, ifp); 297 can_bpf_mtap(ifp, m, 1); 298 can_input(ifp, m); 299 } 300 301 static void 302 sunxi_can_tx_intr(struct sunxi_can_softc *sc) 303 { 304 struct ifnet * const ifp = sc->sc_ifp; 305 struct mbuf *m; 306 307 KASSERT(mutex_owned(&sc->sc_intr_lock)); 308 if ((m = sc->sc_m_transmit) != NULL) { 309 if_statadd2(ifp, if_obytes, m->m_len, if_opackets, 1); 310 can_mbuf_tag_clean(m); 311 m_set_rcvif(m, ifp); 312 can_input(ifp, m); /* loopback */ 313 sc->sc_m_transmit = NULL; 314 ifp->if_timer = 0; 315 } 316 ifp->if_flags &= ~IFF_OACTIVE; 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 or cleae OACTIVE, 331 * 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_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_CLR_OR); 350 } 351 if (irq & SUNXI_CAN_INT_ERR) { 352 reg = sunxi_can_read(sc, SUNXI_CAN_REC_REG); 353 printf("%s: ERR interrupt status 0x%x counters 0x%x\n", 354 device_xname(sc->sc_dev), sts, reg); 355 356 } 357 if (irq & SUNXI_CAN_INT_BERR) { 358 if (sts & SUNXI_CAN_STA_TX) 359 txerr++; 360 if (sts & SUNXI_CAN_STA_RX) 361 if_statinc(ifp, if_ierrors); 362 } 363 if (irq & SUNXI_CAN_INT_ERR_PASSIVE) { 364 printf("%s: PASSV interrupt status 0x%x\n", 365 device_xname(sc->sc_dev), sts); 366 } 367 if (irq & SUNXI_CAN_INT_ARB_LOST) { 368 txerr++; 369 } 370 if (txerr) { 371 if_statadd(ifp, if_oerrors, txerr); 372 (void) sunxi_can_tx_abort(sc); 373 } 374 } 375 376 int 377 sunxi_can_intr(void *arg) 378 { 379 struct sunxi_can_softc * const sc = arg; 380 int rv = 0; 381 int irq; 382 383 mutex_enter(&sc->sc_intr_lock); 384 385 while ((irq = sunxi_can_read(sc, SUNXI_CAN_INT_REG)) != 0) { 386 uint32_t sts = sunxi_can_read(sc, SUNXI_CAN_STA_REG); 387 rv = 1; 388 389 if (irq & SUNXI_CAN_INT_TX_FLAG) { 390 sunxi_can_tx_intr(sc); 391 } 392 if (irq & 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 if (irq & SUNXI_CAN_INT_ALLERRS) { 399 sunxi_can_err_intr(sc, irq, sts); 400 } 401 sunxi_can_write(sc, SUNXI_CAN_INT_REG, irq); 402 rnd_add_uint32(&sc->sc_rnd_source, irq); 403 404 } 405 mutex_exit(&sc->sc_intr_lock); 406 407 return rv; 408 } 409 410 void 411 sunxi_can_ifstart(struct ifnet *ifp) 412 { 413 struct sunxi_can_softc * const sc = ifp->if_softc; 414 struct mbuf *m; 415 struct can_frame *cf; 416 int regd; 417 uint32_t reg0val; 418 int i; 419 420 mutex_enter(&sc->sc_intr_lock); 421 if (ifp->if_flags & IFF_OACTIVE) 422 goto out; 423 424 IF_DEQUEUE(&ifp->if_snd, m); 425 426 if (m == NULL) 427 goto out; 428 429 MCLAIM(m, ifp->if_mowner); 430 sc->sc_m_transmit = m; 431 432 KASSERT((m->m_flags & M_PKTHDR) != 0); 433 KASSERT(m->m_len == m->m_pkthdr.len); 434 435 cf = mtod(m, struct can_frame *); 436 reg0val = cf->can_dlc & SUNXI_CAN_TXBUF0_DL; 437 if (cf->can_id & CAN_RTR_FLAG) 438 reg0val |= SUNXI_CAN_TXBUF0_RTR; 439 440 if (cf->can_id & CAN_EFF_FLAG) { 441 reg0val |= SUNXI_CAN_TXBUF0_EFF; 442 sunxi_can_write(sc, SUNXI_CAN_TXBUF1_REG, 443 (cf->can_id >> 21) & 0xff); 444 sunxi_can_write(sc, SUNXI_CAN_TXBUF2_REG, 445 (cf->can_id >> 13) & 0xff); 446 sunxi_can_write(sc, SUNXI_CAN_TXBUF3_REG, 447 (cf->can_id >> 5) & 0xff); 448 sunxi_can_write(sc, SUNXI_CAN_TXBUF4_REG, 449 (cf->can_id << 3) & 0xf8); 450 regd = SUNXI_CAN_TXBUF5_REG; 451 } else { 452 sunxi_can_write(sc, SUNXI_CAN_TXBUF1_REG, 453 (cf->can_id >> 3) & 0xff); 454 sunxi_can_write(sc, SUNXI_CAN_TXBUF2_REG, 455 (cf->can_id << 5) & 0xe0); 456 regd = SUNXI_CAN_TXBUF3_REG; 457 } 458 459 for (i = 0; i < cf->can_dlc; i++) { 460 sunxi_can_write(sc, regd + i * 4, cf->data[i]); 461 } 462 sunxi_can_write(sc, SUNXI_CAN_TXBUF0_REG, reg0val); 463 464 if (sc->sc_linkmodes & CAN_LINKMODE_LOOPBACK) { 465 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, 466 SUNXI_CAN_CMD_TANS_REQ | SUNXI_CAN_CMD_SELF_REQ); 467 } else { 468 sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_TANS_REQ); 469 } 470 ifp->if_flags |= IFF_OACTIVE; 471 ifp->if_timer = 5; 472 can_bpf_mtap(ifp, m, 0); 473 out: 474 mutex_exit(&sc->sc_intr_lock); 475 } 476 477 static int 478 sunxi_can_ifup(struct sunxi_can_softc * const sc) 479 { 480 uint32_t reg; 481 482 /* setup timings and mode - has to be done in reset */ 483 reg = SUNXI_CAN_MODSEL_RST; 484 if (sc->sc_linkmodes & CAN_LINKMODE_LISTENONLY) 485 reg |= SUNXI_CAN_MODSEL_LST_ONLY; 486 487 if (sc->sc_linkmodes & CAN_LINKMODE_LOOPBACK) 488 reg |= SUNXI_CAN_MODSEL_LB_MOD; 489 490 sunxi_can_write(sc, SUNXI_CAN_MODSEL_REG, reg); 491 492 reg = 0; 493 if (sc->sc_timings.clt_prop != 0) 494 return EINVAL; 495 496 if (sc->sc_timings.clt_brp > sc->sc_timecaps.cltc_brp_max || 497 sc->sc_timings.clt_brp < sc->sc_timecaps.cltc_brp_min) 498 return EINVAL; 499 reg |= (sc->sc_timings.clt_brp - 1) << 0; 500 501 if (sc->sc_timings.clt_ps1 > sc->sc_timecaps.cltc_ps1_max || 502 sc->sc_timings.clt_ps1 < sc->sc_timecaps.cltc_ps1_min) 503 return EINVAL; 504 reg |= (sc->sc_timings.clt_ps1 - 1) << 16; 505 506 if (sc->sc_timings.clt_ps2 > sc->sc_timecaps.cltc_ps2_max || 507 sc->sc_timings.clt_ps2 < sc->sc_timecaps.cltc_ps2_min) 508 return EINVAL; 509 reg |= (sc->sc_timings.clt_ps2 - 1) << 20; 510 511 if (sc->sc_timings.clt_sjw > sc->sc_timecaps.cltc_sjw_max || 512 sc->sc_timings.clt_sjw < 1) 513 return EINVAL; 514 reg |= (sc->sc_timings.clt_sjw - 1) << 14; 515 516 if (sc->sc_linkmodes & CAN_LINKMODE_3SAMPLES) 517 reg |= SUNXI_CAN_BUS_TIME_SAM; 518 519 sunxi_can_write(sc, SUNXI_CAN_BUS_TIME_REG, reg); 520 521 /* set filters to accept all frames */ 522 sunxi_can_write(sc, SUNXI_CAN_ACPC, 0x00000000); 523 sunxi_can_write(sc, SUNXI_CAN_ACPM, 0xffffffff); 524 525 /* clear errors counter */ 526 sunxi_can_write(sc, SUNXI_CAN_REC_REG, 0); 527 528 /* leave reset mode and enable interrupts */ 529 sunxi_can_exit_reset(sc); 530 sunxi_can_write(sc, SUNXI_CAN_INTE_REG, 531 SUNXI_CAN_INT_TX_FLAG | SUNXI_CAN_INT_RX_FLAG | SUNXI_CAN_INT_ALLERRS); 532 sc->sc_ifp->if_flags |= IFF_RUNNING; 533 return 0; 534 } 535 536 static void 537 sunxi_can_ifdown(struct sunxi_can_softc * const sc) 538 { 539 sc->sc_ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 540 sc->sc_ifp->if_timer = 0; 541 sunxi_can_enter_reset(sc); 542 sunxi_can_write(sc, SUNXI_CAN_INTE_REG, 0); 543 sunxi_can_write(sc, SUNXI_CAN_INT_REG, 544 sunxi_can_read(sc, SUNXI_CAN_INT_REG)); 545 } 546 547 static int 548 sunxi_can_ifioctl(struct ifnet *ifp, u_long cmd, void *data) 549 { 550 struct sunxi_can_softc * const sc = ifp->if_softc; 551 struct ifreq *ifr = (struct ifreq *)data; 552 int error = 0; 553 554 mutex_enter(&sc->sc_intr_lock); 555 556 switch (cmd) { 557 case SIOCINITIFADDR: 558 error = EAFNOSUPPORT; 559 break; 560 case SIOCSIFMTU: 561 if ((unsigned)ifr->ifr_mtu != sizeof(struct can_frame)) 562 error = EINVAL; 563 break; 564 case SIOCADDMULTI: 565 case SIOCDELMULTI: 566 error = EAFNOSUPPORT; 567 break; 568 default: 569 error = ifioctl_common(ifp, cmd, data); 570 if (error == 0) { 571 if ((ifp->if_flags & IFF_UP) != 0 && 572 (ifp->if_flags & IFF_RUNNING) == 0) { 573 error = sunxi_can_ifup(sc); 574 if (error) { 575 ifp->if_flags &= ~IFF_UP; 576 } 577 } else if ((ifp->if_flags & IFF_UP) == 0 && 578 (ifp->if_flags & IFF_RUNNING) != 0) { 579 sunxi_can_ifdown(sc); 580 } 581 } 582 break; 583 } 584 585 mutex_exit(&sc->sc_intr_lock); 586 return error; 587 } 588 589 void 590 sunxi_can_ifwatchdog(struct ifnet *ifp) 591 { 592 struct sunxi_can_softc * const sc = ifp->if_softc; 593 printf("%s: watchdog timeout\n", device_xname(sc->sc_dev)); 594 595 mutex_enter(&sc->sc_intr_lock); 596 printf("irq 0x%x en 0x%x mode 0x%x status 0x%x timings 0x%x err 0x%x\n", 597 sunxi_can_read(sc, SUNXI_CAN_INT_REG), 598 sunxi_can_read(sc, SUNXI_CAN_INTE_REG), 599 sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG), 600 sunxi_can_read(sc, SUNXI_CAN_STA_REG), 601 sunxi_can_read(sc, SUNXI_CAN_BUS_TIME_REG), 602 sunxi_can_read(sc, SUNXI_CAN_REC_REG)); 603 /* if there is a transmit in progress abort */ 604 if (sunxi_can_tx_abort(sc)) { 605 if_statinc(ifp, if_oerrors); 606 } 607 mutex_exit(&sc->sc_intr_lock); 608 } 609 610 static void 611 sunxi_can_enter_reset(struct sunxi_can_softc *sc) 612 { 613 int i; 614 uint32_t val; 615 616 for (i = 0; i < 1000; i++) { 617 val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); 618 val |= SUNXI_CAN_MODSEL_RST; 619 sunxi_can_write(sc, SUNXI_CAN_MODSEL_REG, val); 620 val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); 621 if (val & SUNXI_CAN_MODSEL_RST) 622 return; 623 } 624 printf("%s: couldn't enter reset mode\n", device_xname(sc->sc_dev)); 625 } 626 627 static void 628 sunxi_can_exit_reset(struct sunxi_can_softc *sc) 629 { 630 int i; 631 uint32_t val; 632 633 for (i = 0; i < 1000; i++) { 634 val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); 635 val &= ~SUNXI_CAN_MODSEL_RST; 636 sunxi_can_write(sc, SUNXI_CAN_MODSEL_REG, val); 637 val = sunxi_can_read(sc, SUNXI_CAN_MODSEL_REG); 638 if ((val & SUNXI_CAN_MODSEL_RST) == 0) 639 return; 640 } 641 printf("%s: couldn't leave reset mode\n", device_xname(sc->sc_dev)); 642 } 643