1 /* $NetBSD: sc16is7xx_tty.c,v 1.1 2025/10/24 23:16:11 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.1 2025/10/24 23:16:11 brad Exp $"); 21 22 /* TTY specific common driver to the NXP SC16IS7xx UART bridge */ 23 24 #include <sys/param.h> 25 #include <sys/device.h> 26 #include <sys/tty.h> 27 #ifdef __REMOVE 28 #include <sys/sysctl.h> 29 #endif 30 #include <sys/systm.h> 31 32 #include <sys/bus.h> 33 34 #include <dev/ic/sc16is7xxreg.h> 35 #include <dev/ic/sc16is7xxvar.h> 36 #include <dev/ic/sc16is7xx_ttyvar.h> 37 38 #include <dev/ic/comreg.h> 39 #include <dev/ic/comvar.h> 40 41 static int sc16is7xx_tty_match(device_t, cfdata_t, void *); 42 static void sc16is7xx_tty_attach(device_t, device_t, void *); 43 static int sc16is7xx_tty_detach(device_t, int); 44 #ifdef __REMOVE 45 static int sc16is7xx_tty_verify_prescale(SYSCTLFN_ARGS); 46 #endif 47 48 CFATTACH_DECL_NEW(sc16is7xx_tty, sizeof(struct sc16is7xx_tty_softc), 49 sc16is7xx_tty_match, sc16is7xx_tty_attach, sc16is7xx_tty_detach, NULL); 50 51 #ifdef __REMOVE 52 int 53 sc16is7xx_tty_verify_prescale(SYSCTLFN_ARGS) 54 { 55 struct sc16is7xx_tty_softc *sc; 56 int error; 57 bool t = false; 58 struct sysctlnode node; 59 60 node = *rnode; 61 sc = node.sysctl_data; 62 if (ISSET(sc->sc_com.sc_mcr, MCR_PRESCALE)) 63 t = true; 64 node.sysctl_data = &t; 65 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 66 if (error || newp == NULL) 67 return error; 68 69 if (t) 70 SET(sc->sc_com.sc_mcr, MCR_PRESCALE); 71 else 72 CLR(sc->sc_com.sc_mcr, MCR_PRESCALE); 73 74 return 0; 75 } 76 77 static int 78 sc16is7xx_tty_sysctl_init(struct sc16is7xx_tty_softc *sc) 79 { 80 int error; 81 const struct sysctlnode *cnode; 82 int sysctlroot_num; 83 84 if ((error = sysctl_createv(&sc->sc_sc16is7xx_tty_log, 0, NULL, &cnode, 85 0, CTLTYPE_NODE, device_xname(sc->sc_com.sc_dev), 86 SYSCTL_DESCR("sc16ix7xx tty controls"), NULL, 0, NULL, 0, CTL_HW, 87 CTL_CREATE, CTL_EOL)) != 0) 88 return error; 89 90 sysctlroot_num = cnode->sysctl_num; 91 92 if ((error = sysctl_createv(&sc->sc_sc16is7xx_tty_log, 0, NULL, &cnode, 93 CTLFLAG_READWRITE, CTLTYPE_BOOL, "prescale", 94 SYSCTL_DESCR("Prescale"), sc16is7xx_tty_verify_prescale, 0, 95 (void *)sc, 0, CTL_HW, sysctlroot_num, CTL_CREATE, 96 CTL_EOL)) != 0) 97 return error; 98 99 return 0; 100 } 101 #endif 102 103 static int 104 sc16is7xx_tty_match(device_t parent, cfdata_t match, void *aux) 105 { 106 return 1; 107 } 108 109 void 110 sc16is7xx_tty_attach(device_t parent, device_t self, void *aux) 111 { 112 struct sc16is7xx_tty_attach_args *caa = aux; 113 struct sc16is7xx_tty_softc *sc = device_private(self); 114 struct sc16is7xx_sc *psc = device_private(parent); 115 #ifdef __REMOVE 116 int error; 117 #endif 118 119 sc->sc_com.sc_dev = self; 120 121 #ifdef __REMOVE 122 if ((error = sc16is7xx_tty_sysctl_init(sc)) != 0) { 123 aprint_error_dev(sc->sc_com.sc_dev, "Can't setup sysctl tree (%d)\n", error); 124 goto out; 125 } 126 #endif 127 128 /* Set and then override the callouts to read and write the registers. */ 129 com_init_regs(&sc->sc_com.sc_regs, 0, 0, 0); 130 sc->sc_com.sc_regs.cr_read = psc->sc_com_funcs->com_read_1; 131 sc->sc_com.sc_regs.cr_write = psc->sc_com_funcs->com_write_1; 132 sc->sc_com.sc_regs.cr_write_multi = psc->sc_com_funcs->com_write_multi_1; 133 sc->sc_com.sc_regs.cr_channel = caa->aa_channel; 134 135 psc->sc_funcs->copy_handles(psc, &sc->sc_com.sc_regs); 136 137 /* We will get a 64 byte FIFO and hardware flow control if we use this */ 138 sc->sc_com.sc_type = COM_TYPE_SC16IS7XX; 139 140 /* The master frequency is pushed down from the bus layer as both 141 * channels use the same clock. However it needs to be set here as 142 * com(4) wants it. */ 143 sc->sc_com.sc_frequency = psc->sc_frequency; 144 145 /* This will always be 0, as the polling will occur at the bus layer, 146 * if needed. */ 147 sc->sc_com.sc_poll_ticks = 0; 148 149 /* Tell com(4) to run in a soft context whenever possible */ 150 SET(sc->sc_com.sc_hwflags, COM_HW_SOFTIRQ); 151 152 com_attach_subr(&sc->sc_com); 153 #ifdef __REMOVE 154 out: 155 #endif 156 157 return; 158 } 159 160 int 161 sc16is7xx_tty_detach(device_t self, int flags) 162 { 163 #ifdef __REMOVE 164 struct sc16is7xx_tty_softc *sc = device_private(self); 165 #endif 166 int error; 167 168 #ifdef __REMOVE 169 sysctl_teardown(&sc->sc_sc16is7xx_tty_log); 170 #endif 171 172 if ((error = com_detach(self, flags)) != 0) 173 return error; 174 175 return 0; 176 177 } 178