summaryrefslogtreecommitdiff
path: root/firmware/target/arm/s5l8702/ipod6g/serial-6g.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/s5l8702/ipod6g/serial-6g.c')
-rw-r--r--firmware/target/arm/s5l8702/ipod6g/serial-6g.c249
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
49extern const struct uartc s5l8702_uartc;
50#ifdef IPOD_ACCESSORY_PROTOCOL
51static void iap_rx_isr(int, char*, char*, uint32_t);
52#endif
53
54struct 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 */
78int tx_rdy(void)
79{
80 return uartc_port_tx_ready(&ser_port) ? 1 : 0;
81}
82
83void tx_writec(unsigned char c)
84{
85 uartc_port_tx_byte(&ser_port, c);
86}
87
88#ifndef IPOD_ACCESSORY_PROTOCOL
89void 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
111static enum {
112 ABR_STATUS_LAUNCHED, /* ST_SYNC */
113 ABR_STATUS_SYNCING, /* ST_SOF */
114 ABR_STATUS_DONE
115} abr_status;
116
117static int bitrate = 0;
118static bool acc_plugged = false;
119
120static 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
145void serial_setup(void)
146{
147 uartc_close(ser_port.uartc);
148 tick_add_task(serial_acc_tick);
149}
150
151void 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
194static 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 */