1 /* $OpenBSD: comms_ebus.c,v 1.2 2009/06/17 06:48:38 matthieu Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Miodrag Vallat. 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 * Copyright (c) 2002 Jason L. Wright (jason@thought.net) 20 * All rights reserved. 21 * 22 * Redistribution and use in source and binary forms, with or without 23 * modification, are permitted provided that the following conditions 24 * are met: 25 * 1. Redistributions of source code must retain the above copyright 26 * notice, this list of conditions and the following disclaimer. 27 * 2. Redistributions in binary form must reproduce the above copyright 28 * notice, this list of conditions and the following disclaimer in the 29 * documentation and/or other materials provided with the distribution. 30 * 31 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 32 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 33 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 34 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 35 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 36 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 37 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 40 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 * POSSIBILITY OF SUCH DAMAGE. 42 * 43 * Effort sponsored in part by the Defense Advanced Research Projects 44 * Agency (DARPA) and Air Force Research Laboratory, Air Force 45 * Materiel Command, USAF, under agreement number F30602-01-2-0537. 46 * 47 */ 48 49 #include <sys/param.h> 50 #include <sys/systm.h> 51 #include <sys/conf.h> 52 #include <sys/device.h> 53 #include <sys/ioctl.h> 54 #include <sys/kernel.h> 55 #include <sys/proc.h> 56 #include <sys/syslog.h> 57 #include <sys/tty.h> 58 59 #include <machine/bus.h> 60 #include <machine/autoconf.h> 61 #include <machine/openfirm.h> 62 63 #include <sparc64/dev/ebusreg.h> 64 #include <sparc64/dev/ebusvar.h> 65 66 #include <dev/wscons/wsconsio.h> 67 #include <dev/wscons/wsmousevar.h> 68 69 #include <dev/sun/sunmsvar.h> 70 71 #include <dev/ic/comreg.h> 72 #include <dev/ic/comvar.h> 73 #include <dev/ic/ns16550reg.h> 74 #define com_lcr com_cfcr 75 76 #include <dev/cons.h> 77 78 /* should match com_ebus.c */ 79 #define BAUD_BASE (1843200) 80 81 #define COMMS_RX_RING 64 82 83 struct comms_softc { 84 struct sunms_softc sc_base; 85 86 u_int sc_ier; 87 88 bus_space_tag_t sc_iot; /* bus tag */ 89 bus_space_handle_t sc_ioh; /* bus handle */ 90 void *sc_ih, *sc_si; /* interrupt vectors */ 91 92 u_int sc_rxcnt; 93 u_int8_t sc_rxbuf[COMMS_RX_RING]; 94 u_int8_t *sc_rxbeg, *sc_rxend, *sc_rxget, *sc_rxput; 95 }; 96 97 #define COM_WRITE(sc,r,v) \ 98 bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (r), (v)) 99 #define COM_READ(sc,r) \ 100 bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (r)) 101 102 /* 103 * autoconf glue. 104 */ 105 106 int comms_match(struct device *, void *, void *); 107 void comms_attach(struct device *, struct device *, void *); 108 109 const struct cfattach comms_ca = { 110 sizeof(struct comms_softc), comms_match, comms_attach 111 }; 112 113 struct cfdriver comms_cd = { 114 NULL, "comms", DV_DULL 115 }; 116 117 /* 118 * wsmouse accessops. 119 */ 120 121 void comms_disable(void *); 122 int comms_enable(void *); 123 124 const struct wsmouse_accessops comms_accessops = { 125 comms_enable, 126 sunms_ioctl, 127 comms_disable 128 }; 129 130 /* 131 * com glue. 132 */ 133 134 int comms_hardintr(void *); 135 int comms_ismouse(int); 136 void comms_softintr(void *); 137 void comms_speed_change(void *, uint); 138 139 /* 140 * autoconf glue. 141 */ 142 143 static const char *comms_names[] = { 144 "su", 145 "su_pnp", 146 NULL 147 }; 148 149 int 150 comms_ismouse(int node) 151 { 152 if (OF_getproplen(node, "mouse") == 0) 153 return 10; 154 return 0; 155 } 156 157 int 158 comms_match(struct device *parent, void *match, void *aux) 159 { 160 struct ebus_attach_args *ea = aux; 161 int i; 162 163 for (i = 0; comms_names[i]; i++) 164 if (strcmp(ea->ea_name, comms_names[i]) == 0) 165 return comms_ismouse(ea->ea_node); 166 167 if (strcmp(ea->ea_name, "serial") == 0) { 168 char compat[80]; 169 170 if ((i = OF_getproplen(ea->ea_node, "compatible")) && 171 OF_getprop(ea->ea_node, "compatible", compat, 172 sizeof(compat)) == i) { 173 if (strcmp(compat, "su16550") == 0 || 174 strcmp(compat, "su") == 0) 175 return comms_ismouse(ea->ea_node); 176 } 177 } 178 return 0; 179 } 180 181 void 182 comms_attach(struct device *parent, struct device *self, void *aux) 183 { 184 struct comms_softc *sc = (void *)self; 185 struct ebus_attach_args *ea = aux; 186 187 sc->sc_iot = ea->ea_memtag; 188 189 sc->sc_rxget = sc->sc_rxput = sc->sc_rxbeg = sc->sc_rxbuf; 190 sc->sc_rxend = sc->sc_rxbuf + COMMS_RX_RING; 191 sc->sc_rxcnt = 0; 192 193 /* we really want IPL_TTY here. */ 194 sc->sc_si = softintr_establish(IPL_TTY, comms_softintr, sc); 195 if (sc->sc_si == NULL) { 196 printf(": can't get soft intr\n"); 197 return; 198 } 199 200 /* Use prom address if available, otherwise map it. */ 201 if (ea->ea_nvaddrs && bus_space_map(ea->ea_memtag, ea->ea_vaddrs[0], 0, 202 BUS_SPACE_MAP_PROMADDRESS, &sc->sc_ioh) == 0) { 203 sc->sc_iot = ea->ea_memtag; 204 } else if (ebus_bus_map(ea->ea_memtag, 0, 205 EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), 206 ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) { 207 sc->sc_iot = ea->ea_memtag; 208 } else if (ebus_bus_map(ea->ea_iotag, 0, 209 EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), 210 ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) { 211 sc->sc_iot = ea->ea_iotag; 212 } else { 213 printf(": can't map register space\n"); 214 return; 215 } 216 217 sc->sc_ih = bus_intr_establish(sc->sc_iot, 218 ea->ea_intrs[0], IPL_TTY, 0, comms_hardintr, sc, self->dv_xname); 219 if (sc->sc_ih == NULL) { 220 printf(": can't get hard intr\n"); 221 return; 222 } 223 224 /* Initialize hardware. */ 225 sc->sc_ier = 0; 226 comms_speed_change(sc, INIT_SPEED); 227 228 sc->sc_base.sc_speed_change = comms_speed_change; 229 230 sunms_attach(&sc->sc_base, &comms_accessops); 231 } 232 233 /* 234 * wsmouse accessops. 235 */ 236 237 void 238 comms_disable(void *v) 239 { 240 struct comms_softc *sc = v; 241 int s; 242 243 s = spltty(); 244 sc->sc_ier = 0; 245 COM_WRITE(sc, com_ier, sc->sc_ier); 246 splx(s); 247 } 248 249 int 250 comms_enable(void *v) 251 { 252 struct comms_softc *sc = v; 253 int s; 254 255 s = spltty(); 256 sc->sc_ier = IER_ERXRDY; 257 COM_WRITE(sc, com_ier, sc->sc_ier); 258 splx(s); 259 260 return 0; 261 } 262 263 /* 264 * com glue. 265 */ 266 267 void 268 comms_softintr(void *v) 269 { 270 struct comms_softc *sc = v; 271 uint8_t c; 272 273 /* 274 * If we have a baud rate change pending, do it now. 275 * This will reset the rx ring, so we can proceed safely. 276 */ 277 if (sc->sc_base.sc_state == STATE_RATE_CHANGE) { 278 sunms_speed_change(&sc->sc_base); 279 } 280 281 /* 282 * Copy data from the receive ring, if any, to the event layer. 283 */ 284 while (sc->sc_rxcnt) { 285 c = *sc->sc_rxget; 286 if (++sc->sc_rxget == sc->sc_rxend) 287 sc->sc_rxget = sc->sc_rxbeg; 288 sc->sc_rxcnt--; 289 sunms_input(&sc->sc_base, c); 290 } 291 } 292 293 int 294 comms_hardintr(void *v) 295 { 296 struct comms_softc *sc = v; 297 u_int8_t iir, lsr, data; 298 int needsoft = 0; 299 300 /* Nothing to do */ 301 iir = COM_READ(sc, com_iir); 302 if (ISSET(iir, IIR_NOPEND)) 303 return 0; 304 305 for (;;) { 306 lsr = COM_READ(sc, com_lsr); 307 308 if (ISSET(lsr, LSR_BI)) { 309 if (sc->sc_base.sc_state != STATE_RATE_CHANGE && 310 ++sc->sc_base.sc_brk > 1) { 311 sc->sc_base.sc_state = STATE_RATE_CHANGE; 312 needsoft = 1; 313 #ifdef DEBUG 314 printf("%s: break detected, changing speed\n", 315 sc->sc_base.sc_dev.dv_xname); 316 #endif 317 } 318 } 319 320 if (ISSET(lsr, LSR_RXRDY)) { 321 needsoft = 1; 322 323 do { 324 data = COM_READ(sc, com_data); 325 if (sc->sc_base.sc_state != STATE_RATE_CHANGE && 326 sc->sc_rxcnt != COMMS_RX_RING) { 327 *sc->sc_rxput = data; 328 if (++sc->sc_rxput == sc->sc_rxend) 329 sc->sc_rxput = sc->sc_rxbeg; 330 sc->sc_rxcnt++; 331 } 332 lsr = COM_READ(sc, com_lsr); 333 } while (ISSET(lsr, LSR_RXRDY)); 334 } 335 336 iir = COM_READ(sc, com_iir); 337 if (ISSET(iir, IIR_NOPEND)) 338 break; 339 } 340 341 if (needsoft) 342 softintr_schedule(sc->sc_si); 343 344 return 1; 345 } 346 347 /* 348 * Reinitialize the line to a different speed. Invoked at spltty(). 349 */ 350 void 351 comms_speed_change(void *v, uint bps) 352 { 353 struct comms_softc *sc = v; 354 int ospeed; 355 356 /* 357 * Eat everything on the line. 358 */ 359 while (ISSET(COM_READ(sc, com_lsr), LSR_RXRDY)) 360 COM_READ(sc, com_data); 361 362 ospeed = comspeed(BAUD_BASE, bps); 363 364 /* disable interrupts while the chip is reprogrammed */ 365 COM_WRITE(sc, com_ier, 0); 366 367 COM_WRITE(sc, com_lcr, LCR_DLAB); 368 COM_WRITE(sc, com_dlbl, ospeed); 369 COM_WRITE(sc, com_dlbh, ospeed >> 8); 370 /* 8 data bits, no parity, 2 stop bits */ 371 COM_WRITE(sc, com_lcr, LCR_8BITS | LCR_PNONE | LCR_STOPB); 372 COM_READ(sc, com_iir); 373 374 COM_WRITE(sc, com_mcr, MCR_IENABLE | MCR_DTR | MCR_RTS); 375 /* XXX do something about the FIFO? */ 376 377 COM_WRITE(sc, com_ier, sc->sc_ier); 378 } 379