diff options
Diffstat (limited to 'firmware/target/arm/s5l8702/ipod6g/serial-6g.c')
-rw-r--r-- | firmware/target/arm/s5l8702/ipod6g/serial-6g.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/firmware/target/arm/s5l8702/ipod6g/serial-6g.c b/firmware/target/arm/s5l8702/ipod6g/serial-6g.c new file mode 100644 index 0000000000..f1f06987bc --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/serial-6g.c | |||
@@ -0,0 +1,249 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Cástor Muñoz | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdint.h> | ||
22 | #include <stdbool.h> | ||
23 | |||
24 | #include "config.h" | ||
25 | #include "cpu.h" | ||
26 | #include "system.h" | ||
27 | #include "serial.h" | ||
28 | |||
29 | #include "s5l8702.h" | ||
30 | #include "uc870x.h" | ||
31 | |||
32 | /* Define LOGF_ENABLE to enable logf output in this file */ | ||
33 | #define LOGF_ENABLE | ||
34 | #include "logf.h" | ||
35 | |||
36 | |||
37 | /* shall include serial HW configuracion for specific target */ | ||
38 | #define IPOD6G_UART_CLK_HZ 12000000 /* external OSC0 ??? */ | ||
39 | |||
40 | /* This values below are valid with a UCLK of 12MHz */ | ||
41 | #define BRDATA_9600 (77) /* 9615 */ | ||
42 | #define BRDATA_19200 (38) /* 19231 */ | ||
43 | #define BRDATA_28800 (25) /* 28846 */ | ||
44 | #define BRDATA_38400 (19 | (0xc330c << 8)) /* 38305 */ | ||
45 | #define BRDATA_57600 (12) /* 57692 */ | ||
46 | #define BRDATA_115200 (6 | (0xffffff << 8)) /* 114286 */ | ||
47 | |||
48 | |||
49 | extern const struct uartc s5l8702_uartc; | ||
50 | #ifdef IPOD_ACCESSORY_PROTOCOL | ||
51 | static void iap_rx_isr(int, char*, char*, uint32_t); | ||
52 | #endif | ||
53 | |||
54 | struct uartc_port ser_port IDATA_ATTR = | ||
55 | { | ||
56 | /* location */ | ||
57 | .uartc = &s5l8702_uartc, | ||
58 | .id = 0, | ||
59 | |||
60 | /* configuration */ | ||
61 | .rx_trg = UFCON_RX_FIFO_TRG_4, | ||
62 | .tx_trg = UFCON_TX_FIFO_TRG_EMPTY, | ||
63 | .clksel = UCON_CLKSEL_ECLK, | ||
64 | .clkhz = IPOD6G_UART_CLK_HZ, | ||
65 | |||
66 | /* interrupt callbacks */ | ||
67 | #ifdef IPOD_ACCESSORY_PROTOCOL | ||
68 | .rx_cb = iap_rx_isr, | ||
69 | #else | ||
70 | .rx_cb = NULL, | ||
71 | #endif | ||
72 | .tx_cb = NULL, /* polling */ | ||
73 | }; | ||
74 | |||
75 | /* | ||
76 | * serial driver API | ||
77 | */ | ||
78 | int tx_rdy(void) | ||
79 | { | ||
80 | return uartc_port_tx_ready(&ser_port) ? 1 : 0; | ||
81 | } | ||
82 | |||
83 | void tx_writec(unsigned char c) | ||
84 | { | ||
85 | uartc_port_tx_byte(&ser_port, c); | ||
86 | } | ||
87 | |||
88 | #ifndef IPOD_ACCESSORY_PROTOCOL | ||
89 | void serial_setup(void) | ||
90 | { | ||
91 | uartc_port_open(&ser_port); | ||
92 | |||
93 | /* set a default configuration, Tx and Rx modes are | ||
94 | disabled when the port is initialized */ | ||
95 | uartc_port_config(&ser_port, ULCON_DATA_BITS_8, | ||
96 | ULCON_PARITY_NONE, ULCON_STOP_BITS_1); | ||
97 | uartc_port_set_bitrate_raw(&ser_port, BRDATA_115200); | ||
98 | |||
99 | /* enable Tx interrupt request or POLLING mode */ | ||
100 | uartc_port_set_tx_mode(&ser_port, UCON_MODE_INTREQ); | ||
101 | |||
102 | logf("[%lu] "MODEL_NAME" port %d ready!", USEC_TIMER, ser_port.id); | ||
103 | } | ||
104 | |||
105 | |||
106 | #else /* IPOD_ACCESSORY_PROTOCOL */ | ||
107 | #include "kernel.h" | ||
108 | #include "pmu-target.h" | ||
109 | #include "iap.h" | ||
110 | |||
111 | static enum { | ||
112 | ABR_STATUS_LAUNCHED, /* ST_SYNC */ | ||
113 | ABR_STATUS_SYNCING, /* ST_SOF */ | ||
114 | ABR_STATUS_DONE | ||
115 | } abr_status; | ||
116 | |||
117 | static int bitrate = 0; | ||
118 | static bool acc_plugged = false; | ||
119 | |||
120 | static void serial_acc_tick(void) | ||
121 | { | ||
122 | bool plugged = pmu_accessory_present(); | ||
123 | if (acc_plugged != plugged) | ||
124 | { | ||
125 | acc_plugged = plugged; | ||
126 | if (acc_plugged) | ||
127 | { | ||
128 | uartc_open(ser_port.uartc); | ||
129 | uartc_port_open(&ser_port); | ||
130 | /* set a default configuration, Tx and Rx modes are | ||
131 | disabled when the port is initialized */ | ||
132 | uartc_port_config(&ser_port, ULCON_DATA_BITS_8, | ||
133 | ULCON_PARITY_NONE, ULCON_STOP_BITS_1); | ||
134 | uartc_port_set_tx_mode(&ser_port, UCON_MODE_INTREQ); | ||
135 | serial_bitrate(bitrate); | ||
136 | } | ||
137 | else | ||
138 | { | ||
139 | uartc_port_close(&ser_port); | ||
140 | uartc_close(ser_port.uartc); | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | |||
145 | void serial_setup(void) | ||
146 | { | ||
147 | uartc_close(ser_port.uartc); | ||
148 | tick_add_task(serial_acc_tick); | ||
149 | } | ||
150 | |||
151 | void serial_bitrate(int rate) | ||
152 | { | ||
153 | bitrate = rate; | ||
154 | if (!acc_plugged) | ||
155 | return; | ||
156 | |||
157 | logf("[%lu] serial_bitrate(%d)", USEC_TIMER, rate); | ||
158 | |||
159 | if (rate == 0) { | ||
160 | /* Using auto-bitrate (ABR) to detect accessory Tx speed: | ||
161 | * | ||
162 | * + Here: | ||
163 | * - Disable Rx logic to clean the FIFO and the shift | ||
164 | * register, thus no Rx data interrupts are generated. | ||
165 | * - Launch ABR and wait for a low pulse in Rx line. | ||
166 | * | ||
167 | * + In ISR, when a low pulse is detected (ideally it is the | ||
168 | * start bit of 0xff): | ||
169 | * - Calculate and configure detected speed. | ||
170 | * - Enable Rx to verify that the next received data frame | ||
171 | * is 0x55 or 0xff: | ||
172 | * - If so, it's assumed bit rate is correctly detected, | ||
173 | * it will not be modified until speed is changed using | ||
174 | * RB options menu. | ||
175 | * - If not, reset iAP state machine and launch a new ABR. | ||
176 | */ | ||
177 | uartc_port_set_rx_mode(&ser_port, UCON_MODE_DISABLED); | ||
178 | uartc_port_abr_start(&ser_port); | ||
179 | abr_status = ABR_STATUS_LAUNCHED; | ||
180 | } | ||
181 | else { | ||
182 | uint32_t brdata; | ||
183 | if (rate == 57600) brdata = BRDATA_57600; | ||
184 | else if (rate == 38400) brdata = BRDATA_38400; | ||
185 | else if (rate == 19200) brdata = BRDATA_19200; | ||
186 | else brdata = BRDATA_9600; | ||
187 | uartc_port_abr_stop(&ser_port); /* abort ABR if already launched */ | ||
188 | uartc_port_set_bitrate_raw(&ser_port, brdata); | ||
189 | uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ); | ||
190 | abr_status = ABR_STATUS_DONE; | ||
191 | } | ||
192 | } | ||
193 | |||
194 | static void iap_rx_isr(int len, char *data, char *err, uint32_t abr_cnt) | ||
195 | { | ||
196 | /* ignore Rx errors, upper layer will discard bad packets */ | ||
197 | (void) err; | ||
198 | |||
199 | static int sync_retry; | ||
200 | |||
201 | if (abr_status == ABR_STATUS_LAUNCHED) { | ||
202 | /* autobauding */ | ||
203 | if (abr_cnt) { | ||
204 | #define BR2CNT(s) (IPOD6G_UART_CLK_HZ / (unsigned)(s)) | ||
205 | if (abr_cnt < BR2CNT(57600*1.1) || abr_cnt > BR2CNT(9600*0.9)) { | ||
206 | /* detected speed out of range, relaunch ABR */ | ||
207 | uartc_port_abr_start(&ser_port); | ||
208 | return; | ||
209 | } | ||
210 | /* valid speed detected, select it */ | ||
211 | uint32_t brdata; | ||
212 | if (abr_cnt < BR2CNT(48000)) brdata = BRDATA_57600; | ||
213 | else if (abr_cnt < BR2CNT(33600)) brdata = BRDATA_38400; | ||
214 | else if (abr_cnt < BR2CNT(24000)) brdata = BRDATA_28800; | ||
215 | else if (abr_cnt < BR2CNT(14400)) brdata = BRDATA_19200; | ||
216 | else brdata = BRDATA_9600; | ||
217 | |||
218 | /* set detected speed */ | ||
219 | uartc_port_set_bitrate_raw(&ser_port, brdata); | ||
220 | uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ); | ||
221 | |||
222 | /* enter SOF state */ | ||
223 | iap_getc(0xff); | ||
224 | |||
225 | abr_status = ABR_STATUS_SYNCING; | ||
226 | sync_retry = 2; /* we are expecting [0xff] 0x55 */ | ||
227 | } | ||
228 | } | ||
229 | |||
230 | /* process received data */ | ||
231 | while (len--) | ||
232 | { | ||
233 | bool sync_done = !iap_getc(*data++); | ||
234 | |||
235 | if (abr_status == ABR_STATUS_SYNCING) | ||
236 | { | ||
237 | if (sync_done) { | ||
238 | abr_status = ABR_STATUS_DONE; | ||
239 | } | ||
240 | else if (--sync_retry == 0) { | ||
241 | /* invalid speed detected, relaunch ABR | ||
242 | discarding remaining data (if any) */ | ||
243 | serial_bitrate(0); | ||
244 | break; | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | #endif /* IPOD_ACCESSORY_PROTOCOL */ | ||