xref: /openbsd-src/sys/arch/powerpc64/dev/opalcons.c (revision 471aeecfc619bc9b69519928152daf993376c2a1)
1 /*	$OpenBSD: opalcons.c,v 1.4 2022/04/06 18:59:27 naddy 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 const 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
opalcons_match(struct device * parent,void * match,void * aux)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
opalcons_attach(struct device * parent,struct device * self,void * aux)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 	/* Locate the major number. */
78 	for (maj = 0; maj < nchrdev; maj++)
79 		if (cdevsw[maj].d_open == opalconsopen)
80 			break;
81 
82 	/*
83 	 * Unconditionally set the major/minor here since we attach
84 	 * early and are the fallback console device
85 	 */
86 	cn_tab->cn_dev = makedev(maj, self->dv_unit);
87 
88 	if (faa->fa_node == stdout_node)
89 		printf(": console");
90 
91 	sc->sc_si = softintr_establish(IPL_TTY, opalcons_softintr, sc);
92 	if (sc->sc_si == NULL) {
93 		printf(": can't establish soft interrupt");
94 		return;
95 	}
96 
97 	sc->sc_ih = opal_intr_establish(OPAL_EVENT_CONSOLE_INPUT, IPL_TTY,
98 	    opalcons_intr, sc);
99 	if (sc->sc_ih == NULL) {
100 		printf(": can't establish interrupt\n");
101 		return;
102 	}
103 
104 	printf("\n");
105 }
106 
107 struct opalcons_softc *
opalcons_sc(dev_t dev)108 opalcons_sc(dev_t dev)
109 {
110 	int unit = minor(dev);
111 
112 	if (unit >= opalcons_cd.cd_ndevs)
113 		return NULL;
114 	return (struct opalcons_softc *)opalcons_cd.cd_devs[unit];
115 }
116 
117 
118 int
opalcons_intr(void * arg)119 opalcons_intr(void *arg)
120 {
121 	struct opalcons_softc *sc = arg;
122 
123 	if (sc->sc_tty)
124 		softintr_schedule(sc->sc_si);
125 
126 	return 1;
127 }
128 
129 void
opalcons_putc(dev_t dev,int c)130 opalcons_putc(dev_t dev, int c)
131 {
132 	struct opalcons_softc *sc = opalcons_sc(dev);
133 	uint64_t len = 1;
134 	char ch = c;
135 
136 	opal_console_write(sc->sc_reg, opal_phys(&len), opal_phys(&ch));
137 }
138 
139 int
opalconsopen(dev_t dev,int flag,int mode,struct proc * p)140 opalconsopen(dev_t dev, int flag, int mode, struct proc *p)
141 {
142 	struct opalcons_softc *sc = opalcons_sc(dev);
143 	struct tty *tp;
144 
145 	if (sc == NULL)
146 		return ENXIO;
147 
148 	if (sc->sc_tty)
149 		tp = sc->sc_tty;
150 	else
151 		tp = sc->sc_tty = ttymalloc(0);
152 
153 	tp->t_oproc = opalconsstart;
154 	tp->t_param = opalconsparam;
155 	tp->t_dev = dev;
156 	if ((tp->t_state & TS_ISOPEN) == 0) {
157 		ttychars(tp);
158 		tp->t_iflag = TTYDEF_IFLAG;
159 		tp->t_oflag = TTYDEF_OFLAG;
160 		tp->t_cflag = TTYDEF_CFLAG;
161 		tp->t_lflag = TTYDEF_LFLAG;
162 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
163 		ttsetwater(tp);
164 	} else if ((tp->t_state & TS_XCLUDE) && suser(p))
165 		return EBUSY;
166 	tp->t_state |= TS_CARR_ON;
167 
168 	return (*linesw[tp->t_line].l_open)(dev, tp, p);
169 }
170 
171 int
opalconsclose(dev_t dev,int flag,int mode,struct proc * p)172 opalconsclose(dev_t dev, int flag, int mode, struct proc *p)
173 {
174 	struct opalcons_softc *sc = opalcons_sc(dev);
175 	struct tty *tp;
176 
177 	if (sc == NULL)
178 		return ENXIO;
179 
180 	tp = sc->sc_tty;
181 	(*linesw[tp->t_line].l_close)(tp, flag, p);
182 	ttyclose(tp);
183 	return 0;
184 }
185 
186 int
opalconsread(dev_t dev,struct uio * uio,int flag)187 opalconsread(dev_t dev, struct uio *uio, int flag)
188 {
189 	struct opalcons_softc *sc = opalcons_sc(dev);
190 	struct tty *tp;
191 
192 	if (sc == NULL)
193 		return ENXIO;
194 
195 	tp = sc->sc_tty;
196 	return (*linesw[tp->t_line].l_read)(tp, uio, flag);
197 }
198 
199 int
opalconswrite(dev_t dev,struct uio * uio,int flag)200 opalconswrite(dev_t dev, struct uio *uio, int flag)
201 {
202 	struct opalcons_softc *sc = opalcons_sc(dev);
203 	struct tty *tp;
204 
205 	if (sc == NULL)
206 		return ENXIO;
207 
208 	tp = sc->sc_tty;
209 	return (*linesw[tp->t_line].l_write)(tp, uio, flag);
210 }
211 
212 int
opalconsioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)213 opalconsioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
214 {
215 	struct opalcons_softc *sc = opalcons_sc(dev);
216 	struct tty *tp;
217 	int error;
218 
219 	if (sc == NULL)
220 		return ENXIO;
221 
222 	tp = sc->sc_tty;
223 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
224 	if (error >= 0)
225 		return error;
226 	error = ttioctl(tp, cmd, data, flag, p);
227 	if (error >= 0)
228 		return error;
229 
230 	return ENOTTY;
231 }
232 
233 void
opalconsstart(struct tty * tp)234 opalconsstart(struct tty *tp)
235 {
236 	int s;
237 
238 	s = spltty();
239 	if (tp->t_state & (TS_TTSTOP | TS_BUSY)) {
240 		splx(s);
241 		return;
242 	}
243 	ttwakeupwr(tp);
244 	tp->t_state |= TS_BUSY;
245 	while (tp->t_outq.c_cc != 0)
246 		opalcons_putc(tp->t_dev, getc(&tp->t_outq));
247 	tp->t_state &= ~TS_BUSY;
248 	splx(s);
249 }
250 
251 int
opalconsstop(struct tty * tp,int flag)252 opalconsstop(struct tty *tp, int flag)
253 {
254 	int s;
255 
256 	s = spltty();
257 	if (tp->t_state & TS_BUSY)
258 		if ((tp->t_state & TS_TTSTOP) == 0)
259 			tp->t_state |= TS_FLUSH;
260 	splx(s);
261 	return 0;
262 }
263 
264 struct tty *
opalconstty(dev_t dev)265 opalconstty(dev_t dev)
266 {
267 	struct opalcons_softc *sc = opalcons_sc(dev);
268 
269 	if (sc == NULL)
270 		return NULL;
271 
272 	return sc->sc_tty;
273 }
274 
275 int
opalconsparam(struct tty * tp,struct termios * t)276 opalconsparam(struct tty *tp, struct termios *t)
277 {
278 	tp->t_ispeed = t->c_ispeed;
279 	tp->t_ospeed = t->c_ospeed;
280 	tp->t_cflag = t->c_cflag;
281 	return 0;
282 }
283 
284 int
opalcons_getc(struct opalcons_softc * sc,int * cp)285 opalcons_getc(struct opalcons_softc *sc, int *cp)
286 {
287 	uint64_t len = 1;
288 	uint64_t error;
289 	char ch;
290 
291 	error = opal_console_read(sc->sc_reg, opal_phys(&len), opal_phys(&ch));
292 	if (error != OPAL_SUCCESS || len != 1)
293 		return 0;
294 
295 	*cp = ch;
296 	return 1;
297 }
298 
299 void
opalcons_softintr(void * arg)300 opalcons_softintr(void *arg)
301 {
302 	struct opalcons_softc *sc = arg;
303 	struct tty *tp = sc->sc_tty;
304 	int c;
305 
306 	while (opalcons_getc(sc, &c)) {
307 		if (tp->t_state & TS_ISOPEN)
308 			(*linesw[tp->t_line].l_rint)(c, tp);
309 	}
310 }
311