1/*	$NetBSD: i8042.c,v 1.5 2005/12/11 12:19:05 christos Exp $	*/
2
3/*
4 * Copyright 1997
5 * Digital Equipment Corporation. All rights reserved.
6 *
7 * This software is furnished under license and may be used and
8 * copied only in accordance with the following terms and conditions.
9 * Subject to these conditions, you may download, copy, install,
10 * use, modify and distribute this software in source and/or binary
11 * form. No title or ownership is transferred hereby.
12 *
13 * 1) Any source code used, modified or distributed must reproduce
14 *    and retain this copyright notice and list of conditions as
15 *    they appear in the source file.
16 *
17 * 2) No right is granted to use any trade name, trademark, or logo of
18 *    Digital Equipment Corporation. Neither the "Digital Equipment
19 *    Corporation" name nor any trademark or logo of Digital Equipment
20 *    Corporation may be used to endorse or promote products derived
21 *    from this software without the prior written permission of
22 *    Digital Equipment Corporation.
23 *
24 * 3) This software is provided "AS-IS" and any express or implied
25 *    warranties, including but not limited to, any implied warranties
26 *    of merchantability, fitness for a particular purpose, or
27 *    non-infringement are disclaimed. In no event shall DIGITAL be
28 *    liable for any damages whatsoever, and in particular, DIGITAL
29 *    shall not be liable for special, indirect, consequential, or
30 *    incidental damages or damages for lost profits, loss of
31 *    revenue or loss of use, whether such damages arise in contract,
32 *    negligence, tort, under statute, in equity, at law or otherwise,
33 *    even if advised of the possibility of such damage.
34 */
35
36/*
37**++
38**
39**  FACILITY:
40**
41**    8042 controller functions.
42**
43**  ABSTRACT:
44**
45**    This file contains routines to access the 8042 keyboard microprocessor.
46**    It hopefully allows a level of abstraction which will permit
47**    simplification of keyboard and mouse drivers which have to share the
48**    same registers when talking to the 8042.
49**
50**  AUTHORS:
51**
52**    John Court, Digital Equipment Corporation.
53**
54**  CREATION DATE:
55**
56**    16/4/1997
57**
58**--
59*/
60
61#include <sys/cdefs.h>
62__KERNEL_RCSID(0, "$NetBSD: i8042.c,v 1.5 2005/12/11 12:19:05 christos Exp $");
63
64#include <sys/param.h>
65#include <sys/kernel.h>
66#include <sys/systm.h>
67#include <sys/bus.h>
68#include <machine/kerndebug.h>
69
70#include <shark/shark/i8042reg.h>
71/*
72** Global variables
73*/
74
75/* Variable to control which debugs printed.  No debug code gets
76** built into this driver unless KERN_DEBUG is defined in the config
77** file.
78*/
79int i8042debug = KERN_DEBUG_WARNING | KERN_DEBUG_ERROR;
80
81/*
82**++
83**  FUNCTIONAL DESCRIPTION
84**
85**     i8042_flush
86**
87**     This routine waits until the input and output buffers
88**     on the 8042 are empty, discarding any characters
89**     in the input buffer.
90**
91**  FORMAL PARAMETERS:
92**
93**     iot    I/O tag for the mapped register space
94**     ioh    I/O handle for the mapped register space
95**
96**  IMPLICIT INPUTS:
97**
98**     none.
99**
100**  IMPLICIT OUTPUTS:
101**
102**     none.
103**
104**  FUNCTION VALUE:
105**
106**     none.
107**--
108*/
109void
110i8042_flush( bus_space_tag_t iot,
111             bus_space_handle_t ioh)
112{
113    /* Wait until input and output buffers are empty */
114    (void)i8042_wait_output(iot,ioh);
115    while (i8042_wait_input(iot,ioh,I8042_ANY_DATA))
116    {
117        (void)bus_space_read_1(iot, ioh, KBDATAPO);
118    }
119    return;
120} /* End i8042_flush */
121
122/*
123**++
124**  FUNCTIONAL DESCRIPTION:
125**
126**     i8042_wait_output
127**
128**     This function is boring.  It just waits until output
129**     can be sent to the 8042 buffer.
130**
131**  FORMAL PARAMETERS:
132**
133**     iot    I/O tag for the mapped register space
134**     ioh    I/O handle for the mapped register space
135**
136**  IMPLICIT INPUTS:
137**
138**     none.
139**
140**  IMPLICIT OUTPUTS:
141**
142**     none.
143**
144**  FUNCTION VALUE:
145**
146**   0 - Timed out waiting to send output.
147**   1 - Can now send output to the 8042.
148**--
149*/
150int
151i8042_wait_output( bus_space_tag_t    iot,
152                   bus_space_handle_t ioh )
153{
154    register u_int     count;
155    int                retValue = 0;
156
157    for (count = I8042_WAIT_THRESHOLD; count; count--)
158    {
159        /* Check if output buffer empty */
160        if ((bus_space_read_1(iot, ioh, KBSTATPO) & KBS_IBF) == 0)
161        {
162            retValue = 1;
163	    break;
164        }
165    }
166    return (retValue);
167} /* End i8042_wait_output */
168
169
170/*
171**++
172**  FUNCTIONAL DESCRIPTION:
173**
174**     i8042_wait_input
175**
176**     This function waits until input is available to be read from
177**     the 8042 output buffer.
178**
179**  FORMAL PARAMETERS:
180**
181**     iot    I/O tag for the mapped register space
182**     ioh    I/O handle for the mapped register space
183**     type   Type of input to wait for (auxiliary, keyboard or any).
184**
185**  IMPLICIT INPUTS:
186**
187**     none.
188**
189**  IMPLICIT OUTPUTS:
190**
191**     none.
192**
193**  FUNCTION VALUE:
194**
195**   0 - Timed out waiting for input
196**   1 - Input available to be read
197**--
198*/
199int
200i8042_wait_input(bus_space_tag_t    iot,
201                 bus_space_handle_t ioh,
202                 u_char             type)
203{
204    register u_int     count;
205    register u_char    status;
206    int                retValue = 0;
207
208    for (count = I8042_WAIT_THRESHOLD; count; count--)
209    {
210        /* Check if there is a character to be read */
211        status = bus_space_read_1(iot, ioh, KBSTATPO);
212        if (((status & type) == type) ||
213            ((type == I8042_ANY_DATA) && (status & KBS_DIB)))
214        {
215            retValue = 1;
216	    break;
217        }
218	I8042_DELAY;
219    }
220    KERN_DEBUG(i8042debug, KERN_DEBUG_INFO,
221	       ("i8042_wait_input: returning : %s\n\tlast status : 0x%x\n",
222		retValue ? "Found Data" : "Exceeded Wait Threshold",
223		status));
224
225    return (retValue);
226} /* End i8042_wait_input */
227
228
229/*
230**++
231**  FUNCTIONAL DESCRIPTION:
232**
233**     i8042_cmd
234**
235**    This function sends a command to the 8042 device or the auxiliary
236**    device hanging off it. The command is retried a
237**    number of times if a resend response is received.
238**
239**  FORMAL PARAMETERS:
240**
241**     iot               I/O tag for the mapped register space
242**     ioh               I/O handle for the mapped register space
243**     auxCmd            An indication of what type of command this is.
244**     checkResponse     A switch indicating whether to read a result after
245**                       executing the command and compare ot with
246**                       "responseExpected".
247**     responseExpected  Only valid if "checkResponse" is non-zero.  This
248**                       is compared with the data value read after the
249**                       command has been executed.
250**     value             Command to send to the device selected by "auxCmd".
251**
252**  IMPLICIT INPUTS:
253**
254**     none.
255**
256**  IMPLICIT OUTPUTS:
257**
258**     none.
259**
260**  FUNCTION VALUE:
261**
262**   0 - Failed to send command or receive acknowledgement for it
263**   1 - Command sent and responded to successfully.
264**--
265*/
266int
267i8042_cmd(bus_space_tag_t    iot,
268          bus_space_handle_t ioh,
269          u_char             auxCmd,
270          u_char             checkResponse,
271          u_char             responseExpected,
272          u_char             value)
273{
274    u_int              retries;
275    register u_char    c = 0;
276    int                status;
277
278    /* Assume failure
279    */
280    status = 0;
281
282    for (retries = I8042_RETRIES;
283         i8042_wait_output(iot,ioh) && retries;
284         retries--)
285    {
286        if (auxCmd == I8042_AUX_CMD)
287        {
288            /* Setup to write command to auxiliary device
289            */
290            bus_space_write_1(iot, ioh, KBCMDPO, KBC_AUXWRITE);
291            /* Write actual command to selected device
292            */
293            if (i8042_wait_output(iot,ioh))
294            {
295                bus_space_write_1(iot, ioh, KBOUTPO, value);
296            }
297            else
298            {
299                KERN_DEBUG(i8042debug, KERN_DEBUG_WARNING,
300                     ("i8042_cmd: failed aux device request of 0x%x\n",
301                      value));
302                break;
303            }
304        }
305        else if (auxCmd == I8042_CMD)
306        {
307            /* Write command to keyboard controller requested.
308            */
309            bus_space_write_1(iot, ioh, KBCMDPO, value);
310        }
311        else if (auxCmd == I8042_KBD_CMD)
312        {
313            /* Write a command to actual keyboard H/W device.
314            */
315            bus_space_write_1(iot, ioh, KBOUTPO, value);
316        }
317        else if (auxCmd == I8042_WRITE_CCB)
318        {
319            /* Write 8042 Controller Command Byte requested
320            */
321            bus_space_write_1(iot, ioh, KBCMDPO, K_LDCMDBYTE);
322            /* Write actual command to selected device
323            */
324            if (i8042_wait_output(iot,ioh))
325            {
326                bus_space_write_1(iot, ioh, KBOUTPO, value);
327            }
328            else
329            {
330                KERN_DEBUG(i8042debug, KERN_DEBUG_WARNING,
331                           ("i8042_cmd: failed contoller command byte "
332                            "write request of 0x%x\n",
333                            value));
334                break;
335            }
336        }
337        else
338        {
339            KERN_DEBUG(i8042debug, KERN_DEBUG_WARNING,
340                       ("i8042_cmd: invalid device identifier of 0x%x\n",
341                        auxCmd));
342            break;
343        }
344
345        /* Does anyone need to check the result of this command ?
346        */
347        if (checkResponse == I8042_CHECK_RESPONSE)
348        {
349            /* get response from device and check if
350            ** successful.
351            */
352            if (i8042_wait_input(iot,ioh,I8042_ANY_DATA))
353            {
354                c = bus_space_read_1(iot, ioh, KBDATAPO);
355                if (c == responseExpected)
356                {
357                    /* Successfull command so we're outa here
358                    */
359                    status = 1;
360                    break;
361                }
362                else if (c == KBR_RESEND)
363                {
364                    /* Hmm response was try again so lets.
365                    */
366                    KERN_DEBUG(i8042debug, KERN_DEBUG_WARNING,
367                               ("i8042_cmd: resend of 0x%x\n", value));
368                }
369                else
370                {
371                    /* response was nothing we expected so we're
372                    ** outa here.
373                    */
374                    KERN_DEBUG(i8042debug, KERN_DEBUG_WARNING,
375                         ("i8042_cmd: unexpected response 0x%x\n", c));
376                    break;
377                }
378            } /* End If able to get response from device */
379            else
380            {
381                /* Timmed out waiting for a response .... maybe we
382                ** weren't meant to get one ??
383                */
384                KERN_DEBUG(i8042debug, KERN_DEBUG_WARNING,
385                           ("i8042_cmd: no response to command 0x%x\n",
386                            value));
387                break;
388            }
389        } /* End if need to check for response */
390        else
391        {
392            /* Not requested to check for response and we did send the
393            ** command so I guess we were successful :-)
394            */
395            status = 1;
396            break;
397        }
398    } /* End loop for several retries if needed a response */
399    /* Diagnostic output on command value, result and status returned
400    */
401    KERN_DEBUG(i8042debug, KERN_DEBUG_INFO,
402               ("i8042_cmd: %s Device : Command 0x%x: %s:\n\t "
403                "Check Value 0x%x: "
404                "Response Value 0x%x: Status being returned 0x%x\n",
405                (auxCmd == I8042_AUX_CMD) ? "Auxiliary" : "Keyboard",
406                value,
407                (checkResponse == I8042_CHECK_RESPONSE) ?
408                         "Checking response" : "NOT checking response",
409                responseExpected, c, status));
410
411    return (status);
412} /* End i8042_cmd */
413
414
415