xref: /openbsd-src/sys/arch/sparc64/dev/comms_ebus.c (revision a7f5b4e97487baf7da391542800e3ae56df6ae45)
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