~vojtech-horky/helenos/numa

« back to all changes in this revision

Viewing changes to kernel/genarch/src/drivers/via-cuda/cuda.c

  • Committer: Martin Decky
  • Date: 2009-08-04 11:19:19 UTC
  • Revision ID: martin@uranus.dsrg.hide.ms.mff.cuni.cz-20090804111919-evyclddlr3v5lhmp
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2006 Martin Decky
 
3
 * Copyright (c) 2009 Jiri Svoboda
 
4
 * All rights reserved.
 
5
 *
 
6
 * Redistribution and use in source and binary forms, with or without
 
7
 * modification, are permitted provided that the following conditions
 
8
 * are met:
 
9
 *
 
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.
 
17
 *
 
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.
 
28
 */
 
29
 
 
30
/** @addtogroup genarch
 
31
 * @{
 
32
 */
 
33
/** @file
 
34
 */
 
35
 
 
36
#include <genarch/drivers/via-cuda/cuda.h>
 
37
#include <console/chardev.h>
 
38
#include <ddi/irq.h>
 
39
#include <arch/asm.h>
 
40
#include <mm/slab.h>
 
41
#include <ddi/device.h>
 
42
#include <synch/spinlock.h>
 
43
 
 
44
static irq_ownership_t cuda_claim(irq_t *irq);
 
45
static void cuda_irq_handler(irq_t *irq);
 
46
 
 
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);
 
52
 
 
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);
 
56
 
 
57
/** B register fields */
 
58
enum {
 
59
        TREQ    = 0x08,
 
60
        TACK    = 0x10,
 
61
        TIP     = 0x20
 
62
};
 
63
 
 
64
/** IER register fields */
 
65
enum {
 
66
        IER_CLR = 0x00,
 
67
        IER_SET = 0x80,
 
68
 
 
69
        SR_INT  = 0x04,
 
70
        ALL_INT = 0x7f
 
71
};
 
72
 
 
73
/** ACR register fields */
 
74
enum {
 
75
        SR_OUT  = 0x10
 
76
};
 
77
 
 
78
/** Packet types */
 
79
enum {
 
80
        PT_ADB  = 0x00,
 
81
        PT_CUDA = 0x01
 
82
};
 
83
 
 
84
/** CUDA packet types */
 
85
enum {
 
86
        CPT_AUTOPOLL    = 0x01
 
87
};
 
88
 
 
89
cuda_instance_t *cuda_init(cuda_t *dev, inr_t inr, cir_t cir, void *cir_arg)
 
90
{
 
91
        cuda_instance_t *instance
 
92
            = malloc(sizeof(cuda_instance_t), FRAME_ATOMIC);
 
93
        if (instance) {
 
94
                instance->cuda = dev;
 
95
                instance->kbrdin = NULL;
 
96
                instance->xstate = cx_listen;
 
97
                instance->bidx = 0;
 
98
                instance->snd_bytes = 0;
 
99
 
 
100
                spinlock_initialize(&instance->dev_lock, "cuda_dev");
 
101
 
 
102
                /* Disable all interrupts from CUDA. */
 
103
                pio_write_8(&dev->ier, IER_CLR | ALL_INT);
 
104
 
 
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;
 
114
        }
 
115
        
 
116
        return instance;
 
117
}
 
118
 
 
119
#include <print.h>
 
120
void cuda_wire(cuda_instance_t *instance, indev_t *kbrdin)
 
121
{
 
122
        cuda_t *dev = instance->cuda;
 
123
 
 
124
        ASSERT(instance);
 
125
        ASSERT(kbrdin);
 
126
 
 
127
        instance->kbrdin = kbrdin;
 
128
        irq_register(&instance->irq);
 
129
 
 
130
        /* Enable SR interrupt. */
 
131
        pio_write_8(&dev->ier, TIP | TREQ);
 
132
        pio_write_8(&dev->ier, IER_SET | SR_INT);
 
133
 
 
134
        /* Enable ADB autopolling. */
 
135
        cuda_autopoll_set(instance, true);
 
136
}
 
137
 
 
138
static irq_ownership_t cuda_claim(irq_t *irq)
 
139
{
 
140
        cuda_instance_t *instance = irq->instance;
 
141
        cuda_t *dev = instance->cuda;
 
142
        uint8_t ifr;
 
143
 
 
144
        spinlock_lock(&instance->dev_lock);
 
145
        ifr = pio_read_8(&dev->ifr);
 
146
        spinlock_unlock(&instance->dev_lock);
 
147
 
 
148
        if ((ifr & SR_INT) == 0)
 
149
                return IRQ_DECLINE;
 
150
 
 
151
        return IRQ_ACCEPT;
 
152
}
 
153
 
 
154
static void cuda_irq_handler(irq_t *irq)
 
155
{
 
156
        cuda_instance_t *instance = irq->instance;
 
157
        uint8_t rbuf[CUDA_RCV_BUF_SIZE];
 
158
        size_t len;
 
159
        bool handle;
 
160
 
 
161
        handle = false;
 
162
        len = 0;
 
163
 
 
164
        spinlock_lock(&instance->dev_lock);
 
165
 
 
166
        /* Lower IFR.SR_INT so that CUDA can generate next int by raising it. */
 
167
        pio_write_8(&instance->cuda->ifr, SR_INT);
 
168
 
 
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;
 
176
        }
 
177
 
 
178
        spinlock_unlock(&instance->dev_lock);
 
179
 
 
180
        /* Handle an incoming packet. */
 
181
        if (handle)
 
182
                cuda_packet_handle(instance, rbuf, len);
 
183
}
 
184
 
 
185
/** Interrupt in listen state.
 
186
 *
 
187
 * Start packet reception.
 
188
 */
 
189
static void cuda_irq_listen(irq_t *irq)
 
190
{
 
191
        cuda_instance_t *instance = irq->instance;
 
192
        cuda_t *dev = instance->cuda;
 
193
        uint8_t b;
 
194
 
 
195
        b = pio_read_8(&dev->b);
 
196
 
 
197
        if ((b & TREQ) != 0) {
 
198
                printf("cuda_irq_listen: no TREQ?!\n");
 
199
                return;
 
200
        }
 
201
 
 
202
        pio_read_8(&dev->sr);
 
203
        pio_write_8(&dev->b, pio_read_8(&dev->b) & ~TIP);
 
204
        instance->xstate = cx_receive;
 
205
}
 
206
 
 
207
/** Interrupt in receive state.
 
208
 *
 
209
 * Receive next byte of packet.
 
210
 */
 
211
static void cuda_irq_receive(irq_t *irq)
 
212
{
 
213
        cuda_instance_t *instance = irq->instance;
 
214
        cuda_t *dev = instance->cuda;
 
215
        uint8_t b, data;
 
216
 
 
217
        data = pio_read_8(&dev->sr);
 
218
        if (instance->bidx < CUDA_RCV_BUF_SIZE)
 
219
                instance->rcv_buf[instance->bidx++] = data;
 
220
 
 
221
        b = pio_read_8(&dev->b);
 
222
 
 
223
        if ((b & TREQ) == 0) {
 
224
                pio_write_8(&dev->b, b ^ TACK);
 
225
        } else {
 
226
                pio_write_8(&dev->b, b | TACK | TIP);
 
227
                instance->xstate = cx_rcv_end;
 
228
        }
 
229
}
 
230
 
 
231
/** Interrupt in rcv_end state.
 
232
 *
 
233
 * Terminate packet reception. Either go back to listen state or start
 
234
 * receiving another packet if CUDA has one for us.
 
235
 */
 
236
static void cuda_irq_rcv_end(irq_t *irq, void *buf, size_t *len)
 
237
{
 
238
        cuda_instance_t *instance = irq->instance;
 
239
        cuda_t *dev = instance->cuda;
 
240
        uint8_t data, b;
 
241
 
 
242
        b = pio_read_8(&dev->b);
 
243
        data = pio_read_8(&dev->sr);
 
244
 
 
245
        if ((b & TREQ) == 0) {
 
246
                instance->xstate = cx_receive;
 
247
                pio_write_8(&dev->b, b & ~TIP);
 
248
        } else {
 
249
                instance->xstate = cx_listen;
 
250
                cuda_send_start(instance);
 
251
        }
 
252
 
 
253
        memcpy(buf, instance->rcv_buf, instance->bidx);
 
254
        *len = instance->bidx;
 
255
        instance->bidx = 0;
 
256
}
 
257
 
 
258
/** Interrupt in send_start state.
 
259
 *
 
260
 * Process result of sending first byte (and send second on success).
 
261
 */
 
262
static void cuda_irq_send_start(irq_t *irq)
 
263
{
 
264
        cuda_instance_t *instance = irq->instance;
 
265
        cuda_t *dev = instance->cuda;
 
266
        uint8_t b;
 
267
 
 
268
        b = pio_read_8(&dev->b);
 
269
 
 
270
        if ((b & TREQ) == 0) {
 
271
                /* Collision */
 
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;
 
276
                return;
 
277
        }
 
278
 
 
279
        pio_write_8(&dev->sr, instance->snd_buf[1]);
 
280
        pio_write_8(&dev->b, pio_read_8(&dev->b) ^ TACK);
 
281
        instance->bidx = 2;
 
282
 
 
283
        instance->xstate = cx_send;
 
284
}
 
285
 
 
286
/** Interrupt in send state.
 
287
 *
 
288
 * Send next byte or terminate transmission.
 
289
 */
 
290
static void cuda_irq_send(irq_t *irq)
 
291
{
 
292
        cuda_instance_t *instance = irq->instance;
 
293
        cuda_t *dev = instance->cuda;
 
294
 
 
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);
 
299
                return;
 
300
        }
 
301
 
 
302
        /* End transfer. */
 
303
        instance->snd_bytes = 0;
 
304
        instance->bidx = 0;
 
305
 
 
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);
 
309
 
 
310
        instance->xstate = cx_listen;
 
311
        /* TODO: Match reply with request. */
 
312
}
 
313
 
 
314
static void cuda_packet_handle(cuda_instance_t *instance, uint8_t *data, size_t len)
 
315
{
 
316
        if (data[0] != 0x00 || data[1] != 0x40 || (data[2] != 0x2c
 
317
                && data[2] != 0x8c))
 
318
                return;
 
319
 
 
320
        /* The packet contains one or two scancodes. */
 
321
        if (data[3] != 0xff)
 
322
                indev_push_character(instance->kbrdin, data[3]);                
 
323
        if (data[4] != 0xff)
 
324
                indev_push_character(instance->kbrdin, data[4]);
 
325
}
 
326
 
 
327
static void cuda_autopoll_set(cuda_instance_t *instance, bool enable)
 
328
{
 
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;
 
333
        instance->bidx = 0;
 
334
 
 
335
        cuda_send_start(instance);
 
336
}
 
337
 
 
338
static void cuda_send_start(cuda_instance_t *instance)
 
339
{
 
340
        cuda_t *dev = instance->cuda;
 
341
 
 
342
        ASSERT(instance->xstate == cx_listen);
 
343
 
 
344
        if (instance->snd_bytes == 0)
 
345
                return;
 
346
 
 
347
        /* Check for incoming data. */
 
348
        if ((pio_read_8(&dev->b) & TREQ) == 0)
 
349
                return;
 
350
 
 
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);
 
354
 
 
355
        instance->xstate = cx_send_start;
 
356
}
 
357
 
 
358
 
 
359
/** @}
 
360
 */