1*a7f5b4e9Smatthieu /* $OpenBSD: comms_ebus.c,v 1.2 2009/06/17 06:48:38 matthieu Exp $ */
219cddadbSmiod
319cddadbSmiod /*
419cddadbSmiod * Copyright (c) 2009 Miodrag Vallat.
519cddadbSmiod *
619cddadbSmiod * Permission to use, copy, modify, and distribute this software for any
719cddadbSmiod * purpose with or without fee is hereby granted, provided that the above
819cddadbSmiod * copyright notice and this permission notice appear in all copies.
919cddadbSmiod *
1019cddadbSmiod * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1119cddadbSmiod * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1219cddadbSmiod * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1319cddadbSmiod * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1419cddadbSmiod * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1519cddadbSmiod * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1619cddadbSmiod * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1719cddadbSmiod */
1819cddadbSmiod /*
1919cddadbSmiod * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
2019cddadbSmiod * All rights reserved.
2119cddadbSmiod *
2219cddadbSmiod * Redistribution and use in source and binary forms, with or without
2319cddadbSmiod * modification, are permitted provided that the following conditions
2419cddadbSmiod * are met:
2519cddadbSmiod * 1. Redistributions of source code must retain the above copyright
2619cddadbSmiod * notice, this list of conditions and the following disclaimer.
2719cddadbSmiod * 2. Redistributions in binary form must reproduce the above copyright
2819cddadbSmiod * notice, this list of conditions and the following disclaimer in the
2919cddadbSmiod * documentation and/or other materials provided with the distribution.
3019cddadbSmiod *
3119cddadbSmiod * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
3219cddadbSmiod * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
3319cddadbSmiod * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
3419cddadbSmiod * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
3519cddadbSmiod * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
3619cddadbSmiod * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
3719cddadbSmiod * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3819cddadbSmiod * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
3919cddadbSmiod * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
4019cddadbSmiod * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
4119cddadbSmiod * POSSIBILITY OF SUCH DAMAGE.
4219cddadbSmiod *
4319cddadbSmiod * Effort sponsored in part by the Defense Advanced Research Projects
4419cddadbSmiod * Agency (DARPA) and Air Force Research Laboratory, Air Force
4519cddadbSmiod * Materiel Command, USAF, under agreement number F30602-01-2-0537.
4619cddadbSmiod *
4719cddadbSmiod */
4819cddadbSmiod
4919cddadbSmiod #include <sys/param.h>
5019cddadbSmiod #include <sys/systm.h>
5119cddadbSmiod #include <sys/conf.h>
5219cddadbSmiod #include <sys/device.h>
5319cddadbSmiod #include <sys/ioctl.h>
5419cddadbSmiod #include <sys/kernel.h>
5519cddadbSmiod #include <sys/proc.h>
5619cddadbSmiod #include <sys/syslog.h>
5719cddadbSmiod #include <sys/tty.h>
5819cddadbSmiod
5919cddadbSmiod #include <machine/bus.h>
6019cddadbSmiod #include <machine/autoconf.h>
6119cddadbSmiod #include <machine/openfirm.h>
6219cddadbSmiod
6319cddadbSmiod #include <sparc64/dev/ebusreg.h>
6419cddadbSmiod #include <sparc64/dev/ebusvar.h>
6519cddadbSmiod
6619cddadbSmiod #include <dev/wscons/wsconsio.h>
6719cddadbSmiod #include <dev/wscons/wsmousevar.h>
6819cddadbSmiod
6919cddadbSmiod #include <dev/sun/sunmsvar.h>
7019cddadbSmiod
7119cddadbSmiod #include <dev/ic/comreg.h>
7219cddadbSmiod #include <dev/ic/comvar.h>
7319cddadbSmiod #include <dev/ic/ns16550reg.h>
7419cddadbSmiod #define com_lcr com_cfcr
7519cddadbSmiod
7619cddadbSmiod #include <dev/cons.h>
7719cddadbSmiod
7819cddadbSmiod /* should match com_ebus.c */
7919cddadbSmiod #define BAUD_BASE (1843200)
8019cddadbSmiod
8119cddadbSmiod #define COMMS_RX_RING 64
8219cddadbSmiod
8319cddadbSmiod struct comms_softc {
8419cddadbSmiod struct sunms_softc sc_base;
8519cddadbSmiod
8619cddadbSmiod u_int sc_ier;
8719cddadbSmiod
8819cddadbSmiod bus_space_tag_t sc_iot; /* bus tag */
8919cddadbSmiod bus_space_handle_t sc_ioh; /* bus handle */
9019cddadbSmiod void *sc_ih, *sc_si; /* interrupt vectors */
9119cddadbSmiod
9219cddadbSmiod u_int sc_rxcnt;
9319cddadbSmiod u_int8_t sc_rxbuf[COMMS_RX_RING];
9419cddadbSmiod u_int8_t *sc_rxbeg, *sc_rxend, *sc_rxget, *sc_rxput;
9519cddadbSmiod };
9619cddadbSmiod
9719cddadbSmiod #define COM_WRITE(sc,r,v) \
9819cddadbSmiod bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (r), (v))
9919cddadbSmiod #define COM_READ(sc,r) \
10019cddadbSmiod bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (r))
10119cddadbSmiod
10219cddadbSmiod /*
10319cddadbSmiod * autoconf glue.
10419cddadbSmiod */
10519cddadbSmiod
10619cddadbSmiod int comms_match(struct device *, void *, void *);
10719cddadbSmiod void comms_attach(struct device *, struct device *, void *);
10819cddadbSmiod
10919cddadbSmiod const struct cfattach comms_ca = {
11019cddadbSmiod sizeof(struct comms_softc), comms_match, comms_attach
11119cddadbSmiod };
11219cddadbSmiod
11319cddadbSmiod struct cfdriver comms_cd = {
11419cddadbSmiod NULL, "comms", DV_DULL
11519cddadbSmiod };
11619cddadbSmiod
11719cddadbSmiod /*
11819cddadbSmiod * wsmouse accessops.
11919cddadbSmiod */
12019cddadbSmiod
12119cddadbSmiod void comms_disable(void *);
12219cddadbSmiod int comms_enable(void *);
12319cddadbSmiod
12419cddadbSmiod const struct wsmouse_accessops comms_accessops = {
12519cddadbSmiod comms_enable,
12619cddadbSmiod sunms_ioctl,
12719cddadbSmiod comms_disable
12819cddadbSmiod };
12919cddadbSmiod
13019cddadbSmiod /*
13119cddadbSmiod * com glue.
13219cddadbSmiod */
13319cddadbSmiod
13419cddadbSmiod int comms_hardintr(void *);
13519cddadbSmiod int comms_ismouse(int);
13619cddadbSmiod void comms_softintr(void *);
13719cddadbSmiod void comms_speed_change(void *, uint);
13819cddadbSmiod
13919cddadbSmiod /*
14019cddadbSmiod * autoconf glue.
14119cddadbSmiod */
14219cddadbSmiod
14319cddadbSmiod static const char *comms_names[] = {
14419cddadbSmiod "su",
14519cddadbSmiod "su_pnp",
14619cddadbSmiod NULL
14719cddadbSmiod };
14819cddadbSmiod
14919cddadbSmiod int
comms_ismouse(int node)15019cddadbSmiod comms_ismouse(int node)
15119cddadbSmiod {
15219cddadbSmiod if (OF_getproplen(node, "mouse") == 0)
15319cddadbSmiod return 10;
15419cddadbSmiod return 0;
15519cddadbSmiod }
15619cddadbSmiod
15719cddadbSmiod int
comms_match(struct device * parent,void * match,void * aux)15819cddadbSmiod comms_match(struct device *parent, void *match, void *aux)
15919cddadbSmiod {
16019cddadbSmiod struct ebus_attach_args *ea = aux;
16119cddadbSmiod int i;
16219cddadbSmiod
16319cddadbSmiod for (i = 0; comms_names[i]; i++)
16419cddadbSmiod if (strcmp(ea->ea_name, comms_names[i]) == 0)
16519cddadbSmiod return comms_ismouse(ea->ea_node);
16619cddadbSmiod
16719cddadbSmiod if (strcmp(ea->ea_name, "serial") == 0) {
16819cddadbSmiod char compat[80];
16919cddadbSmiod
17019cddadbSmiod if ((i = OF_getproplen(ea->ea_node, "compatible")) &&
17119cddadbSmiod OF_getprop(ea->ea_node, "compatible", compat,
17219cddadbSmiod sizeof(compat)) == i) {
17319cddadbSmiod if (strcmp(compat, "su16550") == 0 ||
17419cddadbSmiod strcmp(compat, "su") == 0)
17519cddadbSmiod return comms_ismouse(ea->ea_node);
17619cddadbSmiod }
17719cddadbSmiod }
17819cddadbSmiod return 0;
17919cddadbSmiod }
18019cddadbSmiod
18119cddadbSmiod void
comms_attach(struct device * parent,struct device * self,void * aux)18219cddadbSmiod comms_attach(struct device *parent, struct device *self, void *aux)
18319cddadbSmiod {
18419cddadbSmiod struct comms_softc *sc = (void *)self;
18519cddadbSmiod struct ebus_attach_args *ea = aux;
18619cddadbSmiod
18719cddadbSmiod sc->sc_iot = ea->ea_memtag;
18819cddadbSmiod
18919cddadbSmiod sc->sc_rxget = sc->sc_rxput = sc->sc_rxbeg = sc->sc_rxbuf;
19019cddadbSmiod sc->sc_rxend = sc->sc_rxbuf + COMMS_RX_RING;
19119cddadbSmiod sc->sc_rxcnt = 0;
19219cddadbSmiod
19319cddadbSmiod /* we really want IPL_TTY here. */
19419cddadbSmiod sc->sc_si = softintr_establish(IPL_TTY, comms_softintr, sc);
19519cddadbSmiod if (sc->sc_si == NULL) {
19619cddadbSmiod printf(": can't get soft intr\n");
19719cddadbSmiod return;
19819cddadbSmiod }
19919cddadbSmiod
20019cddadbSmiod /* Use prom address if available, otherwise map it. */
20119cddadbSmiod if (ea->ea_nvaddrs && bus_space_map(ea->ea_memtag, ea->ea_vaddrs[0], 0,
20219cddadbSmiod BUS_SPACE_MAP_PROMADDRESS, &sc->sc_ioh) == 0) {
20319cddadbSmiod sc->sc_iot = ea->ea_memtag;
20419cddadbSmiod } else if (ebus_bus_map(ea->ea_memtag, 0,
20519cddadbSmiod EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
20619cddadbSmiod ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
20719cddadbSmiod sc->sc_iot = ea->ea_memtag;
20819cddadbSmiod } else if (ebus_bus_map(ea->ea_iotag, 0,
20919cddadbSmiod EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
21019cddadbSmiod ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
21119cddadbSmiod sc->sc_iot = ea->ea_iotag;
21219cddadbSmiod } else {
21319cddadbSmiod printf(": can't map register space\n");
21419cddadbSmiod return;
21519cddadbSmiod }
21619cddadbSmiod
21719cddadbSmiod sc->sc_ih = bus_intr_establish(sc->sc_iot,
21819cddadbSmiod ea->ea_intrs[0], IPL_TTY, 0, comms_hardintr, sc, self->dv_xname);
21919cddadbSmiod if (sc->sc_ih == NULL) {
22019cddadbSmiod printf(": can't get hard intr\n");
22119cddadbSmiod return;
22219cddadbSmiod }
22319cddadbSmiod
22419cddadbSmiod /* Initialize hardware. */
22519cddadbSmiod sc->sc_ier = 0;
22619cddadbSmiod comms_speed_change(sc, INIT_SPEED);
22719cddadbSmiod
22819cddadbSmiod sc->sc_base.sc_speed_change = comms_speed_change;
22919cddadbSmiod
23019cddadbSmiod sunms_attach(&sc->sc_base, &comms_accessops);
23119cddadbSmiod }
23219cddadbSmiod
23319cddadbSmiod /*
23419cddadbSmiod * wsmouse accessops.
23519cddadbSmiod */
23619cddadbSmiod
23719cddadbSmiod void
comms_disable(void * v)23819cddadbSmiod comms_disable(void *v)
23919cddadbSmiod {
24019cddadbSmiod struct comms_softc *sc = v;
24119cddadbSmiod int s;
24219cddadbSmiod
24319cddadbSmiod s = spltty();
24419cddadbSmiod sc->sc_ier = 0;
24519cddadbSmiod COM_WRITE(sc, com_ier, sc->sc_ier);
24619cddadbSmiod splx(s);
24719cddadbSmiod }
24819cddadbSmiod
24919cddadbSmiod int
comms_enable(void * v)25019cddadbSmiod comms_enable(void *v)
25119cddadbSmiod {
25219cddadbSmiod struct comms_softc *sc = v;
25319cddadbSmiod int s;
25419cddadbSmiod
25519cddadbSmiod s = spltty();
25619cddadbSmiod sc->sc_ier = IER_ERXRDY;
25719cddadbSmiod COM_WRITE(sc, com_ier, sc->sc_ier);
25819cddadbSmiod splx(s);
25919cddadbSmiod
26019cddadbSmiod return 0;
26119cddadbSmiod }
26219cddadbSmiod
26319cddadbSmiod /*
26419cddadbSmiod * com glue.
26519cddadbSmiod */
26619cddadbSmiod
26719cddadbSmiod void
comms_softintr(void * v)26819cddadbSmiod comms_softintr(void *v)
26919cddadbSmiod {
27019cddadbSmiod struct comms_softc *sc = v;
27119cddadbSmiod uint8_t c;
27219cddadbSmiod
27319cddadbSmiod /*
27419cddadbSmiod * If we have a baud rate change pending, do it now.
27519cddadbSmiod * This will reset the rx ring, so we can proceed safely.
27619cddadbSmiod */
27719cddadbSmiod if (sc->sc_base.sc_state == STATE_RATE_CHANGE) {
27819cddadbSmiod sunms_speed_change(&sc->sc_base);
27919cddadbSmiod }
28019cddadbSmiod
28119cddadbSmiod /*
28219cddadbSmiod * Copy data from the receive ring, if any, to the event layer.
28319cddadbSmiod */
28419cddadbSmiod while (sc->sc_rxcnt) {
28519cddadbSmiod c = *sc->sc_rxget;
28619cddadbSmiod if (++sc->sc_rxget == sc->sc_rxend)
28719cddadbSmiod sc->sc_rxget = sc->sc_rxbeg;
28819cddadbSmiod sc->sc_rxcnt--;
28919cddadbSmiod sunms_input(&sc->sc_base, c);
29019cddadbSmiod }
29119cddadbSmiod }
29219cddadbSmiod
29319cddadbSmiod int
comms_hardintr(void * v)29419cddadbSmiod comms_hardintr(void *v)
29519cddadbSmiod {
29619cddadbSmiod struct comms_softc *sc = v;
29719cddadbSmiod u_int8_t iir, lsr, data;
29819cddadbSmiod int needsoft = 0;
29919cddadbSmiod
30019cddadbSmiod /* Nothing to do */
30119cddadbSmiod iir = COM_READ(sc, com_iir);
30219cddadbSmiod if (ISSET(iir, IIR_NOPEND))
30319cddadbSmiod return 0;
30419cddadbSmiod
30519cddadbSmiod for (;;) {
30619cddadbSmiod lsr = COM_READ(sc, com_lsr);
30719cddadbSmiod
30819cddadbSmiod if (ISSET(lsr, LSR_BI)) {
30919cddadbSmiod if (sc->sc_base.sc_state != STATE_RATE_CHANGE &&
31019cddadbSmiod ++sc->sc_base.sc_brk > 1) {
31119cddadbSmiod sc->sc_base.sc_state = STATE_RATE_CHANGE;
31219cddadbSmiod needsoft = 1;
31319cddadbSmiod #ifdef DEBUG
31419cddadbSmiod printf("%s: break detected, changing speed\n",
31519cddadbSmiod sc->sc_base.sc_dev.dv_xname);
31619cddadbSmiod #endif
31719cddadbSmiod }
31819cddadbSmiod }
31919cddadbSmiod
32019cddadbSmiod if (ISSET(lsr, LSR_RXRDY)) {
32119cddadbSmiod needsoft = 1;
32219cddadbSmiod
32319cddadbSmiod do {
32419cddadbSmiod data = COM_READ(sc, com_data);
32519cddadbSmiod if (sc->sc_base.sc_state != STATE_RATE_CHANGE &&
32619cddadbSmiod sc->sc_rxcnt != COMMS_RX_RING) {
32719cddadbSmiod *sc->sc_rxput = data;
32819cddadbSmiod if (++sc->sc_rxput == sc->sc_rxend)
32919cddadbSmiod sc->sc_rxput = sc->sc_rxbeg;
33019cddadbSmiod sc->sc_rxcnt++;
33119cddadbSmiod }
33219cddadbSmiod lsr = COM_READ(sc, com_lsr);
33319cddadbSmiod } while (ISSET(lsr, LSR_RXRDY));
33419cddadbSmiod }
33519cddadbSmiod
33619cddadbSmiod iir = COM_READ(sc, com_iir);
33719cddadbSmiod if (ISSET(iir, IIR_NOPEND))
33819cddadbSmiod break;
33919cddadbSmiod }
34019cddadbSmiod
34119cddadbSmiod if (needsoft)
34219cddadbSmiod softintr_schedule(sc->sc_si);
34319cddadbSmiod
34419cddadbSmiod return 1;
34519cddadbSmiod }
34619cddadbSmiod
34719cddadbSmiod /*
34819cddadbSmiod * Reinitialize the line to a different speed. Invoked at spltty().
34919cddadbSmiod */
35019cddadbSmiod void
comms_speed_change(void * v,uint bps)35119cddadbSmiod comms_speed_change(void *v, uint bps)
35219cddadbSmiod {
35319cddadbSmiod struct comms_softc *sc = v;
35419cddadbSmiod int ospeed;
35519cddadbSmiod
35619cddadbSmiod /*
35719cddadbSmiod * Eat everything on the line.
35819cddadbSmiod */
35919cddadbSmiod while (ISSET(COM_READ(sc, com_lsr), LSR_RXRDY))
36019cddadbSmiod COM_READ(sc, com_data);
36119cddadbSmiod
36219cddadbSmiod ospeed = comspeed(BAUD_BASE, bps);
36319cddadbSmiod
36419cddadbSmiod /* disable interrupts while the chip is reprogrammed */
36519cddadbSmiod COM_WRITE(sc, com_ier, 0);
36619cddadbSmiod
36719cddadbSmiod COM_WRITE(sc, com_lcr, LCR_DLAB);
36819cddadbSmiod COM_WRITE(sc, com_dlbl, ospeed);
36919cddadbSmiod COM_WRITE(sc, com_dlbh, ospeed >> 8);
37019cddadbSmiod /* 8 data bits, no parity, 2 stop bits */
37119cddadbSmiod COM_WRITE(sc, com_lcr, LCR_8BITS | LCR_PNONE | LCR_STOPB);
37219cddadbSmiod COM_READ(sc, com_iir);
37319cddadbSmiod
37419cddadbSmiod COM_WRITE(sc, com_mcr, MCR_IENABLE | MCR_DTR | MCR_RTS);
37519cddadbSmiod /* XXX do something about the FIFO? */
37619cddadbSmiod
37719cddadbSmiod COM_WRITE(sc, com_ier, sc->sc_ier);
37819cddadbSmiod }
379