1 /* $NetBSD: sc16is7xx_tty.c,v 1.4 2025/12/01 14:56:03 brad Exp $ */ 2 3 /* 4 * Copyright (c) 2025 Brad Spencer <brad (at) anduin.eldar.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/cdefs.h> 20 __KERNEL_RCSID(0, "$NetBSD: sc16is7xx_tty.c,v 1.4 2025/12/01 14:56:03 brad Exp $"); 21 22 /* TTY specific common driver to the NXP SC16IS7xx UART bridge */ 23 24 #include <sys/kthread.h> 25 #include <sys/param.h> 26 #include <sys/device.h> 27 #include <sys/tty.h> 28 #include <sys/systm.h> 29 30 #include <sys/bus.h> 31 32 #include <dev/ic/sc16is7xxreg.h> 33 #include <dev/ic/sc16is7xxvar.h> 34 #include <dev/ic/sc16is7xx_ttyvar.h> 35 36 #include <dev/ic/comreg.h> 37 #include <dev/ic/comvar.h> 38 39 static int sc16is7xx_tty_match(device_t, cfdata_t, void *); 40 static void sc16is7xx_tty_attach(device_t, device_t, void *); 41 static int sc16is7xx_tty_detach(device_t, int); 42 43 CFATTACH_DECL_NEW(sc16is7xx_tty, sizeof(struct sc16is7xx_tty_softc), 44 sc16is7xx_tty_match, sc16is7xx_tty_attach, sc16is7xx_tty_detach, NULL); 45 46 #define COM_TO_SC16IS7XX(sc) \ 47 container_of((sc), struct sc16is7xx_tty_softc, sc_com) 48 49 static int 50 sc16is7xx_tty_enable(struct com_softc *com_sc) 51 { 52 struct sc16is7xx_tty_softc *sc = COM_TO_SC16IS7XX(com_sc); 53 struct sc16is7xx_sc *psc = sc->psc; 54 int error = 0; 55 56 mutex_enter(&psc->sc_thread_mutex); 57 58 if (psc->sc_thread_state != SC16IS7XX_THREAD_GPIO && 59 psc->sc_thread_state != SC16IS7XX_THREAD_RUNNING && 60 psc->sc_thread_state != SC16IS7XX_THREAD_EXIT && 61 psc->sc_de_count == 0) { 62 if (psc->sc_thread_state == SC16IS7XX_THREAD_STOPPED) 63 error = kthread_create(PRI_SOFTSERIAL, KTHREAD_MUSTJOIN | KTHREAD_MPSAFE, NULL, 64 sc16is7xx_thread, psc, &psc->sc_thread, "%s", device_xname(psc->sc_dev)); 65 if (error) { 66 aprint_error_dev(psc->sc_dev, 67 "Could not create kernel thread for polling: %d\n", 68 error); 69 } else { 70 psc->sc_thread_state = SC16IS7XX_THREAD_RUNNING; 71 cv_signal(&psc->sc_threadvar); 72 } 73 } 74 75 psc->sc_de_count++; 76 77 KASSERTMSG(psc->sc_de_count >= 0 && psc->sc_de_count <= 2, 78 "Disable / Enable count out of bounds: %d\n", psc->sc_de_count); 79 80 com_sc->sc_regs.cr_has_errored = false; 81 82 mutex_exit(&psc->sc_thread_mutex); 83 84 return 0; 85 } 86 87 static void 88 sc16is7xx_tty_disable(struct com_softc *com_sc) 89 { 90 struct sc16is7xx_tty_softc *sc = COM_TO_SC16IS7XX(com_sc); 91 struct sc16is7xx_sc *psc = sc->psc; 92 93 mutex_enter(&psc->sc_thread_mutex); 94 95 psc->sc_de_count--; 96 97 KASSERTMSG(psc->sc_de_count >= 0 && psc->sc_de_count <= 2, 98 "Disable / Enable count out of bounds: %d\n", psc->sc_de_count); 99 100 if (psc->sc_thread_state != SC16IS7XX_THREAD_GPIO && 101 psc->sc_thread_state != SC16IS7XX_THREAD_PAUSED && 102 psc->sc_thread_state != SC16IS7XX_THREAD_EXIT && 103 psc->sc_de_count == 0) { 104 psc->sc_thread_state = SC16IS7XX_THREAD_PAUSED; 105 cv_signal(&psc->sc_threadvar); 106 } 107 108 mutex_exit(&psc->sc_thread_mutex); 109 110 } 111 112 static int 113 sc16is7xx_tty_match(device_t parent, cfdata_t match, void *aux) 114 { 115 return 1; 116 } 117 118 void 119 sc16is7xx_tty_attach(device_t parent, device_t self, void *aux) 120 { 121 struct sc16is7xx_tty_attach_args *caa = aux; 122 struct sc16is7xx_tty_softc *sc = device_private(self); 123 struct sc16is7xx_sc *psc = device_private(parent); 124 125 sc->sc_com.sc_dev = self; 126 sc->psc = psc; 127 128 device_setprop_bool(self, "is-console", false); 129 device_setprop_bool(self, "force-console", false); 130 131 /* Set and then override the callouts to read and write the registers. */ 132 com_init_regs(&sc->sc_com.sc_regs, 0, 0, 0); 133 sc->sc_com.sc_regs.cr_read = psc->sc_com_funcs->com_read_1; 134 sc->sc_com.sc_regs.cr_write = psc->sc_com_funcs->com_write_1; 135 sc->sc_com.sc_regs.cr_write_multi = psc->sc_com_funcs->com_write_multi_1; 136 sc->sc_com.sc_regs.cr_channel = caa->aa_channel; 137 138 sc->sc_com.enable = &sc16is7xx_tty_enable; 139 sc->sc_com.disable = &sc16is7xx_tty_disable; 140 141 psc->sc_funcs->copy_handles(psc, &sc->sc_com.sc_regs); 142 143 /* We will get a 64 byte FIFO and hardware flow control if we use this */ 144 sc->sc_com.sc_type = COM_TYPE_SC16IS7XX; 145 146 /* The master frequency is pushed down from the bus layer as both 147 * channels use the same clock. However it needs to be set here as 148 * com(4) wants it. */ 149 sc->sc_com.sc_frequency = psc->sc_frequency; 150 151 /* This will always be 0, as the polling will occur at the bus layer, 152 * if needed. */ 153 sc->sc_com.sc_poll_ticks = 0; 154 155 /* Tell com(4) to run in a soft context whenever possible */ 156 SET(sc->sc_com.sc_hwflags, COM_HW_SOFTIRQ); 157 158 com_attach_subr(&sc->sc_com); 159 160 return; 161 } 162 163 int 164 sc16is7xx_tty_detach(device_t self, int flags) 165 { 166 int error = 0; 167 168 error = com_detach(self, flags); 169 170 return error; 171 } 172