1 /*        $NetBSD: pstwo.c,v 1.5 2012/10/27 17:17:51 chs Exp $ */
2 
3 /*
4  * Copyright (c) 2006 Jachym Holecek
5  * All rights reserved.
6  *
7  * Written for DFC Design, s.r.o.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: pstwo.c,v 1.5 2012/10/27 17:17:51 chs Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/conf.h>
38 #include <sys/device.h>
39 #include <sys/file.h>
40 #include <sys/ioctl.h>
41 #include <sys/kernel.h>
42 #include <sys/proc.h>
43 #include <sys/time.h>
44 #include <sys/syslog.h>
45 
46 #include <machine/intr.h>
47 #include <sys/bus.h>
48 
49 #include <dev/pckbport/pckbportvar.h>
50 
51 #include <evbppc/virtex/virtex.h>
52 #include <evbppc/virtex/dev/xcvbusvar.h>
53 #include <evbppc/virtex/dev/pstworeg.h>
54 
55 
56 #define PSTWO_RXBUF_SIZE      32
57 #define NEXT(idx)             (void)((idx) = ((idx) + 1) % PSTWO_RXBUF_SIZE)
58 
59 struct pstwo_softc {
60           device_t            sc_dev;
61           void                          *sc_ih;
62 
63           bus_space_tag_t     sc_iot;
64           bus_space_handle_t  sc_ioh;
65 
66           pckbport_tag_t                sc_pt;
67           pckbport_slot_t     sc_slot;
68 };
69 
70 static int          pstwo_intr(void *);
71 static void         pstwo_reset(bus_space_tag_t, bus_space_handle_t, bool);
72 #if 0
73 static void         pstwo_printreg(struct pstwo_softc *);
74 #endif
75 
76 /* Interface to pckbport. */
77 static int          pstwo_xt_translation(void *, pckbport_slot_t, int);
78 static int          pstwo_send_devcmd(void *, pckbport_slot_t, u_char);
79 static int          pstwo_poll_data1(void *, pckbport_slot_t);
80 static void         pstwo_slot_enable(void *, pckbport_slot_t, int);
81 static void         pstwo_intr_establish(void *, pckbport_slot_t);
82 static void         pstwo_set_poll(void *, pckbport_slot_t, int);
83 
84 /* Generic device. */
85 static void         pstwo_attach(device_t, device_t, void *);
86 
87 CFATTACH_DECL_NEW(pstwo, sizeof(struct pstwo_softc),
88     xcvbus_child_match, pstwo_attach, NULL, NULL);
89 
90 static struct pckbport_accessops pstwo_ops = {
91           .t_xt_translation   = pstwo_xt_translation,
92           .t_send_devcmd                = pstwo_send_devcmd,
93           .t_poll_data1                 = pstwo_poll_data1,
94           .t_slot_enable                = pstwo_slot_enable,
95           .t_intr_establish   = pstwo_intr_establish,
96           .t_set_poll                   = pstwo_set_poll,
97 };
98 
99 static void
pstwo_attach(device_t parent,device_t self,void * aux)100 pstwo_attach(device_t parent, device_t self, void *aux)
101 {
102           struct xcvbus_attach_args     *vaa = aux;
103           struct pstwo_softc            *sc = device_private(self);
104           int                                     i;
105 
106           aprint_normal(": PS2 port\n");
107 
108           if ((sc->sc_ih = intr_establish(vaa->vaa_intr, IST_LEVEL, IPL_TTY,
109               pstwo_intr, sc)) == NULL) {
110                     aprint_error_dev(self, "could not establish interrupt\n");
111                     return;
112           }
113 
114           sc->sc_dev = self;
115           sc->sc_iot = vaa->vaa_iot;
116 
117           if (bus_space_map(vaa->vaa_iot, vaa->vaa_addr, PSTWO_SIZE, 0,
118               &sc->sc_ioh) != 0) {
119                     aprint_error_dev(self, "could not map registers\n");
120                     return ;
121           }
122 
123           pstwo_reset(sc->sc_iot, sc->sc_ioh, 1);
124 
125           sc->sc_pt = pckbport_attach(sc, &pstwo_ops);
126 
127           /* Emulate the concept of "slot" to make pms(4)/pckbd(4) happy. */
128           for (i = 0; i < PCKBPORT_NSLOTS; i++)
129                     if (pckbport_attach_slot(self, sc->sc_pt, i) != NULL) {
130                               sc->sc_slot = i;
131                               break;                        /* only one device allowed */
132                     }
133 }
134 
135 #if 0
136 static void
137 pstwo_printreg(struct pstwo_softc *sc)
138 {
139 #define PRINTREG(name, reg) \
140           printf("%s: [0x%08x] %s -> 0x%08x\n", device_xname(sc->sc_dev), \
141               reg, name, bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg))
142 
143           PRINTREG("status   ", PSTWO_STAT);
144           PRINTREG("recv byte", PSTWO_RX_DATA);
145           PRINTREG("intr stat", PSTWO_INTR_STAT);
146           PRINTREG("intr mask", PSTWO_INTR_MSET);
147 
148 #undef PRINTREG
149 }
150 #endif
151 
152 static int
pstwo_intr(void * arg)153 pstwo_intr(void *arg)
154 {
155           struct pstwo_softc  *sc = arg;
156           uint32_t            stat;
157 
158           stat = bus_space_read_4(sc->sc_iot, sc->sc_ioh, PSTWO_INTR_STAT);
159           bus_space_write_4(sc->sc_iot, sc->sc_ioh, PSTWO_INTR_ACK, stat);
160 
161           if (stat & INTR_RX_FULL) {
162                     uint32_t  val;
163 
164                     val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, PSTWO_RX_DATA);
165                     pckbportintr(sc->sc_pt, sc->sc_slot, DATA_RECV(val));
166           }
167           return (0);
168 }
169 
170 static void
pstwo_reset(bus_space_tag_t iot,bus_space_handle_t ioh,bool restart)171 pstwo_reset(bus_space_tag_t iot, bus_space_handle_t ioh, bool restart)
172 {
173           bus_space_write_4(iot, ioh, PSTWO_CTRL, CTRL_RESET);
174 
175           delay(10);
176 
177           if (restart) {
178                     bus_space_write_4(iot, ioh, PSTWO_CTRL, ~CTRL_RESET);
179                     bus_space_write_4(iot, ioh, PSTWO_INTR_MSET, INTR_ANY);
180           }
181 }
182 
183 /*
184  * pstwo_wait:
185  *
186  *        Wait while status bits indicated by ${mask} have the value ${set}.
187  *        Return zero on success, one on timeout.
188  */
189 static int
pstwo_wait(struct pstwo_softc * sc,uint32_t mask,bool set)190 pstwo_wait(struct pstwo_softc *sc, uint32_t mask, bool set)
191 {
192           uint32_t            val = (set ? mask : 0);
193           int                           i = 1000;
194 
195           while (i--) {
196                     if ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, PSTWO_STAT) &
197                         mask) == val) {
198                               delay(10);
199                               continue;
200                     }
201 
202                     return (0);
203           }
204 
205           return (1);
206 }
207 
208 /*
209  * pckbport
210  */
211 
212 static int
pstwo_xt_translation(void * arg,pckbport_slot_t port,int enable)213 pstwo_xt_translation(void *arg, pckbport_slot_t port, int enable)
214 {
215           /* Can't do translation, so disable succeeds & enable fails. */
216           return (!enable);
217 }
218 
219 static int
pstwo_send_devcmd(void * arg,pckbport_slot_t slot,u_char byte)220 pstwo_send_devcmd(void *arg, pckbport_slot_t slot, u_char byte)
221 {
222           struct pstwo_softc  *sc = arg;
223 
224           if (pstwo_wait(sc, STAT_TX_BUSY, 1))
225                     return (0);
226 
227           bus_space_write_4(sc->sc_iot, sc->sc_ioh, PSTWO_TX_DATA,
228               DATA_SEND(byte));
229           return (1);
230 }
231 
232 static int
pstwo_poll_data1(void * arg,pckbport_slot_t slot)233 pstwo_poll_data1(void *arg, pckbport_slot_t slot)
234 {
235           struct pstwo_softc  *sc = arg;
236           uint32_t            val;
237 
238           if (pstwo_wait(sc, STAT_RX_DONE, 0))
239                     return (-1);
240 
241           val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, PSTWO_RX_DATA);
242           return DATA_RECV(val);
243 }
244 
245 static void
pstwo_slot_enable(void * arg,pckbport_slot_t slot,int enable)246 pstwo_slot_enable(void *arg, pckbport_slot_t slot, int enable)
247 {
248           /* Nothing to do */
249 }
250 
251 static void
pstwo_intr_establish(void * arg,pckbport_slot_t slot)252 pstwo_intr_establish(void *arg, pckbport_slot_t slot)
253 {
254           /* XXX no-op because we don't support polled mode */
255 }
256 
257 static void
pstwo_set_poll(void * arg,pckbport_slot_t slot,int enable)258 pstwo_set_poll(void *arg, pckbport_slot_t slot, int enable)
259 {
260           /* XXX only needed by console */
261 }
262