1 /* $OpenBSD: com_puc.c,v 1.4 2002/01/30 20:45:34 nordin Exp $ */ 2 3 /* 4 * Copyright (c) 1997 - 1999, Jason Downs. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name(s) of the author(s) nor the name OpenBSD 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS 19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, 22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/ioctl.h> 34 #include <sys/select.h> 35 #include <sys/tty.h> 36 #include <sys/proc.h> 37 #include <sys/user.h> 38 #include <sys/conf.h> 39 #include <sys/file.h> 40 #include <sys/uio.h> 41 #include <sys/kernel.h> 42 #include <sys/syslog.h> 43 #include <sys/types.h> 44 #include <sys/device.h> 45 46 #include <machine/intr.h> 47 #include <machine/bus.h> 48 49 #include <dev/pci/pcivar.h> 50 #include <dev/pci/pucvar.h> 51 52 #include <dev/isa/isavar.h> /* XXX */ 53 54 #include "com.h" 55 #ifdef i386 56 #include "pccom.h" 57 #endif 58 59 #include <dev/ic/comreg.h> 60 #if NPCCOM > 0 61 #include <i386/isa/pccomvar.h> 62 #endif 63 #if NCOM > 0 64 #include <dev/ic/comvar.h> 65 #endif 66 #include <dev/ic/ns16550reg.h> 67 68 #define com_lcr com_cfcr 69 70 int com_puc_match __P((struct device *, void *, void *)); 71 void com_puc_attach __P((struct device *, struct device *, void *)); 72 73 #if NCOM_PUC 74 struct cfattach com_puc_ca = { 75 sizeof(struct com_softc), com_puc_match, com_puc_attach 76 }; 77 #endif 78 79 #if NPCCOM_PUC 80 struct cfattach pccom_puc_ca = { 81 sizeof(struct com_softc), com_puc_match, com_puc_attach 82 }; 83 #endif 84 85 void com_puc_attach2 __P((struct com_softc *)); 86 87 int 88 com_puc_match(parent, match, aux) 89 struct device *parent; 90 void *match, *aux; 91 { 92 struct puc_attach_args *pa = aux; 93 94 if (pa->type == PUC_PORT_TYPE_COM) 95 return(1); 96 97 return(0); 98 } 99 100 void 101 com_puc_attach(parent, self, aux) 102 struct device *parent, *self; 103 void *aux; 104 { 105 struct com_softc *sc = (void *)self; 106 struct puc_attach_args *pa = aux; 107 const char *intrstr; 108 109 /* Grab a PCI interrupt. */ 110 intrstr = pci_intr_string(pa->pc, pa->intrhandle); 111 sc->sc_ih = pci_intr_establish(pa->pc, pa->intrhandle, 112 IPL_HIGH, comintr, sc, 113 sc->sc_dev.dv_xname); 114 if (sc->sc_ih == NULL) { 115 printf(": couldn't establish interrupt"); 116 if (intrstr != NULL) 117 printf(" at %s", intrstr); 118 printf("\n"); 119 return; 120 } 121 printf(" %s", intrstr); 122 123 sc->sc_iot = pa->t; 124 sc->sc_ioh = pa->h; 125 sc->sc_iobase = pa->a; 126 sc->sc_frequency = COM_FREQ; 127 128 if (pa->flags) 129 sc->sc_frequency = pa->flags & PUC_COM_CLOCKMASK; 130 131 com_puc_attach2(sc); 132 } 133 134 /* 135 * XXX This should be handled by a generic attach 136 */ 137 void 138 com_puc_attach2(sc) 139 struct com_softc *sc; 140 { 141 bus_space_tag_t iot = sc->sc_iot; 142 bus_space_handle_t ioh = sc->sc_ioh; 143 u_int8_t lcr; 144 145 sc->sc_hwflags = 0; 146 sc->sc_swflags = 0; 147 148 timeout_set(&sc->sc_dtr_tmo, com_raisedtr, sc); 149 timeout_set(&sc->sc_diag_tmo, comdiag, sc); 150 151 /* 152 * Probe for all known forms of UART. 153 */ 154 lcr = bus_space_read_1(iot, ioh, com_lcr); 155 156 bus_space_write_1(iot, ioh, com_lcr, LCR_EFR); 157 bus_space_write_1(iot, ioh, com_efr, 0); 158 bus_space_write_1(iot, ioh, com_lcr, 0); 159 160 bus_space_write_1(iot, ioh, com_fifo, FIFO_ENABLE); 161 delay(100); 162 163 switch(bus_space_read_1(iot, ioh, com_iir) >> 6) { 164 case 0: 165 sc->sc_uarttype = COM_UART_16450; 166 break; 167 case 2: 168 sc->sc_uarttype = COM_UART_16550; 169 break; 170 case 3: 171 sc->sc_uarttype = COM_UART_16550A; 172 break; 173 default: 174 sc->sc_uarttype = COM_UART_UNKNOWN; 175 break; 176 } 177 178 if (sc->sc_uarttype == COM_UART_16550A) { /* Probe for ST16650s */ 179 bus_space_write_1(iot, ioh, com_lcr, lcr | LCR_DLAB); 180 if (bus_space_read_1(iot, ioh, com_efr) == 0) { 181 sc->sc_uarttype = COM_UART_ST16650; 182 } else { 183 bus_space_write_1(iot, ioh, com_lcr, LCR_EFR); 184 if (bus_space_read_1(iot, ioh, com_efr) == 0) 185 sc->sc_uarttype = COM_UART_ST16650V2; 186 } 187 } 188 189 #if NPCCOM > 0 190 #ifdef i386 191 if (sc->sc_uarttype == COM_UART_ST16650V2) { /* Probe for XR16850s */ 192 u_int8_t dlbl, dlbh; 193 194 /* Enable latch access and get the current values. */ 195 bus_space_write_1(iot, ioh, com_lcr, lcr | LCR_DLAB); 196 dlbl = bus_space_read_1(iot, ioh, com_dlbl); 197 dlbh = bus_space_read_1(iot, ioh, com_dlbh); 198 199 /* Zero out the latch divisors */ 200 bus_space_write_1(iot, ioh, com_dlbl, 0); 201 bus_space_write_1(iot, ioh, com_dlbh, 0); 202 203 if (bus_space_read_1(iot, ioh, com_dlbh) == 0x10) { 204 sc->sc_uarttype = COM_UART_XR16850; 205 sc->sc_uartrev = bus_space_read_1(iot, ioh, com_dlbl); 206 } 207 208 /* Reset to original. */ 209 bus_space_write_1(iot, ioh, com_dlbl, dlbl); 210 bus_space_write_1(iot, ioh, com_dlbh, dlbh); 211 } 212 #endif 213 #endif 214 215 /* Reset the LCR (latch access is probably enabled). */ 216 bus_space_write_1(iot, ioh, com_lcr, lcr); 217 if (sc->sc_uarttype == COM_UART_16450) { /* Probe for 8250 */ 218 u_int8_t scr0, scr1, scr2; 219 220 scr0 = bus_space_read_1(iot, ioh, com_scratch); 221 bus_space_write_1(iot, ioh, com_scratch, 0xa5); 222 scr1 = bus_space_read_1(iot, ioh, com_scratch); 223 bus_space_write_1(iot, ioh, com_scratch, 0x5a); 224 scr2 = bus_space_read_1(iot, ioh, com_scratch); 225 bus_space_write_1(iot, ioh, com_scratch, scr0); 226 227 if ((scr1 != 0xa5) || (scr2 != 0x5a)) 228 sc->sc_uarttype = COM_UART_8250; 229 } 230 231 /* 232 * Print UART type and initialize ourself. 233 */ 234 sc->sc_fifolen = 1; /* default */ 235 switch (sc->sc_uarttype) { 236 case COM_UART_UNKNOWN: 237 printf(": unknown uart\n"); 238 break; 239 case COM_UART_8250: 240 printf(": ns8250, no fifo\n"); 241 break; 242 case COM_UART_16450: 243 printf(": ns16450, no fifo\n"); 244 break; 245 case COM_UART_16550: 246 printf(": ns16550, no working fifo\n"); 247 break; 248 case COM_UART_16550A: 249 printf(": ns16550a, 16 byte fifo\n"); 250 SET(sc->sc_hwflags, COM_HW_FIFO); 251 sc->sc_fifolen = 16; 252 break; 253 case COM_UART_ST16650: 254 printf(": st16650, no working fifo\n"); 255 break; 256 case COM_UART_ST16650V2: 257 printf(": st16650, 32 byte fifo\n"); 258 SET(sc->sc_hwflags, COM_HW_FIFO); 259 sc->sc_fifolen = 32; 260 break; 261 #if NPCCOM > 0 262 #ifdef i386 263 case COM_UART_XR16850: 264 printf(": xr16850 (rev %d), 128 byte fifo\n", sc->sc_uartrev); 265 SET(sc->sc_hwflags, COM_HW_FIFO); 266 sc->sc_fifolen = 128; 267 break; 268 #endif 269 #endif 270 default: 271 panic("comattach: bad fifo type"); 272 } 273 274 /* clear and disable fifo */ 275 bus_space_write_1(iot, ioh, com_fifo, FIFO_RCV_RST | FIFO_XMT_RST); 276 (void)bus_space_read_1(iot, ioh, com_data); 277 bus_space_write_1(iot, ioh, com_fifo, 0); 278 } 279