root / hw / char / cadence_uart.c @ a8aec295
History | View | Annotate | Download (13.2 kB)
1 | 35548b06 | Peter A. G. Crosthwaite | /*
|
---|---|---|---|
2 | 35548b06 | Peter A. G. Crosthwaite | * Device model for Cadence UART
|
3 | 35548b06 | Peter A. G. Crosthwaite | *
|
4 | 35548b06 | Peter A. G. Crosthwaite | * Copyright (c) 2010 Xilinx Inc.
|
5 | 35548b06 | Peter A. G. Crosthwaite | * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
|
6 | 35548b06 | Peter A. G. Crosthwaite | * Copyright (c) 2012 PetaLogix Pty Ltd.
|
7 | 35548b06 | Peter A. G. Crosthwaite | * Written by Haibing Ma
|
8 | 35548b06 | Peter A. G. Crosthwaite | * M.Habib
|
9 | 35548b06 | Peter A. G. Crosthwaite | *
|
10 | 35548b06 | Peter A. G. Crosthwaite | * This program is free software; you can redistribute it and/or
|
11 | 35548b06 | Peter A. G. Crosthwaite | * modify it under the terms of the GNU General Public License
|
12 | 35548b06 | Peter A. G. Crosthwaite | * as published by the Free Software Foundation; either version
|
13 | 35548b06 | Peter A. G. Crosthwaite | * 2 of the License, or (at your option) any later version.
|
14 | 35548b06 | Peter A. G. Crosthwaite | *
|
15 | 35548b06 | Peter A. G. Crosthwaite | * You should have received a copy of the GNU General Public License along
|
16 | 35548b06 | Peter A. G. Crosthwaite | * with this program; if not, see <http://www.gnu.org/licenses/>.
|
17 | 35548b06 | Peter A. G. Crosthwaite | */
|
18 | 35548b06 | Peter A. G. Crosthwaite | |
19 | 83c9f4ca | Paolo Bonzini | #include "hw/sysbus.h" |
20 | dccfcd0e | Paolo Bonzini | #include "sysemu/char.h" |
21 | 1de7afc9 | Paolo Bonzini | #include "qemu/timer.h" |
22 | 35548b06 | Peter A. G. Crosthwaite | |
23 | 35548b06 | Peter A. G. Crosthwaite | #ifdef CADENCE_UART_ERR_DEBUG
|
24 | 35548b06 | Peter A. G. Crosthwaite | #define DB_PRINT(...) do { \ |
25 | 35548b06 | Peter A. G. Crosthwaite | fprintf(stderr, ": %s: ", __func__); \
|
26 | 35548b06 | Peter A. G. Crosthwaite | fprintf(stderr, ## __VA_ARGS__); \ |
27 | 35548b06 | Peter A. G. Crosthwaite | } while (0); |
28 | 35548b06 | Peter A. G. Crosthwaite | #else
|
29 | 35548b06 | Peter A. G. Crosthwaite | #define DB_PRINT(...)
|
30 | 35548b06 | Peter A. G. Crosthwaite | #endif
|
31 | 35548b06 | Peter A. G. Crosthwaite | |
32 | 35548b06 | Peter A. G. Crosthwaite | #define UART_SR_INTR_RTRIG 0x00000001 |
33 | 35548b06 | Peter A. G. Crosthwaite | #define UART_SR_INTR_REMPTY 0x00000002 |
34 | 35548b06 | Peter A. G. Crosthwaite | #define UART_SR_INTR_RFUL 0x00000004 |
35 | 35548b06 | Peter A. G. Crosthwaite | #define UART_SR_INTR_TEMPTY 0x00000008 |
36 | 35548b06 | Peter A. G. Crosthwaite | #define UART_SR_INTR_TFUL 0x00000010 |
37 | 35548b06 | Peter A. G. Crosthwaite | /* bits fields in CSR that correlate to CISR. If any of these bits are set in
|
38 | 35548b06 | Peter A. G. Crosthwaite | * SR, then the same bit in CISR is set high too */
|
39 | 35548b06 | Peter A. G. Crosthwaite | #define UART_SR_TO_CISR_MASK 0x0000001F |
40 | 35548b06 | Peter A. G. Crosthwaite | |
41 | 35548b06 | Peter A. G. Crosthwaite | #define UART_INTR_ROVR 0x00000020 |
42 | 35548b06 | Peter A. G. Crosthwaite | #define UART_INTR_FRAME 0x00000040 |
43 | 35548b06 | Peter A. G. Crosthwaite | #define UART_INTR_PARE 0x00000080 |
44 | 35548b06 | Peter A. G. Crosthwaite | #define UART_INTR_TIMEOUT 0x00000100 |
45 | 35548b06 | Peter A. G. Crosthwaite | #define UART_INTR_DMSI 0x00000200 |
46 | 35548b06 | Peter A. G. Crosthwaite | |
47 | 35548b06 | Peter A. G. Crosthwaite | #define UART_SR_RACTIVE 0x00000400 |
48 | 35548b06 | Peter A. G. Crosthwaite | #define UART_SR_TACTIVE 0x00000800 |
49 | 35548b06 | Peter A. G. Crosthwaite | #define UART_SR_FDELT 0x00001000 |
50 | 35548b06 | Peter A. G. Crosthwaite | |
51 | 35548b06 | Peter A. G. Crosthwaite | #define UART_CR_RXRST 0x00000001 |
52 | 35548b06 | Peter A. G. Crosthwaite | #define UART_CR_TXRST 0x00000002 |
53 | 35548b06 | Peter A. G. Crosthwaite | #define UART_CR_RX_EN 0x00000004 |
54 | 35548b06 | Peter A. G. Crosthwaite | #define UART_CR_RX_DIS 0x00000008 |
55 | 35548b06 | Peter A. G. Crosthwaite | #define UART_CR_TX_EN 0x00000010 |
56 | 35548b06 | Peter A. G. Crosthwaite | #define UART_CR_TX_DIS 0x00000020 |
57 | 35548b06 | Peter A. G. Crosthwaite | #define UART_CR_RST_TO 0x00000040 |
58 | 35548b06 | Peter A. G. Crosthwaite | #define UART_CR_STARTBRK 0x00000080 |
59 | 35548b06 | Peter A. G. Crosthwaite | #define UART_CR_STOPBRK 0x00000100 |
60 | 35548b06 | Peter A. G. Crosthwaite | |
61 | 35548b06 | Peter A. G. Crosthwaite | #define UART_MR_CLKS 0x00000001 |
62 | 35548b06 | Peter A. G. Crosthwaite | #define UART_MR_CHRL 0x00000006 |
63 | 35548b06 | Peter A. G. Crosthwaite | #define UART_MR_CHRL_SH 1 |
64 | 35548b06 | Peter A. G. Crosthwaite | #define UART_MR_PAR 0x00000038 |
65 | 35548b06 | Peter A. G. Crosthwaite | #define UART_MR_PAR_SH 3 |
66 | 35548b06 | Peter A. G. Crosthwaite | #define UART_MR_NBSTOP 0x000000C0 |
67 | 35548b06 | Peter A. G. Crosthwaite | #define UART_MR_NBSTOP_SH 6 |
68 | 35548b06 | Peter A. G. Crosthwaite | #define UART_MR_CHMODE 0x00000300 |
69 | 35548b06 | Peter A. G. Crosthwaite | #define UART_MR_CHMODE_SH 8 |
70 | 35548b06 | Peter A. G. Crosthwaite | #define UART_MR_UCLKEN 0x00000400 |
71 | 35548b06 | Peter A. G. Crosthwaite | #define UART_MR_IRMODE 0x00000800 |
72 | 35548b06 | Peter A. G. Crosthwaite | |
73 | 35548b06 | Peter A. G. Crosthwaite | #define UART_DATA_BITS_6 (0x3 << UART_MR_CHRL_SH) |
74 | 35548b06 | Peter A. G. Crosthwaite | #define UART_DATA_BITS_7 (0x2 << UART_MR_CHRL_SH) |
75 | 35548b06 | Peter A. G. Crosthwaite | #define UART_PARITY_ODD (0x1 << UART_MR_PAR_SH) |
76 | 35548b06 | Peter A. G. Crosthwaite | #define UART_PARITY_EVEN (0x0 << UART_MR_PAR_SH) |
77 | 35548b06 | Peter A. G. Crosthwaite | #define UART_STOP_BITS_1 (0x3 << UART_MR_NBSTOP_SH) |
78 | 35548b06 | Peter A. G. Crosthwaite | #define UART_STOP_BITS_2 (0x2 << UART_MR_NBSTOP_SH) |
79 | 35548b06 | Peter A. G. Crosthwaite | #define NORMAL_MODE (0x0 << UART_MR_CHMODE_SH) |
80 | 35548b06 | Peter A. G. Crosthwaite | #define ECHO_MODE (0x1 << UART_MR_CHMODE_SH) |
81 | 35548b06 | Peter A. G. Crosthwaite | #define LOCAL_LOOPBACK (0x2 << UART_MR_CHMODE_SH) |
82 | 35548b06 | Peter A. G. Crosthwaite | #define REMOTE_LOOPBACK (0x3 << UART_MR_CHMODE_SH) |
83 | 35548b06 | Peter A. G. Crosthwaite | |
84 | 35548b06 | Peter A. G. Crosthwaite | #define RX_FIFO_SIZE 16 |
85 | 35548b06 | Peter A. G. Crosthwaite | #define TX_FIFO_SIZE 16 |
86 | 35548b06 | Peter A. G. Crosthwaite | #define UART_INPUT_CLK 50000000 |
87 | 35548b06 | Peter A. G. Crosthwaite | |
88 | 35548b06 | Peter A. G. Crosthwaite | #define R_CR (0x00/4) |
89 | 35548b06 | Peter A. G. Crosthwaite | #define R_MR (0x04/4) |
90 | 35548b06 | Peter A. G. Crosthwaite | #define R_IER (0x08/4) |
91 | 35548b06 | Peter A. G. Crosthwaite | #define R_IDR (0x0C/4) |
92 | 35548b06 | Peter A. G. Crosthwaite | #define R_IMR (0x10/4) |
93 | 35548b06 | Peter A. G. Crosthwaite | #define R_CISR (0x14/4) |
94 | 35548b06 | Peter A. G. Crosthwaite | #define R_BRGR (0x18/4) |
95 | 35548b06 | Peter A. G. Crosthwaite | #define R_RTOR (0x1C/4) |
96 | 35548b06 | Peter A. G. Crosthwaite | #define R_RTRIG (0x20/4) |
97 | 35548b06 | Peter A. G. Crosthwaite | #define R_MCR (0x24/4) |
98 | 35548b06 | Peter A. G. Crosthwaite | #define R_MSR (0x28/4) |
99 | 35548b06 | Peter A. G. Crosthwaite | #define R_SR (0x2C/4) |
100 | 35548b06 | Peter A. G. Crosthwaite | #define R_TX_RX (0x30/4) |
101 | 35548b06 | Peter A. G. Crosthwaite | #define R_BDIV (0x34/4) |
102 | 35548b06 | Peter A. G. Crosthwaite | #define R_FDEL (0x38/4) |
103 | 35548b06 | Peter A. G. Crosthwaite | #define R_PMIN (0x3C/4) |
104 | 35548b06 | Peter A. G. Crosthwaite | #define R_PWID (0x40/4) |
105 | 35548b06 | Peter A. G. Crosthwaite | #define R_TTRIG (0x44/4) |
106 | 35548b06 | Peter A. G. Crosthwaite | |
107 | 35548b06 | Peter A. G. Crosthwaite | #define R_MAX (R_TTRIG + 1) |
108 | 35548b06 | Peter A. G. Crosthwaite | |
109 | 35548b06 | Peter A. G. Crosthwaite | typedef struct { |
110 | 35548b06 | Peter A. G. Crosthwaite | SysBusDevice busdev; |
111 | 35548b06 | Peter A. G. Crosthwaite | MemoryRegion iomem; |
112 | 35548b06 | Peter A. G. Crosthwaite | uint32_t r[R_MAX]; |
113 | 35548b06 | Peter A. G. Crosthwaite | uint8_t r_fifo[RX_FIFO_SIZE]; |
114 | 35548b06 | Peter A. G. Crosthwaite | uint32_t rx_wpos; |
115 | 35548b06 | Peter A. G. Crosthwaite | uint32_t rx_count; |
116 | 35548b06 | Peter A. G. Crosthwaite | uint64_t char_tx_time; |
117 | 35548b06 | Peter A. G. Crosthwaite | CharDriverState *chr; |
118 | 35548b06 | Peter A. G. Crosthwaite | qemu_irq irq; |
119 | 35548b06 | Peter A. G. Crosthwaite | struct QEMUTimer *fifo_trigger_handle;
|
120 | 35548b06 | Peter A. G. Crosthwaite | struct QEMUTimer *tx_time_handle;
|
121 | 35548b06 | Peter A. G. Crosthwaite | } UartState; |
122 | 35548b06 | Peter A. G. Crosthwaite | |
123 | 35548b06 | Peter A. G. Crosthwaite | static void uart_update_status(UartState *s) |
124 | 35548b06 | Peter A. G. Crosthwaite | { |
125 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_CISR] |= s->r[R_SR] & UART_SR_TO_CISR_MASK; |
126 | 35548b06 | Peter A. G. Crosthwaite | qemu_set_irq(s->irq, !!(s->r[R_IMR] & s->r[R_CISR])); |
127 | 35548b06 | Peter A. G. Crosthwaite | } |
128 | 35548b06 | Peter A. G. Crosthwaite | |
129 | 35548b06 | Peter A. G. Crosthwaite | static void fifo_trigger_update(void *opaque) |
130 | 35548b06 | Peter A. G. Crosthwaite | { |
131 | 35548b06 | Peter A. G. Crosthwaite | UartState *s = (UartState *)opaque; |
132 | 35548b06 | Peter A. G. Crosthwaite | |
133 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_CISR] |= UART_INTR_TIMEOUT; |
134 | 35548b06 | Peter A. G. Crosthwaite | |
135 | 35548b06 | Peter A. G. Crosthwaite | uart_update_status(s); |
136 | 35548b06 | Peter A. G. Crosthwaite | } |
137 | 35548b06 | Peter A. G. Crosthwaite | |
138 | 35548b06 | Peter A. G. Crosthwaite | static void uart_tx_redo(UartState *s) |
139 | 35548b06 | Peter A. G. Crosthwaite | { |
140 | 35548b06 | Peter A. G. Crosthwaite | uint64_t new_tx_time = qemu_get_clock_ns(vm_clock); |
141 | 35548b06 | Peter A. G. Crosthwaite | |
142 | 35548b06 | Peter A. G. Crosthwaite | qemu_mod_timer(s->tx_time_handle, new_tx_time + s->char_tx_time); |
143 | 35548b06 | Peter A. G. Crosthwaite | |
144 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_SR] |= UART_SR_INTR_TEMPTY; |
145 | 35548b06 | Peter A. G. Crosthwaite | |
146 | 35548b06 | Peter A. G. Crosthwaite | uart_update_status(s); |
147 | 35548b06 | Peter A. G. Crosthwaite | } |
148 | 35548b06 | Peter A. G. Crosthwaite | |
149 | 35548b06 | Peter A. G. Crosthwaite | static void uart_tx_write(void *opaque) |
150 | 35548b06 | Peter A. G. Crosthwaite | { |
151 | 35548b06 | Peter A. G. Crosthwaite | UartState *s = (UartState *)opaque; |
152 | 35548b06 | Peter A. G. Crosthwaite | |
153 | 35548b06 | Peter A. G. Crosthwaite | uart_tx_redo(s); |
154 | 35548b06 | Peter A. G. Crosthwaite | } |
155 | 35548b06 | Peter A. G. Crosthwaite | |
156 | 35548b06 | Peter A. G. Crosthwaite | static void uart_rx_reset(UartState *s) |
157 | 35548b06 | Peter A. G. Crosthwaite | { |
158 | 35548b06 | Peter A. G. Crosthwaite | s->rx_wpos = 0;
|
159 | 35548b06 | Peter A. G. Crosthwaite | s->rx_count = 0;
|
160 | 1db8b5ef | Peter Crosthwaite | qemu_chr_accept_input(s->chr); |
161 | 35548b06 | Peter A. G. Crosthwaite | |
162 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_SR] |= UART_SR_INTR_REMPTY; |
163 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_SR] &= ~UART_SR_INTR_RFUL; |
164 | 35548b06 | Peter A. G. Crosthwaite | } |
165 | 35548b06 | Peter A. G. Crosthwaite | |
166 | 35548b06 | Peter A. G. Crosthwaite | static void uart_tx_reset(UartState *s) |
167 | 35548b06 | Peter A. G. Crosthwaite | { |
168 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_SR] |= UART_SR_INTR_TEMPTY; |
169 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_SR] &= ~UART_SR_INTR_TFUL; |
170 | 35548b06 | Peter A. G. Crosthwaite | } |
171 | 35548b06 | Peter A. G. Crosthwaite | |
172 | 35548b06 | Peter A. G. Crosthwaite | static void uart_send_breaks(UartState *s) |
173 | 35548b06 | Peter A. G. Crosthwaite | { |
174 | 35548b06 | Peter A. G. Crosthwaite | int break_enabled = 1; |
175 | 35548b06 | Peter A. G. Crosthwaite | |
176 | 35548b06 | Peter A. G. Crosthwaite | qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK, |
177 | 35548b06 | Peter A. G. Crosthwaite | &break_enabled); |
178 | 35548b06 | Peter A. G. Crosthwaite | } |
179 | 35548b06 | Peter A. G. Crosthwaite | |
180 | 35548b06 | Peter A. G. Crosthwaite | static void uart_parameters_setup(UartState *s) |
181 | 35548b06 | Peter A. G. Crosthwaite | { |
182 | 35548b06 | Peter A. G. Crosthwaite | QEMUSerialSetParams ssp; |
183 | 35548b06 | Peter A. G. Crosthwaite | unsigned int baud_rate, packet_size; |
184 | 35548b06 | Peter A. G. Crosthwaite | |
185 | 35548b06 | Peter A. G. Crosthwaite | baud_rate = (s->r[R_MR] & UART_MR_CLKS) ? |
186 | 35548b06 | Peter A. G. Crosthwaite | UART_INPUT_CLK / 8 : UART_INPUT_CLK;
|
187 | 35548b06 | Peter A. G. Crosthwaite | |
188 | 35548b06 | Peter A. G. Crosthwaite | ssp.speed = baud_rate / (s->r[R_BRGR] * (s->r[R_BDIV] + 1));
|
189 | 35548b06 | Peter A. G. Crosthwaite | packet_size = 1;
|
190 | 35548b06 | Peter A. G. Crosthwaite | |
191 | 35548b06 | Peter A. G. Crosthwaite | switch (s->r[R_MR] & UART_MR_PAR) {
|
192 | 35548b06 | Peter A. G. Crosthwaite | case UART_PARITY_EVEN:
|
193 | 35548b06 | Peter A. G. Crosthwaite | ssp.parity = 'E';
|
194 | 35548b06 | Peter A. G. Crosthwaite | packet_size++; |
195 | 35548b06 | Peter A. G. Crosthwaite | break;
|
196 | 35548b06 | Peter A. G. Crosthwaite | case UART_PARITY_ODD:
|
197 | 35548b06 | Peter A. G. Crosthwaite | ssp.parity = 'O';
|
198 | 35548b06 | Peter A. G. Crosthwaite | packet_size++; |
199 | 35548b06 | Peter A. G. Crosthwaite | break;
|
200 | 35548b06 | Peter A. G. Crosthwaite | default:
|
201 | 35548b06 | Peter A. G. Crosthwaite | ssp.parity = 'N';
|
202 | 35548b06 | Peter A. G. Crosthwaite | break;
|
203 | 35548b06 | Peter A. G. Crosthwaite | } |
204 | 35548b06 | Peter A. G. Crosthwaite | |
205 | 35548b06 | Peter A. G. Crosthwaite | switch (s->r[R_MR] & UART_MR_CHRL) {
|
206 | 35548b06 | Peter A. G. Crosthwaite | case UART_DATA_BITS_6:
|
207 | 35548b06 | Peter A. G. Crosthwaite | ssp.data_bits = 6;
|
208 | 35548b06 | Peter A. G. Crosthwaite | break;
|
209 | 35548b06 | Peter A. G. Crosthwaite | case UART_DATA_BITS_7:
|
210 | 35548b06 | Peter A. G. Crosthwaite | ssp.data_bits = 7;
|
211 | 35548b06 | Peter A. G. Crosthwaite | break;
|
212 | 35548b06 | Peter A. G. Crosthwaite | default:
|
213 | 35548b06 | Peter A. G. Crosthwaite | ssp.data_bits = 8;
|
214 | 35548b06 | Peter A. G. Crosthwaite | break;
|
215 | 35548b06 | Peter A. G. Crosthwaite | } |
216 | 35548b06 | Peter A. G. Crosthwaite | |
217 | 35548b06 | Peter A. G. Crosthwaite | switch (s->r[R_MR] & UART_MR_NBSTOP) {
|
218 | 35548b06 | Peter A. G. Crosthwaite | case UART_STOP_BITS_1:
|
219 | 35548b06 | Peter A. G. Crosthwaite | ssp.stop_bits = 1;
|
220 | 35548b06 | Peter A. G. Crosthwaite | break;
|
221 | 35548b06 | Peter A. G. Crosthwaite | default:
|
222 | 35548b06 | Peter A. G. Crosthwaite | ssp.stop_bits = 2;
|
223 | 35548b06 | Peter A. G. Crosthwaite | break;
|
224 | 35548b06 | Peter A. G. Crosthwaite | } |
225 | 35548b06 | Peter A. G. Crosthwaite | |
226 | 35548b06 | Peter A. G. Crosthwaite | packet_size += ssp.data_bits + ssp.stop_bits; |
227 | 35548b06 | Peter A. G. Crosthwaite | s->char_tx_time = (get_ticks_per_sec() / ssp.speed) * packet_size; |
228 | 35548b06 | Peter A. G. Crosthwaite | qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); |
229 | 35548b06 | Peter A. G. Crosthwaite | } |
230 | 35548b06 | Peter A. G. Crosthwaite | |
231 | 35548b06 | Peter A. G. Crosthwaite | static int uart_can_receive(void *opaque) |
232 | 35548b06 | Peter A. G. Crosthwaite | { |
233 | 35548b06 | Peter A. G. Crosthwaite | UartState *s = (UartState *)opaque; |
234 | 35548b06 | Peter A. G. Crosthwaite | |
235 | 35548b06 | Peter A. G. Crosthwaite | return RX_FIFO_SIZE - s->rx_count;
|
236 | 35548b06 | Peter A. G. Crosthwaite | } |
237 | 35548b06 | Peter A. G. Crosthwaite | |
238 | 35548b06 | Peter A. G. Crosthwaite | static void uart_ctrl_update(UartState *s) |
239 | 35548b06 | Peter A. G. Crosthwaite | { |
240 | 35548b06 | Peter A. G. Crosthwaite | if (s->r[R_CR] & UART_CR_TXRST) {
|
241 | 35548b06 | Peter A. G. Crosthwaite | uart_tx_reset(s); |
242 | 35548b06 | Peter A. G. Crosthwaite | } |
243 | 35548b06 | Peter A. G. Crosthwaite | |
244 | 35548b06 | Peter A. G. Crosthwaite | if (s->r[R_CR] & UART_CR_RXRST) {
|
245 | 35548b06 | Peter A. G. Crosthwaite | uart_rx_reset(s); |
246 | 35548b06 | Peter A. G. Crosthwaite | } |
247 | 35548b06 | Peter A. G. Crosthwaite | |
248 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_CR] &= ~(UART_CR_TXRST | UART_CR_RXRST); |
249 | 35548b06 | Peter A. G. Crosthwaite | |
250 | 35548b06 | Peter A. G. Crosthwaite | if ((s->r[R_CR] & UART_CR_TX_EN) && !(s->r[R_CR] & UART_CR_TX_DIS)) {
|
251 | 35548b06 | Peter A. G. Crosthwaite | uart_tx_redo(s); |
252 | 35548b06 | Peter A. G. Crosthwaite | } |
253 | 35548b06 | Peter A. G. Crosthwaite | |
254 | 35548b06 | Peter A. G. Crosthwaite | if (s->r[R_CR] & UART_CR_STARTBRK && !(s->r[R_CR] & UART_CR_STOPBRK)) {
|
255 | 35548b06 | Peter A. G. Crosthwaite | uart_send_breaks(s); |
256 | 35548b06 | Peter A. G. Crosthwaite | } |
257 | 35548b06 | Peter A. G. Crosthwaite | } |
258 | 35548b06 | Peter A. G. Crosthwaite | |
259 | 35548b06 | Peter A. G. Crosthwaite | static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size) |
260 | 35548b06 | Peter A. G. Crosthwaite | { |
261 | 35548b06 | Peter A. G. Crosthwaite | UartState *s = (UartState *)opaque; |
262 | 35548b06 | Peter A. G. Crosthwaite | uint64_t new_rx_time = qemu_get_clock_ns(vm_clock); |
263 | 35548b06 | Peter A. G. Crosthwaite | int i;
|
264 | 35548b06 | Peter A. G. Crosthwaite | |
265 | 35548b06 | Peter A. G. Crosthwaite | if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) {
|
266 | 35548b06 | Peter A. G. Crosthwaite | return;
|
267 | 35548b06 | Peter A. G. Crosthwaite | } |
268 | 35548b06 | Peter A. G. Crosthwaite | |
269 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_SR] &= ~UART_SR_INTR_REMPTY; |
270 | 35548b06 | Peter A. G. Crosthwaite | |
271 | 35548b06 | Peter A. G. Crosthwaite | if (s->rx_count == RX_FIFO_SIZE) {
|
272 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_CISR] |= UART_INTR_ROVR; |
273 | 35548b06 | Peter A. G. Crosthwaite | } else {
|
274 | 35548b06 | Peter A. G. Crosthwaite | for (i = 0; i < size; i++) { |
275 | 35548b06 | Peter A. G. Crosthwaite | s->r_fifo[s->rx_wpos] = buf[i]; |
276 | 35548b06 | Peter A. G. Crosthwaite | s->rx_wpos = (s->rx_wpos + 1) % RX_FIFO_SIZE;
|
277 | 35548b06 | Peter A. G. Crosthwaite | s->rx_count++; |
278 | 35548b06 | Peter A. G. Crosthwaite | |
279 | 35548b06 | Peter A. G. Crosthwaite | if (s->rx_count == RX_FIFO_SIZE) {
|
280 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_SR] |= UART_SR_INTR_RFUL; |
281 | 35548b06 | Peter A. G. Crosthwaite | break;
|
282 | 35548b06 | Peter A. G. Crosthwaite | } |
283 | 35548b06 | Peter A. G. Crosthwaite | |
284 | 35548b06 | Peter A. G. Crosthwaite | if (s->rx_count >= s->r[R_RTRIG]) {
|
285 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_SR] |= UART_SR_INTR_RTRIG; |
286 | 35548b06 | Peter A. G. Crosthwaite | } |
287 | 35548b06 | Peter A. G. Crosthwaite | } |
288 | 35548b06 | Peter A. G. Crosthwaite | qemu_mod_timer(s->fifo_trigger_handle, new_rx_time + |
289 | 35548b06 | Peter A. G. Crosthwaite | (s->char_tx_time * 4));
|
290 | 35548b06 | Peter A. G. Crosthwaite | } |
291 | 35548b06 | Peter A. G. Crosthwaite | uart_update_status(s); |
292 | 35548b06 | Peter A. G. Crosthwaite | } |
293 | 35548b06 | Peter A. G. Crosthwaite | |
294 | 35548b06 | Peter A. G. Crosthwaite | static void uart_write_tx_fifo(UartState *s, const uint8_t *buf, int size) |
295 | 35548b06 | Peter A. G. Crosthwaite | { |
296 | 35548b06 | Peter A. G. Crosthwaite | if ((s->r[R_CR] & UART_CR_TX_DIS) || !(s->r[R_CR] & UART_CR_TX_EN)) {
|
297 | 35548b06 | Peter A. G. Crosthwaite | return;
|
298 | 35548b06 | Peter A. G. Crosthwaite | } |
299 | 35548b06 | Peter A. G. Crosthwaite | |
300 | b52df465 | Edgar E. Iglesias | qemu_chr_fe_write_all(s->chr, buf, size); |
301 | 35548b06 | Peter A. G. Crosthwaite | } |
302 | 35548b06 | Peter A. G. Crosthwaite | |
303 | 35548b06 | Peter A. G. Crosthwaite | static void uart_receive(void *opaque, const uint8_t *buf, int size) |
304 | 35548b06 | Peter A. G. Crosthwaite | { |
305 | 35548b06 | Peter A. G. Crosthwaite | UartState *s = (UartState *)opaque; |
306 | 35548b06 | Peter A. G. Crosthwaite | uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE; |
307 | 35548b06 | Peter A. G. Crosthwaite | |
308 | 35548b06 | Peter A. G. Crosthwaite | if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
|
309 | 35548b06 | Peter A. G. Crosthwaite | uart_write_rx_fifo(opaque, buf, size); |
310 | 35548b06 | Peter A. G. Crosthwaite | } |
311 | 35548b06 | Peter A. G. Crosthwaite | if (ch_mode == REMOTE_LOOPBACK || ch_mode == ECHO_MODE) {
|
312 | 35548b06 | Peter A. G. Crosthwaite | uart_write_tx_fifo(s, buf, size); |
313 | 35548b06 | Peter A. G. Crosthwaite | } |
314 | 35548b06 | Peter A. G. Crosthwaite | } |
315 | 35548b06 | Peter A. G. Crosthwaite | |
316 | 35548b06 | Peter A. G. Crosthwaite | static void uart_event(void *opaque, int event) |
317 | 35548b06 | Peter A. G. Crosthwaite | { |
318 | 35548b06 | Peter A. G. Crosthwaite | UartState *s = (UartState *)opaque; |
319 | 35548b06 | Peter A. G. Crosthwaite | uint8_t buf = '\0';
|
320 | 35548b06 | Peter A. G. Crosthwaite | |
321 | 35548b06 | Peter A. G. Crosthwaite | if (event == CHR_EVENT_BREAK) {
|
322 | 35548b06 | Peter A. G. Crosthwaite | uart_write_rx_fifo(opaque, &buf, 1);
|
323 | 35548b06 | Peter A. G. Crosthwaite | } |
324 | 35548b06 | Peter A. G. Crosthwaite | |
325 | 35548b06 | Peter A. G. Crosthwaite | uart_update_status(s); |
326 | 35548b06 | Peter A. G. Crosthwaite | } |
327 | 35548b06 | Peter A. G. Crosthwaite | |
328 | 35548b06 | Peter A. G. Crosthwaite | static void uart_read_rx_fifo(UartState *s, uint32_t *c) |
329 | 35548b06 | Peter A. G. Crosthwaite | { |
330 | 35548b06 | Peter A. G. Crosthwaite | if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) {
|
331 | 35548b06 | Peter A. G. Crosthwaite | return;
|
332 | 35548b06 | Peter A. G. Crosthwaite | } |
333 | 35548b06 | Peter A. G. Crosthwaite | |
334 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_SR] &= ~UART_SR_INTR_RFUL; |
335 | 35548b06 | Peter A. G. Crosthwaite | |
336 | 35548b06 | Peter A. G. Crosthwaite | if (s->rx_count) {
|
337 | 35548b06 | Peter A. G. Crosthwaite | uint32_t rx_rpos = |
338 | 35548b06 | Peter A. G. Crosthwaite | (RX_FIFO_SIZE + s->rx_wpos - s->rx_count) % RX_FIFO_SIZE; |
339 | 35548b06 | Peter A. G. Crosthwaite | *c = s->r_fifo[rx_rpos]; |
340 | 35548b06 | Peter A. G. Crosthwaite | s->rx_count--; |
341 | 35548b06 | Peter A. G. Crosthwaite | |
342 | 35548b06 | Peter A. G. Crosthwaite | if (!s->rx_count) {
|
343 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_SR] |= UART_SR_INTR_REMPTY; |
344 | 35548b06 | Peter A. G. Crosthwaite | } |
345 | 9893c80d | Peter Crosthwaite | qemu_chr_accept_input(s->chr); |
346 | 35548b06 | Peter A. G. Crosthwaite | } else {
|
347 | 35548b06 | Peter A. G. Crosthwaite | *c = 0;
|
348 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_SR] |= UART_SR_INTR_REMPTY; |
349 | 35548b06 | Peter A. G. Crosthwaite | } |
350 | 35548b06 | Peter A. G. Crosthwaite | |
351 | 35548b06 | Peter A. G. Crosthwaite | if (s->rx_count < s->r[R_RTRIG]) {
|
352 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_SR] &= ~UART_SR_INTR_RTRIG; |
353 | 35548b06 | Peter A. G. Crosthwaite | } |
354 | 35548b06 | Peter A. G. Crosthwaite | uart_update_status(s); |
355 | 35548b06 | Peter A. G. Crosthwaite | } |
356 | 35548b06 | Peter A. G. Crosthwaite | |
357 | a8170e5e | Avi Kivity | static void uart_write(void *opaque, hwaddr offset, |
358 | 35548b06 | Peter A. G. Crosthwaite | uint64_t value, unsigned size)
|
359 | 35548b06 | Peter A. G. Crosthwaite | { |
360 | 35548b06 | Peter A. G. Crosthwaite | UartState *s = (UartState *)opaque; |
361 | 35548b06 | Peter A. G. Crosthwaite | |
362 | 2ddef11b | Peter Crosthwaite | DB_PRINT(" offset:%x data:%08x\n", (unsigned)offset, (unsigned)value); |
363 | 35548b06 | Peter A. G. Crosthwaite | offset >>= 2;
|
364 | 35548b06 | Peter A. G. Crosthwaite | switch (offset) {
|
365 | 35548b06 | Peter A. G. Crosthwaite | case R_IER: /* ier (wts imr) */ |
366 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_IMR] |= value; |
367 | 35548b06 | Peter A. G. Crosthwaite | break;
|
368 | 35548b06 | Peter A. G. Crosthwaite | case R_IDR: /* idr (wtc imr) */ |
369 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_IMR] &= ~value; |
370 | 35548b06 | Peter A. G. Crosthwaite | break;
|
371 | 35548b06 | Peter A. G. Crosthwaite | case R_IMR: /* imr (read only) */ |
372 | 35548b06 | Peter A. G. Crosthwaite | break;
|
373 | 35548b06 | Peter A. G. Crosthwaite | case R_CISR: /* cisr (wtc) */ |
374 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_CISR] &= ~value; |
375 | 35548b06 | Peter A. G. Crosthwaite | break;
|
376 | 35548b06 | Peter A. G. Crosthwaite | case R_TX_RX: /* UARTDR */ |
377 | 35548b06 | Peter A. G. Crosthwaite | switch (s->r[R_MR] & UART_MR_CHMODE) {
|
378 | 35548b06 | Peter A. G. Crosthwaite | case NORMAL_MODE:
|
379 | 35548b06 | Peter A. G. Crosthwaite | uart_write_tx_fifo(s, (uint8_t *) &value, 1);
|
380 | 35548b06 | Peter A. G. Crosthwaite | break;
|
381 | 35548b06 | Peter A. G. Crosthwaite | case LOCAL_LOOPBACK:
|
382 | 35548b06 | Peter A. G. Crosthwaite | uart_write_rx_fifo(opaque, (uint8_t *) &value, 1);
|
383 | 35548b06 | Peter A. G. Crosthwaite | break;
|
384 | 35548b06 | Peter A. G. Crosthwaite | } |
385 | 35548b06 | Peter A. G. Crosthwaite | break;
|
386 | 35548b06 | Peter A. G. Crosthwaite | default:
|
387 | 35548b06 | Peter A. G. Crosthwaite | s->r[offset] = value; |
388 | 35548b06 | Peter A. G. Crosthwaite | } |
389 | 35548b06 | Peter A. G. Crosthwaite | |
390 | 35548b06 | Peter A. G. Crosthwaite | switch (offset) {
|
391 | 35548b06 | Peter A. G. Crosthwaite | case R_CR:
|
392 | 35548b06 | Peter A. G. Crosthwaite | uart_ctrl_update(s); |
393 | 35548b06 | Peter A. G. Crosthwaite | break;
|
394 | 35548b06 | Peter A. G. Crosthwaite | case R_MR:
|
395 | 35548b06 | Peter A. G. Crosthwaite | uart_parameters_setup(s); |
396 | 35548b06 | Peter A. G. Crosthwaite | break;
|
397 | 35548b06 | Peter A. G. Crosthwaite | } |
398 | 35548b06 | Peter A. G. Crosthwaite | } |
399 | 35548b06 | Peter A. G. Crosthwaite | |
400 | a8170e5e | Avi Kivity | static uint64_t uart_read(void *opaque, hwaddr offset, |
401 | 35548b06 | Peter A. G. Crosthwaite | unsigned size)
|
402 | 35548b06 | Peter A. G. Crosthwaite | { |
403 | 35548b06 | Peter A. G. Crosthwaite | UartState *s = (UartState *)opaque; |
404 | 35548b06 | Peter A. G. Crosthwaite | uint32_t c = 0;
|
405 | 35548b06 | Peter A. G. Crosthwaite | |
406 | 35548b06 | Peter A. G. Crosthwaite | offset >>= 2;
|
407 | 5d40097f | Stefan Weil | if (offset >= R_MAX) {
|
408 | 2ddef11b | Peter Crosthwaite | c = 0;
|
409 | 35548b06 | Peter A. G. Crosthwaite | } else if (offset == R_TX_RX) { |
410 | 35548b06 | Peter A. G. Crosthwaite | uart_read_rx_fifo(s, &c); |
411 | 2ddef11b | Peter Crosthwaite | } else {
|
412 | 2ddef11b | Peter Crosthwaite | c = s->r[offset]; |
413 | 35548b06 | Peter A. G. Crosthwaite | } |
414 | 2ddef11b | Peter Crosthwaite | |
415 | 2ddef11b | Peter Crosthwaite | DB_PRINT(" offset:%x data:%08x\n", (unsigned)(offset << 2), (unsigned)c); |
416 | 2ddef11b | Peter Crosthwaite | return c;
|
417 | 35548b06 | Peter A. G. Crosthwaite | } |
418 | 35548b06 | Peter A. G. Crosthwaite | |
419 | 35548b06 | Peter A. G. Crosthwaite | static const MemoryRegionOps uart_ops = { |
420 | 35548b06 | Peter A. G. Crosthwaite | .read = uart_read, |
421 | 35548b06 | Peter A. G. Crosthwaite | .write = uart_write, |
422 | 35548b06 | Peter A. G. Crosthwaite | .endianness = DEVICE_NATIVE_ENDIAN, |
423 | 35548b06 | Peter A. G. Crosthwaite | }; |
424 | 35548b06 | Peter A. G. Crosthwaite | |
425 | 35548b06 | Peter A. G. Crosthwaite | static void cadence_uart_reset(UartState *s) |
426 | 35548b06 | Peter A. G. Crosthwaite | { |
427 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_CR] = 0x00000128;
|
428 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_IMR] = 0;
|
429 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_CISR] = 0;
|
430 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_RTRIG] = 0x00000020;
|
431 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_BRGR] = 0x0000000F;
|
432 | 35548b06 | Peter A. G. Crosthwaite | s->r[R_TTRIG] = 0x00000020;
|
433 | 35548b06 | Peter A. G. Crosthwaite | |
434 | 35548b06 | Peter A. G. Crosthwaite | uart_rx_reset(s); |
435 | 35548b06 | Peter A. G. Crosthwaite | uart_tx_reset(s); |
436 | 35548b06 | Peter A. G. Crosthwaite | |
437 | 35548b06 | Peter A. G. Crosthwaite | s->rx_count = 0;
|
438 | 35548b06 | Peter A. G. Crosthwaite | s->rx_wpos = 0;
|
439 | 35548b06 | Peter A. G. Crosthwaite | } |
440 | 35548b06 | Peter A. G. Crosthwaite | |
441 | 35548b06 | Peter A. G. Crosthwaite | static int cadence_uart_init(SysBusDevice *dev) |
442 | 35548b06 | Peter A. G. Crosthwaite | { |
443 | 35548b06 | Peter A. G. Crosthwaite | UartState *s = FROM_SYSBUS(UartState, dev); |
444 | 35548b06 | Peter A. G. Crosthwaite | |
445 | 35548b06 | Peter A. G. Crosthwaite | memory_region_init_io(&s->iomem, &uart_ops, s, "uart", 0x1000); |
446 | 35548b06 | Peter A. G. Crosthwaite | sysbus_init_mmio(dev, &s->iomem); |
447 | 35548b06 | Peter A. G. Crosthwaite | sysbus_init_irq(dev, &s->irq); |
448 | 35548b06 | Peter A. G. Crosthwaite | |
449 | 35548b06 | Peter A. G. Crosthwaite | s->fifo_trigger_handle = qemu_new_timer_ns(vm_clock, |
450 | 35548b06 | Peter A. G. Crosthwaite | (QEMUTimerCB *)fifo_trigger_update, s); |
451 | 35548b06 | Peter A. G. Crosthwaite | |
452 | 35548b06 | Peter A. G. Crosthwaite | s->tx_time_handle = qemu_new_timer_ns(vm_clock, |
453 | 35548b06 | Peter A. G. Crosthwaite | (QEMUTimerCB *)uart_tx_write, s); |
454 | 35548b06 | Peter A. G. Crosthwaite | |
455 | 35548b06 | Peter A. G. Crosthwaite | s->char_tx_time = (get_ticks_per_sec() / 9600) * 10; |
456 | 35548b06 | Peter A. G. Crosthwaite | |
457 | 35548b06 | Peter A. G. Crosthwaite | s->chr = qemu_char_get_next_serial(); |
458 | 35548b06 | Peter A. G. Crosthwaite | |
459 | 35548b06 | Peter A. G. Crosthwaite | cadence_uart_reset(s); |
460 | 35548b06 | Peter A. G. Crosthwaite | |
461 | 35548b06 | Peter A. G. Crosthwaite | if (s->chr) {
|
462 | 35548b06 | Peter A. G. Crosthwaite | qemu_chr_add_handlers(s->chr, uart_can_receive, uart_receive, |
463 | 35548b06 | Peter A. G. Crosthwaite | uart_event, s); |
464 | 35548b06 | Peter A. G. Crosthwaite | } |
465 | 35548b06 | Peter A. G. Crosthwaite | |
466 | 35548b06 | Peter A. G. Crosthwaite | return 0; |
467 | 35548b06 | Peter A. G. Crosthwaite | } |
468 | 35548b06 | Peter A. G. Crosthwaite | |
469 | 35548b06 | Peter A. G. Crosthwaite | static int cadence_uart_post_load(void *opaque, int version_id) |
470 | 35548b06 | Peter A. G. Crosthwaite | { |
471 | 35548b06 | Peter A. G. Crosthwaite | UartState *s = opaque; |
472 | 35548b06 | Peter A. G. Crosthwaite | |
473 | 35548b06 | Peter A. G. Crosthwaite | uart_parameters_setup(s); |
474 | 35548b06 | Peter A. G. Crosthwaite | uart_update_status(s); |
475 | 35548b06 | Peter A. G. Crosthwaite | return 0; |
476 | 35548b06 | Peter A. G. Crosthwaite | } |
477 | 35548b06 | Peter A. G. Crosthwaite | |
478 | 35548b06 | Peter A. G. Crosthwaite | static const VMStateDescription vmstate_cadence_uart = { |
479 | 35548b06 | Peter A. G. Crosthwaite | .name = "cadence_uart",
|
480 | 35548b06 | Peter A. G. Crosthwaite | .version_id = 1,
|
481 | 35548b06 | Peter A. G. Crosthwaite | .minimum_version_id = 1,
|
482 | 35548b06 | Peter A. G. Crosthwaite | .minimum_version_id_old = 1,
|
483 | 35548b06 | Peter A. G. Crosthwaite | .post_load = cadence_uart_post_load, |
484 | 35548b06 | Peter A. G. Crosthwaite | .fields = (VMStateField[]) { |
485 | 35548b06 | Peter A. G. Crosthwaite | VMSTATE_UINT32_ARRAY(r, UartState, R_MAX), |
486 | 35548b06 | Peter A. G. Crosthwaite | VMSTATE_UINT8_ARRAY(r_fifo, UartState, RX_FIFO_SIZE), |
487 | 35548b06 | Peter A. G. Crosthwaite | VMSTATE_UINT32(rx_count, UartState), |
488 | 35548b06 | Peter A. G. Crosthwaite | VMSTATE_UINT32(rx_wpos, UartState), |
489 | 35548b06 | Peter A. G. Crosthwaite | VMSTATE_TIMER(fifo_trigger_handle, UartState), |
490 | 35548b06 | Peter A. G. Crosthwaite | VMSTATE_TIMER(tx_time_handle, UartState), |
491 | 35548b06 | Peter A. G. Crosthwaite | VMSTATE_END_OF_LIST() |
492 | 35548b06 | Peter A. G. Crosthwaite | } |
493 | 35548b06 | Peter A. G. Crosthwaite | }; |
494 | 35548b06 | Peter A. G. Crosthwaite | |
495 | 35548b06 | Peter A. G. Crosthwaite | static void cadence_uart_class_init(ObjectClass *klass, void *data) |
496 | 35548b06 | Peter A. G. Crosthwaite | { |
497 | 35548b06 | Peter A. G. Crosthwaite | DeviceClass *dc = DEVICE_CLASS(klass); |
498 | 35548b06 | Peter A. G. Crosthwaite | SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); |
499 | 35548b06 | Peter A. G. Crosthwaite | |
500 | 35548b06 | Peter A. G. Crosthwaite | sdc->init = cadence_uart_init; |
501 | 35548b06 | Peter A. G. Crosthwaite | dc->vmsd = &vmstate_cadence_uart; |
502 | 35548b06 | Peter A. G. Crosthwaite | } |
503 | 35548b06 | Peter A. G. Crosthwaite | |
504 | 8c43a6f0 | Andreas Färber | static const TypeInfo cadence_uart_info = { |
505 | 35548b06 | Peter A. G. Crosthwaite | .name = "cadence_uart",
|
506 | 35548b06 | Peter A. G. Crosthwaite | .parent = TYPE_SYS_BUS_DEVICE, |
507 | 35548b06 | Peter A. G. Crosthwaite | .instance_size = sizeof(UartState),
|
508 | 35548b06 | Peter A. G. Crosthwaite | .class_init = cadence_uart_class_init, |
509 | 35548b06 | Peter A. G. Crosthwaite | }; |
510 | 35548b06 | Peter A. G. Crosthwaite | |
511 | 35548b06 | Peter A. G. Crosthwaite | static void cadence_uart_register_types(void) |
512 | 35548b06 | Peter A. G. Crosthwaite | { |
513 | 35548b06 | Peter A. G. Crosthwaite | type_register_static(&cadence_uart_info); |
514 | 35548b06 | Peter A. G. Crosthwaite | } |
515 | 35548b06 | Peter A. G. Crosthwaite | |
516 | 35548b06 | Peter A. G. Crosthwaite | type_init(cadence_uart_register_types) |