2 * Copyright (C) 2011, 2012 glevand <geoffrey.levand@mail.ru>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification, immediately at the beginning of the file.
11 * 2. 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.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/module.h>
34 #include <sys/kernel.h>
35 #include <sys/systm.h>
36 #include <sys/malloc.h>
38 #include <sys/mutex.h>
43 #include <powerpc/ps3/ps3-hvcall.h>
45 #include "ps3vuart_port.h"
47 #define PS3VUART_PORT_LOCK_INIT(_vp) \
48 mtx_init(&(_vp)->vp_mtx, "ps3vuart_port", NULL, MTX_DEF)
49 #define PS3VUART_PORT_LOCK_DESTROY(_vp) mtx_destroy(&(_vp)->vp_mtx)
50 #define PS3VUART_PORT_LOCK(_vp) mtx_lock(&(_vp)->vp_mtx)
51 #define PS3VUART_PORT_UNLOCK(_vp) mtx_unlock(&(_vp)->vp_mtx)
52 #define PS3VUART_PORT_ASSERT_LOCKED(_vp) mtx_assert(&(_vp)->vp_mtx, MA_OWNED)
53 #define PS3VUART_PORT_ASSERT_UNLOCKED(_vp) mtx_assert(&(_vp)->vp_mtx, MA_NOTOWNED)
55 static MALLOC_DEFINE(M_PS3VUART_PORT
, "ps3vuart_port", "PS3 VUART Port");
58 ps3vuart_port_set_intr_mask(struct ps3vuart_port
*vp
, uint64_t mask
)
62 PS3VUART_PORT_ASSERT_LOCKED(vp
);
64 if (vp
->vp_intr_mask
== mask
)
67 err
= lv1_set_virtual_uart_param(vp
->vp_port
,
68 PS3VUART_PORT_PARAM_INTR_MASK
, mask
);
72 vp
->vp_intr_mask
= mask
;
78 p3vuart_port_discard_rx_bytes(struct ps3vuart_port
*vp
, int bytes
,
81 uint64_t waiting
, read
;
85 PS3VUART_PORT_ASSERT_LOCKED(vp
);
88 err
= lv1_get_virtual_uart_param(vp
->vp_port
,
89 PS3VUART_PORT_PARAM_RX_BYTES
, &waiting
);
104 buf
= malloc(bytes
, M_PS3VUART_PORT
, M_WAITOK
);
108 err
= lv1_read_virtual_uart(vp
->vp_port
, vtophys(buf
),
111 free(buf
, M_PS3VUART_PORT
);
123 ps3vuart_port_push(struct ps3vuart_port
*vp
, int *pushed
)
125 struct ps3vuart_port_buf
*vb
, *tmp
;
129 PS3VUART_PORT_ASSERT_LOCKED(vp
);
134 TAILQ_FOREACH_SAFE(vb
, &vp
->vp_tx_bufq
, vb_queue
, tmp
) {
135 bytes
= vb
->vb_tail
- vb
->vb_head
;
137 err
= lv1_write_virtual_uart(vp
->vp_port
,
138 vtophys(vb
->vb_head
), bytes
, &written
);
142 vp
->vp_tx_bufq_bytes
-= written
;
143 vp
->vp_stat
.vs_tx_bytes
+= written
;
148 if (written
< bytes
) {
149 /* VUART Tx buffer is full */
150 vb
->vb_head
+= written
;
154 TAILQ_REMOVE(&vp
->vp_tx_bufq
, vb
, vb_queue
);
155 free(vb
, M_PS3VUART_PORT
);
158 /* Tx queue is empty, disable Tx interrupts */
160 err
= ps3vuart_port_set_intr_mask(vp
,
161 vp
->vp_intr_mask
& ~PS3VUART_PORT_INTR_TX
);
167 ps3vuart_port_pull(struct ps3vuart_port
*vp
, int *pulled
)
169 struct ps3vuart_port_buf
*vb
;
170 uint64_t waiting
, read
;
173 PS3VUART_PORT_ASSERT_LOCKED(vp
);
175 err
= lv1_get_virtual_uart_param(vp
->vp_port
,
176 PS3VUART_PORT_PARAM_RX_BYTES
, &waiting
);
188 vb
= malloc(sizeof(*vb
) + waiting
, M_PS3VUART_PORT
, M_WAITOK
);
192 err
= lv1_read_virtual_uart(vp
->vp_port
,
193 vtophys(vb
->vb_data
), waiting
, &read
);
197 vb
->vb_head
= vb
->vb_data
;
198 vb
->vb_tail
= vb
->vb_head
+ read
;
200 TAILQ_INSERT_TAIL(&vp
->vp_rx_bufq
, vb
, vb_queue
);
202 vp
->vp_rx_bufq_bytes
+= read
;
211 ps3vuart_port_init(struct ps3vuart_port
*vp
, int port
)
213 uint64_t status
, bufsize
;
218 PS3VUART_PORT_LOCK_INIT(vp
);
220 PS3VUART_PORT_LOCK(vp
);
222 /* Assert that VUART is connected */
224 err
= lv1_get_virtual_uart_param(vp
->vp_port
,
225 PS3VUART_PORT_PARAM_INTR_STAT
, &status
);
229 if (!(status
& PS3VUART_PORT_INTR_CONNECT
)) {
234 /* Disable all interrupts */
236 vp
->vp_intr_mask
= 0;
238 err
= lv1_set_virtual_uart_param(vp
->vp_port
,
239 PS3VUART_PORT_PARAM_INTR_MASK
, vp
->vp_intr_mask
);
243 /* Init Tx/Rx queues */
245 TAILQ_INIT(&vp
->vp_rx_bufq
);
246 vp
->vp_rx_bufq_bytes
= 0;
248 TAILQ_INIT(&vp
->vp_tx_bufq
);
249 vp
->vp_tx_bufq_bytes
= 0;
251 /* Reset statistic counters */
253 memset(&vp
->vp_stat
, 0, sizeof(struct ps3vuart_port_stat
));
255 /* Discard stale Rx bytes */
257 err
= p3vuart_port_discard_rx_bytes(vp
, 0, NULL
);
261 /* Set Tx trigger level */
264 * An Tx interrupt is generated whenever the amount of
265 * occupied space in VUART Tx buffer becomes less than or
266 * equal the Tx trigger level.
269 err
= lv1_set_virtual_uart_param(vp
->vp_port
,
270 PS3VUART_PORT_PARAM_TX_TRIG
, 1);
274 /* Set Rx trigger level */
277 * An Rx interrupt is generated whenever the amount of
278 * free space in VUART Rx buffer becomes less than or
279 * equal the Rx trigger level.
282 err
= lv1_get_virtual_uart_param(vp
->vp_port
,
283 PS3VUART_PORT_PARAM_RX_BUFSIZE
, &bufsize
);
287 err
= lv1_set_virtual_uart_param(vp
->vp_port
,
288 PS3VUART_PORT_PARAM_RX_TRIG
, bufsize
- 1);
292 /* Enable Rx and disconnect interrupts */
294 err
= ps3vuart_port_set_intr_mask(vp
,
295 PS3VUART_PORT_INTR_RX
| PS3VUART_PORT_INTR_DISCONNECT
);
299 PS3VUART_PORT_UNLOCK(vp
);
302 PS3VUART_PORT_LOCK_DESTROY(vp
);
308 ps3vuart_port_fini(struct ps3vuart_port
*vp
)
310 struct ps3vuart_port_buf
*vb
;
312 PS3VUART_PORT_LOCK(vp
);
314 /* Disable interrupts */
316 ps3vuart_port_set_intr_mask(vp
, 0);
320 while ((vb
= TAILQ_FIRST(&vp
->vp_rx_bufq
))) {
321 TAILQ_REMOVE(&vp
->vp_rx_bufq
, vb
, vb_queue
);
322 free(vb
, M_PS3VUART_PORT
);
327 while ((vb
= TAILQ_FIRST(&vp
->vp_tx_bufq
))) {
328 TAILQ_REMOVE(&vp
->vp_tx_bufq
, vb
, vb_queue
);
329 free(vb
, M_PS3VUART_PORT
);
332 PS3VUART_PORT_UNLOCK(vp
);
334 PS3VUART_PORT_LOCK_DESTROY(vp
);
338 ps3vuart_port_write(struct ps3vuart_port
*vp
, const char *buf
, int size
,
341 struct ps3vuart_port_buf
*vb
;
345 PS3VUART_PORT_LOCK(vp
);
349 /* Try to send as many bytes as possible to VUART */
351 if (TAILQ_EMPTY(&vp
->vp_tx_bufq
)) {
352 err
= lv1_write_virtual_uart(vp
->vp_port
,
353 vtophys(buf
), size
, &bytes
);
362 vp
->vp_stat
.vs_tx_bytes
+= bytes
;
368 /* Enqueue remaining bytes in Tx queue */
370 vb
= malloc(sizeof(*vb
) + size
, M_PS3VUART_PORT
, M_WAITOK
);
376 bcopy(buf
, vb
->vb_data
, size
);
378 vb
->vb_head
= vb
->vb_data
;
379 vb
->vb_tail
= vb
->vb_head
+ size
;
381 TAILQ_INSERT_TAIL(&vp
->vp_tx_bufq
, vb
, vb_queue
);
384 vp
->vp_tx_bufq_bytes
+= size
;
386 /* Enable Tx interrupts */
388 err
= ps3vuart_port_set_intr_mask(vp
,
389 vp
->vp_intr_mask
| PS3VUART_PORT_INTR_TX
);
393 PS3VUART_PORT_UNLOCK(vp
);
399 ps3vuart_port_read(struct ps3vuart_port
*vp
, char *buf
, int size
,
402 struct ps3vuart_port_buf
*vb
, *tmp
;
406 PS3VUART_PORT_LOCK(vp
);
408 err
= ps3vuart_port_pull(vp
, NULL
);
416 TAILQ_FOREACH_SAFE(vb
, &vp
->vp_rx_bufq
, vb_queue
, tmp
) {
417 bytes
= min(vb
->vb_tail
- vb
->vb_head
, size
);
419 bcopy(vb
->vb_head
, buf
, bytes
);
424 vp
->vp_rx_bufq_bytes
-= bytes
;
426 if (bytes
< (vb
->vb_tail
- vb
->vb_head
)) {
427 vb
->vb_head
+= bytes
;
431 TAILQ_REMOVE(&vp
->vp_rx_bufq
, vb
, vb_queue
);
432 free(vb
, M_PS3VUART_PORT
);
437 PS3VUART_PORT_UNLOCK(vp
);
443 ps3vuart_port_set_param(struct ps3vuart_port
*vp
, int param
, uint64_t val
)
447 PS3VUART_PORT_LOCK(vp
);
449 if (param
== PS3VUART_PORT_PARAM_INTR_MASK
)
450 err
= ps3vuart_port_set_intr_mask(vp
, val
);
452 err
= lv1_set_virtual_uart_param(vp
->vp_port
, param
, val
);
454 PS3VUART_PORT_UNLOCK(vp
);
460 ps3vuart_port_get_param(struct ps3vuart_port
*vp
, int param
, uint64_t *val
)
464 PS3VUART_PORT_LOCK(vp
);
466 err
= lv1_get_virtual_uart_param(vp
->vp_port
, param
, val
);
471 case PS3VUART_PORT_PARAM_RX_BYTES
:
472 *val
+= vp
->vp_rx_bufq_bytes
;
474 case PS3VUART_PORT_PARAM_INTR_STAT
:
475 *val
&= vp
->vp_intr_mask
;
481 PS3VUART_PORT_UNLOCK(vp
);
487 ps3vuart_port_intr(struct ps3vuart_port
*vp
)
492 PS3VUART_PORT_LOCK(vp
);
494 err
= lv1_get_virtual_uart_param(vp
->vp_port
,
495 PS3VUART_PORT_PARAM_INTR_STAT
, &status
);
499 KASSERT(status
& PS3VUART_PORT_INTR_CONNECT
,
500 ("port %d disconnected", vp
->vp_port
));
501 KASSERT(!(status
& PS3VUART_PORT_INTR_DISCONNECT
),
502 ("port %d disconnected", vp
->vp_port
));
504 status
&= vp
->vp_intr_mask
;
506 if (status
& PS3VUART_PORT_INTR_RX
) {
507 vp
->vp_stat
.vs_rx_intrs
++;
508 ps3vuart_port_pull(vp
, NULL
);
511 if (status
& PS3VUART_PORT_INTR_TX
) {
512 vp
->vp_stat
.vs_tx_intrs
++;
513 ps3vuart_port_push(vp
, NULL
);
518 PS3VUART_PORT_UNLOCK(vp
);
522 ps3vuart_port_get_stat(struct ps3vuart_port
*vp
,
523 struct ps3vuart_port_stat
*stat
)
525 PS3VUART_PORT_LOCK(vp
);
529 PS3VUART_PORT_UNLOCK(vp
);
533 ps3vuart_port_modevent(module_t mod
, int type
, void *data
)
548 static moduledata_t ps3vuart_port_mod
= {
550 ps3vuart_port_modevent
,
554 DECLARE_MODULE(ps3vuart_port
, ps3vuart_port_mod
, SI_SUB_PSEUDO
, SI_ORDER_ANY
);
555 MODULE_VERSION(ps3vuart_port
, 1);