source: mainline/uspace/drv/bus/adb/cuda_adb/cuda_adb.c@ 80f7c54

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 80f7c54 was c188c62, checked in by Jiri Svoboda <jiri@…>, 8 years ago

CUDA driver should use hw_res to obtain HW configuration.

  • Property mode set to 100644
File size: 11.3 KB
Line 
1/*
2 * Copyright (c) 2010 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup genarch
30 * @{
31 */
32/** @file VIA-CUDA Apple Desktop Bus driver
33 *
34 * Note: We should really do a full bus scan at the beginning and resolve
35 * address conflicts. Also we should consider the handler ID in r3. Instead
36 * we just assume a keyboard at address 2 or 8 and a mouse at address 9.
37 */
38
39#include <assert.h>
40#include <ddf/driver.h>
41#include <ddf/log.h>
42#include <ddi.h>
43#include <errno.h>
44#include <ipc/adb.h>
45#include <libarch/ddi.h>
46#include <stdbool.h>
47#include <stddef.h>
48#include <sysinfo.h>
49#include <stdint.h>
50#include <stdio.h>
51#include <stdlib.h>
52
53#include "cuda_adb.h"
54#include "cuda_hw.h"
55
56#define NAME "cuda_adb"
57
58static void cuda_dev_connection(ipc_callid_t, ipc_call_t *, void *);
59static int cuda_init(cuda_t *);
60static void cuda_irq_handler(ipc_callid_t, ipc_call_t *, void *);
61
62static void cuda_irq_listen(cuda_t *);
63static void cuda_irq_receive(cuda_t *);
64static void cuda_irq_rcv_end(cuda_t *, void *, size_t *);
65static void cuda_irq_send_start(cuda_t *);
66static void cuda_irq_send(cuda_t *);
67
68static void cuda_packet_handle(cuda_t *, uint8_t *, size_t);
69static void cuda_send_start(cuda_t *);
70static void cuda_autopoll_set(cuda_t *, bool);
71
72static void adb_packet_handle(cuda_t *, uint8_t *, size_t, bool);
73
74static irq_pio_range_t cuda_ranges[] = {
75 {
76 .base = 0,
77 .size = sizeof(cuda_regs_t)
78 }
79};
80
81static irq_cmd_t cuda_cmds[] = {
82 {
83 .cmd = CMD_PIO_READ_8,
84 .addr = NULL,
85 .dstarg = 1
86 },
87 {
88 .cmd = CMD_AND,
89 .value = SR_INT,
90 .srcarg = 1,
91 .dstarg = 2
92 },
93 {
94 .cmd = CMD_PREDICATE,
95 .value = 1,
96 .srcarg = 2
97 },
98 {
99 .cmd = CMD_ACCEPT
100 }
101};
102
103
104static irq_code_t cuda_irq_code = {
105 sizeof(cuda_ranges) / sizeof(irq_pio_range_t),
106 cuda_ranges,
107 sizeof(cuda_cmds) / sizeof(irq_cmd_t),
108 cuda_cmds
109};
110
111static int cuda_dev_create(cuda_t *cuda, const char *name, adb_dev_t **rdev)
112{
113 adb_dev_t *dev = NULL;
114 ddf_fun_t *fun;
115 int rc;
116
117 fun = ddf_fun_create(cuda->dev, fun_exposed, name);
118 if (fun == NULL) {
119 ddf_msg(LVL_ERROR, "Failed creating function '%s'.", name);
120 rc = ENOMEM;
121 goto error;
122 }
123
124 dev = ddf_fun_data_alloc(fun, sizeof(adb_dev_t));
125 if (dev == NULL) {
126 ddf_msg(LVL_ERROR, "Failed allocating memory for '%s'.", name);
127 rc = ENOMEM;
128 goto error;
129 }
130
131 dev->fun = fun;
132 list_append(&dev->lcuda, &cuda->devs);
133
134 ddf_fun_set_conn_handler(fun, cuda_dev_connection);
135
136 rc = ddf_fun_bind(fun);
137 if (rc != EOK) {
138 ddf_msg(LVL_ERROR, "Failed binding function '%s'.", name);
139 goto error;
140 }
141
142 *rdev = dev;
143 return EOK;
144error:
145 if (fun != NULL)
146 ddf_fun_destroy(fun);
147 return rc;
148}
149
150int cuda_add(cuda_t *cuda, cuda_res_t *res)
151{
152 adb_dev_t *kbd = NULL;
153 adb_dev_t *mouse = NULL;
154 int rc;
155
156 cuda->phys_base = res->base;
157
158 rc = cuda_dev_create(cuda, "kbd", &kbd);
159 if (rc != EOK)
160 goto error;
161
162 rc = cuda_dev_create(cuda, "mouse", &mouse);
163 if (rc != EOK)
164 goto error;
165
166 cuda->addr_dev[2] = kbd;
167 cuda->addr_dev[8] = kbd;
168
169 cuda->addr_dev[9] = mouse;
170
171 rc = cuda_init(cuda);
172 if (rc != EOK) {
173 ddf_msg(LVL_ERROR, "Failed initializing CUDA hardware.");
174 return rc;
175 }
176
177 return EOK;
178error:
179 return rc;
180}
181
182int cuda_remove(cuda_t *cuda)
183{
184 return ENOTSUP;
185}
186
187int cuda_gone(cuda_t *cuda)
188{
189 return ENOTSUP;
190}
191
192/** Device connection handler */
193static void cuda_dev_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
194{
195 adb_dev_t *dev = (adb_dev_t *) ddf_fun_data_get((ddf_fun_t *) arg);
196 ipc_callid_t callid;
197 ipc_call_t call;
198 sysarg_t method;
199
200 /* Answer the IPC_M_CONNECT_ME_TO call. */
201 async_answer_0(iid, EOK);
202
203 while (true) {
204 callid = async_get_call(&call);
205 method = IPC_GET_IMETHOD(call);
206
207 if (!method) {
208 /* The other side has hung up. */
209 async_answer_0(callid, EOK);
210 return;
211 }
212
213 async_sess_t *sess =
214 async_callback_receive_start(EXCHANGE_SERIALIZE, &call);
215 if (sess != NULL) {
216 dev->client_sess = sess;
217 async_answer_0(callid, EOK);
218 } else {
219 async_answer_0(callid, EINVAL);
220 }
221 }
222}
223
224static int cuda_init(cuda_t *cuda)
225{
226 int rc;
227
228 void *vaddr;
229 rc = pio_enable((void *) cuda->phys_base, sizeof(cuda_regs_t),
230 &vaddr);
231 if (rc != EOK)
232 return rc;
233
234 cuda->regs = vaddr;
235 cuda->xstate = cx_listen;
236 cuda->bidx = 0;
237 cuda->snd_bytes = 0;
238
239 fibril_mutex_initialize(&cuda->dev_lock);
240
241 /* Disable all interrupts from CUDA. */
242 pio_write_8(&cuda->regs->ier, IER_CLR | ALL_INT);
243
244 cuda_irq_code.ranges[0].base = (uintptr_t) cuda->phys_base;
245 cuda_irq_code.cmds[0].addr = (void *) &((cuda_regs_t *)
246 cuda->phys_base)->ifr;
247 async_irq_subscribe(10, cuda_irq_handler, cuda, &cuda_irq_code);
248
249 /* Enable SR interrupt. */
250 pio_write_8(&cuda->regs->ier, TIP | TREQ);
251 pio_write_8(&cuda->regs->ier, IER_SET | SR_INT);
252
253 /* Enable ADB autopolling. */
254 cuda_autopoll_set(cuda, true);
255
256 return EOK;
257}
258
259static void cuda_irq_handler(ipc_callid_t iid, ipc_call_t *call, void *arg)
260{
261 uint8_t rbuf[CUDA_RCV_BUF_SIZE];
262 cuda_t *cuda = (cuda_t *)arg;
263 size_t len;
264 bool handle;
265
266 handle = false;
267 len = 0;
268
269 fibril_mutex_lock(&cuda->dev_lock);
270
271 switch (cuda->xstate) {
272 case cx_listen:
273 cuda_irq_listen(cuda);
274 break;
275 case cx_receive:
276 cuda_irq_receive(cuda);
277 break;
278 case cx_rcv_end:
279 cuda_irq_rcv_end(cuda, rbuf, &len);
280 handle = true;
281 break;
282 case cx_send_start:
283 cuda_irq_send_start(cuda);
284 break;
285 case cx_send:
286 cuda_irq_send(cuda);
287 break;
288 }
289
290 /* Lower IFR.SR_INT so that CUDA can generate next int by raising it. */
291 pio_write_8(&cuda->regs->ifr, SR_INT);
292
293 fibril_mutex_unlock(&cuda->dev_lock);
294
295 /* Handle an incoming packet. */
296 if (handle)
297 cuda_packet_handle(cuda, rbuf, len);
298}
299
300/** Interrupt in listen state.
301 *
302 * Start packet reception.
303 *
304 * @param cuda CUDA instance
305 */
306static void cuda_irq_listen(cuda_t *cuda)
307{
308 uint8_t b = pio_read_8(&cuda->regs->b);
309
310 if ((b & TREQ) != 0) {
311 ddf_msg(LVL_WARN, "cuda_irq_listen: no TREQ?!");
312 return;
313 }
314
315 pio_write_8(&cuda->regs->b, b & ~TIP);
316 cuda->xstate = cx_receive;
317}
318
319/** Interrupt in receive state.
320 *
321 * Receive next byte of packet.
322 *
323 * @param cuda CUDA instance
324 */
325static void cuda_irq_receive(cuda_t *cuda)
326{
327 uint8_t data = pio_read_8(&cuda->regs->sr);
328 if (cuda->bidx < CUDA_RCV_BUF_SIZE)
329 cuda->rcv_buf[cuda->bidx++] = data;
330
331 uint8_t b = pio_read_8(&cuda->regs->b);
332
333 if ((b & TREQ) == 0) {
334 pio_write_8(&cuda->regs->b, b ^ TACK);
335 } else {
336 pio_write_8(&cuda->regs->b, b | TACK | TIP);
337 cuda->xstate = cx_rcv_end;
338 }
339}
340
341/** Interrupt in rcv_end state.
342 *
343 * Terminate packet reception. Either go back to listen state or start
344 * receiving another packet if CUDA has one for us.
345 *
346 * @param cuda CUDA instance
347 * @param buf Buffer for storing received packet
348 * @param len Place to store length of received packet
349 */
350static void cuda_irq_rcv_end(cuda_t *cuda, void *buf, size_t *len)
351{
352 uint8_t b = pio_read_8(&cuda->regs->b);
353
354 if ((b & TREQ) == 0) {
355 cuda->xstate = cx_receive;
356 pio_write_8(&cuda->regs->b, b & ~TIP);
357 } else {
358 cuda->xstate = cx_listen;
359 cuda_send_start(cuda);
360 }
361
362 memcpy(buf, cuda->rcv_buf, cuda->bidx);
363 *len = cuda->bidx;
364 cuda->bidx = 0;
365}
366
367/** Interrupt in send_start state.
368 *
369 * Process result of sending first byte (and send second on success).
370 *
371 * @param cuda CUDA instance
372 */
373static void cuda_irq_send_start(cuda_t *cuda)
374{
375 uint8_t b;
376
377 b = pio_read_8(&cuda->regs->b);
378
379 if ((b & TREQ) == 0) {
380 /* Collision */
381 pio_write_8(&cuda->regs->acr, pio_read_8(&cuda->regs->acr) &
382 ~SR_OUT);
383 pio_read_8(&cuda->regs->sr);
384 pio_write_8(&cuda->regs->b, pio_read_8(&cuda->regs->b) |
385 TIP | TACK);
386 cuda->xstate = cx_listen;
387 return;
388 }
389
390 pio_write_8(&cuda->regs->sr, cuda->snd_buf[1]);
391 pio_write_8(&cuda->regs->b, pio_read_8(&cuda->regs->b) ^ TACK);
392 cuda->bidx = 2;
393
394 cuda->xstate = cx_send;
395}
396
397/** Interrupt in send state.
398 *
399 * Send next byte or terminate transmission.
400 *
401 * @param cuda CUDA instance
402 */
403static void cuda_irq_send(cuda_t *cuda)
404{
405 if (cuda->bidx < cuda->snd_bytes) {
406 /* Send next byte. */
407 pio_write_8(&cuda->regs->sr,
408 cuda->snd_buf[cuda->bidx++]);
409 pio_write_8(&cuda->regs->b, pio_read_8(&cuda->regs->b) ^ TACK);
410 return;
411 }
412
413 /* End transfer. */
414 cuda->snd_bytes = 0;
415 cuda->bidx = 0;
416
417 pio_write_8(&cuda->regs->acr, pio_read_8(&cuda->regs->acr) & ~SR_OUT);
418 pio_read_8(&cuda->regs->sr);
419 pio_write_8(&cuda->regs->b, pio_read_8(&cuda->regs->b) | TACK | TIP);
420
421 cuda->xstate = cx_listen;
422 /* TODO: Match reply with request. */
423}
424
425static void cuda_packet_handle(cuda_t *cuda, uint8_t *data, size_t len)
426{
427 if (data[0] != PT_ADB)
428 return;
429 if (len < 2)
430 return;
431
432 adb_packet_handle(cuda, data + 2, len - 2, (data[1] & 0x40) != 0);
433}
434
435static void adb_packet_handle(cuda_t *cuda, uint8_t *data, size_t size,
436 bool autopoll)
437{
438 uint8_t dev_addr;
439 uint8_t reg_no;
440 uint16_t reg_val;
441 adb_dev_t *dev;
442 unsigned i;
443
444 dev_addr = data[0] >> 4;
445 reg_no = data[0] & 0x03;
446
447 if (size != 3) {
448 ddf_msg(LVL_WARN, "Unrecognized packet, size=%zu", size);
449 for (i = 0; i < size; ++i) {
450 ddf_msg(LVL_WARN, " 0x%02x", data[i]);
451 }
452 return;
453 }
454
455 if (reg_no != 0) {
456 ddf_msg(LVL_WARN, "Unrecognized packet, size=%zu", size);
457 for (i = 0; i < size; ++i) {
458 ddf_msg(LVL_WARN, " 0x%02x", data[i]);
459 }
460 return;
461 }
462
463 reg_val = ((uint16_t) data[1] << 8) | (uint16_t) data[2];
464
465 ddf_msg(LVL_DEBUG, "Received ADB packet for device address %d",
466 dev_addr);
467 dev = cuda->addr_dev[dev_addr];
468 if (dev == NULL)
469 return;
470
471 async_exch_t *exch = async_exchange_begin(dev->client_sess);
472 async_msg_1(exch, ADB_REG_NOTIF, reg_val);
473 async_exchange_end(exch);
474}
475
476static void cuda_autopoll_set(cuda_t *cuda, bool enable)
477{
478 cuda->snd_buf[0] = PT_CUDA;
479 cuda->snd_buf[1] = CPT_AUTOPOLL;
480 cuda->snd_buf[2] = enable ? 0x01 : 0x00;
481 cuda->snd_bytes = 3;
482 cuda->bidx = 0;
483
484 cuda_send_start(cuda);
485}
486
487static void cuda_send_start(cuda_t *cuda)
488{
489 assert(cuda->xstate == cx_listen);
490
491 if (cuda->snd_bytes == 0)
492 return;
493
494 /* Check for incoming data. */
495 if ((pio_read_8(&cuda->regs->b) & TREQ) == 0)
496 return;
497
498 pio_write_8(&cuda->regs->acr, pio_read_8(&cuda->regs->acr) | SR_OUT);
499 pio_write_8(&cuda->regs->sr, cuda->snd_buf[0]);
500 pio_write_8(&cuda->regs->b, pio_read_8(&cuda->regs->b) & ~TIP);
501
502 cuda->xstate = cx_send_start;
503}
504
505/** @}
506 */
Note: See TracBrowser for help on using the repository browser.