2
* Copyright (c) 2006 Martin Decky
3
* Copyright (c) 2009 Jiri Svoboda
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
10
* - Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* - Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
* - The name of the author may not be used to endorse or promote products
16
* derived from this software without specific prior written permission.
18
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
/** @addtogroup genarch
36
#include <genarch/drivers/via-cuda/cuda.h>
37
#include <console/chardev.h>
41
#include <ddi/device.h>
42
#include <synch/spinlock.h>
44
static irq_ownership_t cuda_claim(irq_t *irq);
45
static void cuda_irq_handler(irq_t *irq);
47
static void cuda_irq_listen(irq_t *irq);
48
static void cuda_irq_receive(irq_t *irq);
49
static void cuda_irq_rcv_end(irq_t *irq, void *buf, size_t *len);
50
static void cuda_irq_send_start(irq_t *irq);
51
static void cuda_irq_send(irq_t *irq);
53
static void cuda_packet_handle(cuda_instance_t *instance, uint8_t *buf, size_t len);
54
static void cuda_send_start(cuda_instance_t *instance);
55
static void cuda_autopoll_set(cuda_instance_t *instance, bool enable);
57
/** B register fields */
64
/** IER register fields */
73
/** ACR register fields */
84
/** CUDA packet types */
89
cuda_instance_t *cuda_init(cuda_t *dev, inr_t inr, cir_t cir, void *cir_arg)
91
cuda_instance_t *instance
92
= malloc(sizeof(cuda_instance_t), FRAME_ATOMIC);
95
instance->kbrdin = NULL;
96
instance->xstate = cx_listen;
98
instance->snd_bytes = 0;
100
spinlock_initialize(&instance->dev_lock, "cuda_dev");
102
/* Disable all interrupts from CUDA. */
103
pio_write_8(&dev->ier, IER_CLR | ALL_INT);
105
irq_initialize(&instance->irq);
106
instance->irq.devno = device_assign_devno();
107
instance->irq.inr = inr;
108
instance->irq.claim = cuda_claim;
109
instance->irq.handler = cuda_irq_handler;
110
instance->irq.instance = instance;
111
instance->irq.cir = cir;
112
instance->irq.cir_arg = cir_arg;
113
instance->irq.preack = true;
120
void cuda_wire(cuda_instance_t *instance, indev_t *kbrdin)
122
cuda_t *dev = instance->cuda;
127
instance->kbrdin = kbrdin;
128
irq_register(&instance->irq);
130
/* Enable SR interrupt. */
131
pio_write_8(&dev->ier, TIP | TREQ);
132
pio_write_8(&dev->ier, IER_SET | SR_INT);
134
/* Enable ADB autopolling. */
135
cuda_autopoll_set(instance, true);
138
static irq_ownership_t cuda_claim(irq_t *irq)
140
cuda_instance_t *instance = irq->instance;
141
cuda_t *dev = instance->cuda;
144
spinlock_lock(&instance->dev_lock);
145
ifr = pio_read_8(&dev->ifr);
146
spinlock_unlock(&instance->dev_lock);
148
if ((ifr & SR_INT) == 0)
154
static void cuda_irq_handler(irq_t *irq)
156
cuda_instance_t *instance = irq->instance;
157
uint8_t rbuf[CUDA_RCV_BUF_SIZE];
164
spinlock_lock(&instance->dev_lock);
166
/* Lower IFR.SR_INT so that CUDA can generate next int by raising it. */
167
pio_write_8(&instance->cuda->ifr, SR_INT);
169
switch (instance->xstate) {
170
case cx_listen: cuda_irq_listen(irq); break;
171
case cx_receive: cuda_irq_receive(irq); break;
172
case cx_rcv_end: cuda_irq_rcv_end(irq, rbuf, &len);
173
handle = true; break;
174
case cx_send_start: cuda_irq_send_start(irq); break;
175
case cx_send: cuda_irq_send(irq); break;
178
spinlock_unlock(&instance->dev_lock);
180
/* Handle an incoming packet. */
182
cuda_packet_handle(instance, rbuf, len);
185
/** Interrupt in listen state.
187
* Start packet reception.
189
static void cuda_irq_listen(irq_t *irq)
191
cuda_instance_t *instance = irq->instance;
192
cuda_t *dev = instance->cuda;
195
b = pio_read_8(&dev->b);
197
if ((b & TREQ) != 0) {
198
printf("cuda_irq_listen: no TREQ?!\n");
202
pio_read_8(&dev->sr);
203
pio_write_8(&dev->b, pio_read_8(&dev->b) & ~TIP);
204
instance->xstate = cx_receive;
207
/** Interrupt in receive state.
209
* Receive next byte of packet.
211
static void cuda_irq_receive(irq_t *irq)
213
cuda_instance_t *instance = irq->instance;
214
cuda_t *dev = instance->cuda;
217
data = pio_read_8(&dev->sr);
218
if (instance->bidx < CUDA_RCV_BUF_SIZE)
219
instance->rcv_buf[instance->bidx++] = data;
221
b = pio_read_8(&dev->b);
223
if ((b & TREQ) == 0) {
224
pio_write_8(&dev->b, b ^ TACK);
226
pio_write_8(&dev->b, b | TACK | TIP);
227
instance->xstate = cx_rcv_end;
231
/** Interrupt in rcv_end state.
233
* Terminate packet reception. Either go back to listen state or start
234
* receiving another packet if CUDA has one for us.
236
static void cuda_irq_rcv_end(irq_t *irq, void *buf, size_t *len)
238
cuda_instance_t *instance = irq->instance;
239
cuda_t *dev = instance->cuda;
242
b = pio_read_8(&dev->b);
243
data = pio_read_8(&dev->sr);
245
if ((b & TREQ) == 0) {
246
instance->xstate = cx_receive;
247
pio_write_8(&dev->b, b & ~TIP);
249
instance->xstate = cx_listen;
250
cuda_send_start(instance);
253
memcpy(buf, instance->rcv_buf, instance->bidx);
254
*len = instance->bidx;
258
/** Interrupt in send_start state.
260
* Process result of sending first byte (and send second on success).
262
static void cuda_irq_send_start(irq_t *irq)
264
cuda_instance_t *instance = irq->instance;
265
cuda_t *dev = instance->cuda;
268
b = pio_read_8(&dev->b);
270
if ((b & TREQ) == 0) {
272
pio_write_8(&dev->acr, pio_read_8(&dev->acr) & ~SR_OUT);
273
pio_read_8(&dev->sr);
274
pio_write_8(&dev->b, pio_read_8(&dev->b) | TIP | TACK);
275
instance->xstate = cx_listen;
279
pio_write_8(&dev->sr, instance->snd_buf[1]);
280
pio_write_8(&dev->b, pio_read_8(&dev->b) ^ TACK);
283
instance->xstate = cx_send;
286
/** Interrupt in send state.
288
* Send next byte or terminate transmission.
290
static void cuda_irq_send(irq_t *irq)
292
cuda_instance_t *instance = irq->instance;
293
cuda_t *dev = instance->cuda;
295
if (instance->bidx < instance->snd_bytes) {
296
/* Send next byte. */
297
pio_write_8(&dev->sr, instance->snd_buf[instance->bidx++]);
298
pio_write_8(&dev->b, pio_read_8(&dev->b) ^ TACK);
303
instance->snd_bytes = 0;
306
pio_write_8(&dev->acr, pio_read_8(&dev->acr) & ~SR_OUT);
307
pio_read_8(&dev->sr);
308
pio_write_8(&dev->b, pio_read_8(&dev->b) | TACK | TIP);
310
instance->xstate = cx_listen;
311
/* TODO: Match reply with request. */
314
static void cuda_packet_handle(cuda_instance_t *instance, uint8_t *data, size_t len)
316
if (data[0] != 0x00 || data[1] != 0x40 || (data[2] != 0x2c
320
/* The packet contains one or two scancodes. */
322
indev_push_character(instance->kbrdin, data[3]);
324
indev_push_character(instance->kbrdin, data[4]);
327
static void cuda_autopoll_set(cuda_instance_t *instance, bool enable)
329
instance->snd_buf[0] = PT_CUDA;
330
instance->snd_buf[1] = CPT_AUTOPOLL;
331
instance->snd_buf[2] = enable ? 0x01 : 0x00;
332
instance->snd_bytes = 3;
335
cuda_send_start(instance);
338
static void cuda_send_start(cuda_instance_t *instance)
340
cuda_t *dev = instance->cuda;
342
ASSERT(instance->xstate == cx_listen);
344
if (instance->snd_bytes == 0)
347
/* Check for incoming data. */
348
if ((pio_read_8(&dev->b) & TREQ) == 0)
351
pio_write_8(&dev->acr, pio_read_8(&dev->acr) | SR_OUT);
352
pio_write_8(&dev->sr, instance->snd_buf[0]);
353
pio_write_8(&dev->b, pio_read_8(&dev->b) & ~TIP);
355
instance->xstate = cx_send_start;