xref: /openbsd/sys/arch/powerpc64/dev/opalcons.c (revision 09467b48)
1 /*	$OpenBSD: opalcons.c,v 1.2 2020/06/26 19:13:28 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/conf.h>
20 #include <sys/device.h>
21 #include <sys/systm.h>
22 #include <sys/tty.h>
23 
24 #include <machine/bus.h>
25 #include <machine/conf.h>
26 #include <machine/fdt.h>
27 #include <machine/opal.h>
28 
29 #include <dev/cons.h>
30 
31 #include <dev/ofw/openfirm.h>
32 #include <dev/ofw/fdt.h>
33 
34 struct opalcons_softc {
35 	struct device	sc_dev;
36 	uint64_t	sc_reg;
37 
38 	struct tty	*sc_tty;
39 	void		*sc_ih;
40 	void		*sc_si;
41 };
42 
43 int	opalcons_match(struct device *, void *, void *);
44 void	opalcons_attach(struct device *, struct device *, void *);
45 
46 struct cfattach	opalcons_ca = {
47 	sizeof (struct opalcons_softc), opalcons_match, opalcons_attach
48 };
49 
50 struct cfdriver opalcons_cd = {
51 	NULL, "opalcons", DV_DULL
52 };
53 
54 int	opalcons_intr(void *);
55 void	opalcons_softintr(void *);
56 
57 void	opalconsstart(struct tty *);
58 int	opalconsparam(struct tty *, struct termios *);
59 
60 int
61 opalcons_match(struct device *parent, void *match, void *aux)
62 {
63 	struct fdt_attach_args *faa = aux;
64 
65 	return OF_is_compatible(faa->fa_node, "ibm,opal-console-raw");
66 }
67 
68 void
69 opalcons_attach(struct device *parent, struct device *self, void *aux)
70 {
71 	struct opalcons_softc *sc = (struct opalcons_softc *)self;
72 	struct fdt_attach_args *faa = aux;
73 	int maj;
74 
75 	sc->sc_reg = OF_getpropint(faa->fa_node, "reg", 0);
76 
77 	if (1) {
78 		/* Locate the major number. */
79 		for (maj = 0; maj < nchrdev; maj++)
80 			if (cdevsw[maj].d_open == opalconsopen)
81 				break;
82 		cn_tab->cn_dev = makedev(maj, self->dv_unit);
83 
84 		printf(": console");
85 	}
86 
87 	sc->sc_si = softintr_establish(IPL_TTY, opalcons_softintr, sc);
88 	if (sc->sc_si == NULL) {
89 		printf(": can't establish soft interrupt");
90 		return;
91 	}
92 
93 	sc->sc_ih = opal_intr_establish(OPAL_EVENT_CONSOLE_INPUT, IPL_TTY,
94 	    opalcons_intr, sc);
95 	if (sc->sc_ih == NULL) {
96 		printf(": can't establish interrupt\n");
97 		return;
98 	}
99 
100 	printf("\n");
101 }
102 
103 struct opalcons_softc *
104 opalcons_sc(dev_t dev)
105 {
106 	int unit = minor(dev);
107 
108 	if (unit >= opalcons_cd.cd_ndevs)
109 		return NULL;
110 	return (struct opalcons_softc *)opalcons_cd.cd_devs[unit];
111 }
112 
113 
114 int
115 opalcons_intr(void *arg)
116 {
117 	struct opalcons_softc *sc = arg;
118 
119 	if (sc->sc_tty)
120 		softintr_schedule(sc->sc_si);
121 
122 	return 1;
123 }
124 
125 void
126 opalcons_putc(dev_t dev, int c)
127 {
128 	struct opalcons_softc *sc = opalcons_sc(dev);
129 	uint64_t len = 1;
130 	char ch = c;
131 
132 	opal_console_write(sc->sc_reg, opal_phys(&len), opal_phys(&ch));
133 }
134 
135 int
136 opalconsopen(dev_t dev, int flag, int mode, struct proc *p)
137 {
138 	struct opalcons_softc *sc = opalcons_sc(dev);
139 	struct tty *tp;
140 
141 	if (sc == NULL)
142 		return ENXIO;
143 
144 	if (sc->sc_tty)
145 		tp = sc->sc_tty;
146 	else
147 		tp = sc->sc_tty = ttymalloc(0);
148 
149 	tp->t_oproc = opalconsstart;
150 	tp->t_param = opalconsparam;
151 	tp->t_dev = dev;
152 	if ((tp->t_state & TS_ISOPEN) == 0) {
153 		ttychars(tp);
154 		tp->t_iflag = TTYDEF_IFLAG;
155 		tp->t_oflag = TTYDEF_OFLAG;
156 		tp->t_cflag = TTYDEF_CFLAG;
157 		tp->t_lflag = TTYDEF_LFLAG;
158 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
159 		ttsetwater(tp);
160 	} else if ((tp->t_state & TS_XCLUDE) && suser(p))
161 		return EBUSY;
162 	tp->t_state |= TS_CARR_ON;
163 
164 	return (*linesw[tp->t_line].l_open)(dev, tp, p);
165 }
166 
167 int
168 opalconsclose(dev_t dev, int flag, int mode, struct proc *p)
169 {
170 	struct opalcons_softc *sc = opalcons_sc(dev);
171 	struct tty *tp;
172 
173 	if (sc == NULL)
174 		return ENXIO;
175 
176 	tp = sc->sc_tty;
177 	(*linesw[tp->t_line].l_close)(tp, flag, p);
178 	ttyclose(tp);
179 	return 0;
180 }
181 
182 int
183 opalconsread(dev_t dev, struct uio *uio, int flag)
184 {
185 	struct opalcons_softc *sc = opalcons_sc(dev);
186 	struct tty *tp;
187 
188 	if (sc == NULL)
189 		return ENXIO;
190 
191 	tp = sc->sc_tty;
192 	return (*linesw[tp->t_line].l_read)(tp, uio, flag);
193 }
194 
195 int
196 opalconswrite(dev_t dev, struct uio *uio, int flag)
197 {
198 	struct opalcons_softc *sc = opalcons_sc(dev);
199 	struct tty *tp;
200 
201 	if (sc == NULL)
202 		return ENXIO;
203 
204 	tp = sc->sc_tty;
205 	return (*linesw[tp->t_line].l_write)(tp, uio, flag);
206 }
207 
208 int
209 opalconsioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
210 {
211 	struct opalcons_softc *sc = opalcons_sc(dev);
212 	struct tty *tp;
213 	int error;
214 
215 	if (sc == NULL)
216 		return ENXIO;
217 
218 	tp = sc->sc_tty;
219 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
220 	if (error >= 0)
221 		return error;
222 	error = ttioctl(tp, cmd, data, flag, p);
223 	if (error >= 0)
224 		return error;
225 
226 	return ENOTTY;
227 }
228 
229 void
230 opalconsstart(struct tty *tp)
231 {
232 	int s;
233 
234 	s = spltty();
235 	if (tp->t_state & (TS_TTSTOP | TS_BUSY)) {
236 		splx(s);
237 		return;
238 	}
239 	ttwakeupwr(tp);
240 	tp->t_state |= TS_BUSY;
241 	while (tp->t_outq.c_cc != 0)
242 		opalcons_putc(tp->t_dev, getc(&tp->t_outq));
243 	tp->t_state &= ~TS_BUSY;
244 	splx(s);
245 }
246 
247 int
248 opalconsstop(struct tty *tp, int flag)
249 {
250 	int s;
251 
252 	s = spltty();
253 	if (tp->t_state & TS_BUSY)
254 		if ((tp->t_state & TS_TTSTOP) == 0)
255 			tp->t_state |= TS_FLUSH;
256 	splx(s);
257 	return 0;
258 }
259 
260 struct tty *
261 opalconstty(dev_t dev)
262 {
263 	struct opalcons_softc *sc = opalcons_sc(dev);
264 
265 	if (sc == NULL)
266 		return NULL;
267 
268 	return sc->sc_tty;
269 }
270 
271 int
272 opalconsparam(struct tty *tp, struct termios *t)
273 {
274 	tp->t_ispeed = t->c_ispeed;
275 	tp->t_ospeed = t->c_ospeed;
276 	tp->t_cflag = t->c_cflag;
277 	return 0;
278 }
279 
280 int
281 opalcons_getc(struct opalcons_softc *sc, int *cp)
282 {
283 	uint64_t len = 1;
284 	uint64_t error;
285 	char ch;
286 
287 	error = opal_console_read(sc->sc_reg, opal_phys(&len), opal_phys(&ch));
288 	if (error != OPAL_SUCCESS || len != 1)
289 		return 0;
290 
291 	*cp = ch;
292 	return 1;
293 }
294 
295 void
296 opalcons_softintr(void *arg)
297 {
298 	struct opalcons_softc *sc = arg;
299 	struct tty *tp = sc->sc_tty;
300 	int c;
301 
302 	while (opalcons_getc(sc, &c)) {
303 		if (tp->t_state & TS_ISOPEN)
304 			(*linesw[tp->t_line].l_rint)(c, tp);
305 	}
306 }
307