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