blob: a8f804496a0e1487d810868ad83bf98857f61dd0 [file] [log] [blame]
Harald Welte03d6ebb2019-09-28 23:19:31 +02001/* Card (ICC) UART driver for the Atmel ASF4 asynchronous USART */
2
3#include <errno.h>
4
5#include <osmocom/core/linuxlist.h>
6#include <osmocom/core/utils.h>
7
8#include <hal_usart_async.h>
9#include <utils_ringbuffer.h>
10#include "driver_init.h"
11
12#include "ncn8025.h"
13
14#include "cuart.h"
15
16static struct usart_async_descriptor* SIM_peripheral_descriptors[] = {&SIM0, &SIM1, &SIM2, &SIM3, &SIM4, &SIM5, &SIM6, NULL};
17
18extern struct card_uart *cuart4slot_nr(uint8_t slot_nr);
19
20/***********************************************************************
21 * low-level helper routines
22 ***********************************************************************/
23
24static void _SIM_rx_cb(const struct usart_async_descriptor *const io_descr, uint8_t slot_nr)
25{
26 struct card_uart *cuart = cuart4slot_nr(slot_nr);
27 int rc;
28 OSMO_ASSERT(cuart);
29
30 if (cuart->rx_threshold == 1) {
31 /* bypass ringbuffer and report byte directly */
32 uint8_t rx[1];
33 rc = io_read((struct io_descriptor * const)&io_descr->io, rx, sizeof(rx));
34 OSMO_ASSERT(rc == sizeof(rx));
35 card_uart_notification(cuart, CUART_E_RX_SINGLE, rx);
36 } else {
37 /* go via ringbuffer and notify only after threshold */
38 if (ringbuffer_num(&io_descr->rx) >= cuart->rx_threshold)
39 card_uart_notification(cuart, CUART_E_RX_COMPLETE, NULL);
40 }
41}
42
43static void _SIM_tx_cb(const struct usart_async_descriptor *const io_descr, uint8_t slot_nr)
44{
45 struct card_uart *cuart = cuart4slot_nr(slot_nr);
46 OSMO_ASSERT(cuart);
47 card_uart_notification(cuart, CUART_E_TX_COMPLETE, io_descr->tx_buffer);
48}
49
50#include <hpl_usart_async.h>
51#include <hpl_usart_sync.h>
52
53
54static void _SIM_error_cb(const struct usart_async_descriptor *const descr){
55 volatile uint32_t status = hri_sercomusart_read_STATUS_reg(descr->device.hw);
56 volatile uint32_t data = hri_sercomusart_read_DATA_reg(descr->device.hw);
57 volatile uint32_t errrs = hri_sercomusart_read_RXERRCNT_reg(descr->device.hw);
58 OSMO_ASSERT(0 == 1)
59}
60
61/* the below ugli-ness is required as the usart_async_descriptor doesn't have
62 * some kind of 'private' member that could provide the call-back anty kind of
63 * context */
64static void SIM0_rx_cb(const struct usart_async_descriptor *const io_descr)
65{
66 _SIM_rx_cb(io_descr, 0);
67}
68static void SIM1_rx_cb(const struct usart_async_descriptor *const io_descr)
69{
70 _SIM_rx_cb(io_descr, 1);
71}
72static void SIM2_rx_cb(const struct usart_async_descriptor *const io_descr)
73{
74 _SIM_rx_cb(io_descr, 2);
75}
76static void SIM3_rx_cb(const struct usart_async_descriptor *const io_descr)
77{
78 _SIM_rx_cb(io_descr, 3);
79}
80static void SIM4_rx_cb(const struct usart_async_descriptor *const io_descr)
81{
82 _SIM_rx_cb(io_descr, 4);
83}
84static void SIM5_rx_cb(const struct usart_async_descriptor *const io_descr)
85{
86 _SIM_rx_cb(io_descr, 5);
87}
88static void SIM6_rx_cb(const struct usart_async_descriptor *const io_descr)
89{
90 _SIM_rx_cb(io_descr, 6);
91}
92static void SIM7_rx_cb(const struct usart_async_descriptor *const io_descr)
93{
94 _SIM_rx_cb(io_descr, 7);
95}
96static usart_cb_t SIM_rx_cb[8] = {
97 SIM0_rx_cb, SIM1_rx_cb, SIM2_rx_cb, SIM3_rx_cb,
98 SIM4_rx_cb, SIM5_rx_cb, SIM6_rx_cb, SIM7_rx_cb,
99};
100static void SIM0_tx_cb(const struct usart_async_descriptor *const io_descr)
101{
102 _SIM_tx_cb(io_descr, 0);
103}
104static void SIM1_tx_cb(const struct usart_async_descriptor *const io_descr)
105{
106 _SIM_tx_cb(io_descr, 1);
107}
108static void SIM2_tx_cb(const struct usart_async_descriptor *const io_descr)
109{
110 _SIM_tx_cb(io_descr, 2);
111}
112static void SIM3_tx_cb(const struct usart_async_descriptor *const io_descr)
113{
114 _SIM_tx_cb(io_descr, 3);
115}
116static void SIM4_tx_cb(const struct usart_async_descriptor *const io_descr)
117{
118 _SIM_tx_cb(io_descr, 4);
119}
120static void SIM5_tx_cb(const struct usart_async_descriptor *const io_descr)
121{
122 _SIM_tx_cb(io_descr, 5);
123}
124static void SIM6_tx_cb(const struct usart_async_descriptor *const io_descr)
125{
126 _SIM_tx_cb(io_descr, 6);
127}
128static void SIM7_tx_cb(const struct usart_async_descriptor *const io_descr)
129{
130 _SIM_tx_cb(io_descr, 7);
131}
132static usart_cb_t SIM_tx_cb[8] = {
133 SIM0_tx_cb, SIM1_tx_cb, SIM2_tx_cb, SIM3_tx_cb,
134 SIM4_tx_cb, SIM5_tx_cb, SIM6_tx_cb, SIM7_tx_cb,
135};
136
137#include <math.h>
138#include "atmel_start.h"
139#include "atmel_start_pins.h"
140#include "config/hpl_gclk_config.h"
141#include "iso7816_3.h"
142
143/** possible clock sources for the SERCOM peripheral
144 * warning: the definition must match the GCLK configuration
145 */
146static const uint8_t sercom_glck_sources[] = {GCLK_PCHCTRL_GEN_GCLK2_Val, GCLK_PCHCTRL_GEN_GCLK4_Val, GCLK_PCHCTRL_GEN_GCLK6_Val};
147
148 /** possible clock frequencies in MHz for the SERCOM peripheral
149 * warning: the definition must match the GCLK configuration
150 */
151static const double sercom_glck_freqs[] = {100E6 / CONF_GCLK_GEN_2_DIV, 100E6 / CONF_GCLK_GEN_4_DIV, 120E6 / CONF_GCLK_GEN_6_DIV};
152
153/** the GCLK ID for the SERCOM SIM peripherals
154 * @note: used as index for PCHCTRL
155 */
156static const uint8_t SIM_peripheral_GCLK_ID[] = {SERCOM0_GCLK_ID_CORE, SERCOM1_GCLK_ID_CORE, SERCOM2_GCLK_ID_CORE, SERCOM3_GCLK_ID_CORE, SERCOM4_GCLK_ID_CORE, SERCOM5_GCLK_ID_CORE, SERCOM6_GCLK_ID_CORE, SERCOM7_GCLK_ID_CORE};
157
158
159/** change baud rate of card slot
160 * @param[in] slotnr slot number for which the baud rate should be set
161 * @param[in] baudrate baud rate in bps to set
162 * @return if the baud rate has been set, else a parameter is out of range
163 */
164static bool slot_set_baudrate(uint8_t slotnr, uint32_t baudrate)
165{
166 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
167
168 // calculate the error corresponding to the clock sources
169 uint16_t bauds[ARRAY_SIZE(sercom_glck_freqs)];
170 double errors[ARRAY_SIZE(sercom_glck_freqs)];
171 for (uint8_t i = 0; i < ARRAY_SIZE(sercom_glck_freqs); i++) {
172 double freq = sercom_glck_freqs[i]; // remember possible SERCOM frequency
173 uint32_t min = freq/16. * (1. - 65535. / 65536.); // calculate the minimum baud rate for this frequency
174 uint32_t max = freq/16. * (1. - 1. / 65536.); // calculate the maximum baud rate for this frequency
175 if (baudrate < min || baudrate > max) { // baud rate it out of supported range
176 errors[i] = NAN;
177 } else {
178 uint16_t baud = round(65536. * (1. - 16. * (baudrate/freq)));
179 bauds[i] = baud;
180 double actual = freq/16. * (1. - baud / 65536.);
181 errors[i] = fabs(1.0 - (actual / baudrate));
182 }
183 }
184
185 // find the smallest error
186 uint8_t best = ARRAY_SIZE(sercom_glck_freqs);
187 for (uint8_t i = 0; i < ARRAY_SIZE(sercom_glck_freqs); i++) {
188 if (isnan(errors[i])) {
189 continue;
190 }
191 if (best >= ARRAY_SIZE(sercom_glck_freqs)) {
192 best = i;
193 } else if (errors[i] < errors[best]) {
194 best = i;
195 }
196 }
197 if (best >= ARRAY_SIZE(sercom_glck_freqs)) { // found no clock supporting this baud rate
198 return false;
199 }
200
201 // set clock and baud rate
202 struct usart_async_descriptor* slot = SIM_peripheral_descriptors[slotnr]; // get slot
203 if (NULL == slot) {
204 return false;
205 }
206 printf("(%u) switching SERCOM clock to GCLK%u (freq = %lu kHz) and baud rate to %lu bps (baud = %u)\r\n", slotnr, (best + 1) * 2, (uint32_t)(round(sercom_glck_freqs[best] / 1000)), baudrate, bauds[best]);
207 while (!usart_async_is_tx_empty(slot)); // wait for transmission to complete (WARNING no timeout)
208 usart_async_disable(slot); // disable SERCOM peripheral
209 hri_gclk_clear_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], (1 << GCLK_PCHCTRL_CHEN_Pos)); // disable clock for this peripheral
210 while (hri_gclk_get_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], (1 << GCLK_PCHCTRL_CHEN_Pos))); // wait until clock is really disabled
211 // it does not seem we need to completely disable the peripheral using hri_mclk_clear_APBDMASK_SERCOMn_bit
212 hri_gclk_write_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], sercom_glck_sources[best] | (1 << GCLK_PCHCTRL_CHEN_Pos)); // set peripheral core clock and re-enable it
213 usart_async_set_baud_rate(slot, bauds[best]); // set the new baud rate
214 usart_async_enable(slot); // re-enable SERCOM peripheral
215
216 return true;
217}
218
219/** change ISO baud rate of card slot
220 * @param[in] slotnr slot number for which the baud rate should be set
221 * @param[in] clkdiv can clock divider
222 * @param[in] f clock rate conversion integer F
223 * @param[in] d baud rate adjustment factor D
224 * @return if the baud rate has been set, else a parameter is out of range
225 */
226static bool slot_set_isorate(uint8_t slotnr, enum ncn8025_sim_clkdiv clkdiv, uint16_t f, uint8_t d)
227{
228 // input checks
229 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
230 if (clkdiv != SIM_CLKDIV_1 && clkdiv != SIM_CLKDIV_2 && clkdiv != SIM_CLKDIV_4 && clkdiv != SIM_CLKDIV_8) {
231 return false;
232 }
233 if (!iso7816_3_valid_f(f)) {
234 return false;
235 }
236 if (!iso7816_3_valid_d(d)) {
237 return false;
238 }
239
240 // set clockdiv
241 struct ncn8025_settings settings;
242 ncn8025_get(slotnr, &settings);
243 if (settings.clkdiv != clkdiv) {
244 settings.clkdiv = clkdiv;
245 ncn8025_set(slotnr, &settings);
246 }
247
248 // calculate desired frequency
249 uint32_t freq = 20000000UL; // maximum frequency
250 switch (clkdiv) {
251 case SIM_CLKDIV_1:
252 freq /= 1;
253 break;
254 case SIM_CLKDIV_2:
255 freq /= 2;
256 break;
257 case SIM_CLKDIV_4:
258 freq /= 4;
259 break;
260 case SIM_CLKDIV_8:
261 freq /= 8;
262 break;
263 }
264
265 // set baud rate
266 uint32_t baudrate = (freq * d) / f; // calculate actual baud rate
267 return slot_set_baudrate(slotnr, baudrate); // set baud rate
268}
269
270/***********************************************************************
271 * Interface with card_uart (cuart) core
272 ***********************************************************************/
273
274/* forward-declaration */
275static struct card_uart_driver asf4_usart_driver;
276static int asf4_usart_close(struct card_uart *cuart);
277
278static int asf4_usart_open(struct card_uart *cuart, const char *device_name)
279{
280 struct usart_async_descriptor *usa_pd;
281 int slot_nr = atoi(device_name);
282
283 if (slot_nr >= ARRAY_SIZE(SIM_peripheral_descriptors))
284 return -ENODEV;
285 usa_pd = SIM_peripheral_descriptors[slot_nr];
286 if (!usa_pd)
287 return -ENODEV;
288
289 cuart->u.asf4.usa_pd = usa_pd;
290 cuart->u.asf4.slot_nr = slot_nr;
291
Eric Wildb39d8322019-11-27 16:50:28 +0100292 /* in us, 20Mhz with default ncn8025 divider 8, F=372, D=1*/
293 cuart->u.asf4.extrawait_after_rx = 1./(20./8/372);
Harald Welte03d6ebb2019-09-28 23:19:31 +0200294
295 usart_async_register_callback(usa_pd, USART_ASYNC_RXC_CB, SIM_rx_cb[slot_nr]);
296 usart_async_register_callback(usa_pd, USART_ASYNC_TXC_CB, SIM_tx_cb[slot_nr]);
297 usart_async_register_callback(usa_pd, USART_ASYNC_ERROR_CB, _SIM_error_cb);
298 usart_async_enable(usa_pd);
299
300 // set USART baud rate to match the interface (f = 2.5 MHz) and card default settings (Fd = 372, Dd = 1)
301 slot_set_isorate(cuart->u.asf4.slot_nr, SIM_CLKDIV_8, ISO7816_3_DEFAULT_FD, ISO7816_3_DEFAULT_DD);
302
303 return 0;
304}
305
306static int asf4_usart_close(struct card_uart *cuart)
307{
308 struct usart_async_descriptor *usa_pd = cuart->u.asf4.usa_pd;
309
310 OSMO_ASSERT(cuart->driver == &asf4_usart_driver);
311
312 usart_async_disable(usa_pd);
313
314 return 0;
315}
316
317static int asf4_usart_async_tx(struct card_uart *cuart, const uint8_t *data, size_t len)
318{
319 struct usart_async_descriptor *usa_pd = cuart->u.asf4.usa_pd;
320 int rc;
321
322 OSMO_ASSERT(cuart->driver == &asf4_usart_driver);
323 OSMO_ASSERT(usart_async_is_tx_empty(usa_pd));
324
325 rc = io_write(&usa_pd->io, data, len);
326 if (rc < 0)
327 return rc;
328
329 cuart->tx_busy = true;
330
331 return rc;
332}
333
334static int asf4_usart_async_rx(struct card_uart *cuart, uint8_t *data, size_t len)
335{
336 struct usart_async_descriptor *usa_pd = cuart->u.asf4.usa_pd;
337
338 OSMO_ASSERT(cuart->driver == &asf4_usart_driver);
339
340 return io_read(&usa_pd->io, data, len);
341}
342
Eric Wildb39d8322019-11-27 16:50:28 +0100343#include "ccid_device.h"
344#include "iso7816_3.h"
Harald Welte03d6ebb2019-09-28 23:19:31 +0200345static int asf4_usart_ctrl(struct card_uart *cuart, enum card_uart_ctl ctl, int arg)
346{
347 struct ncn8025_settings settings;
348 Sercom *sercom = cuart->u.asf4.usa_pd->device.hw;
349
350 switch (ctl) {
351 case CUART_CTL_RX:
352 if (arg){
353 sercom->USART.CTRLB.bit.RXEN = 1;
354 sercom->USART.CTRLB.bit.TXEN = 0;
355 } else {
Eric Wildb39d8322019-11-27 16:50:28 +0100356 delay_us(cuart->u.asf4.extrawait_after_rx);
Harald Welte03d6ebb2019-09-28 23:19:31 +0200357 sercom->USART.CTRLB.bit.RXEN = 0;
358 sercom->USART.CTRLB.bit.TXEN = 1;
359 }
360 break;
361 case CUART_CTL_RST:
362 ncn8025_get(cuart->u.asf4.slot_nr, &settings);
363 settings.rstin = arg ? true : false;
364 ncn8025_set(cuart->u.asf4.slot_nr, &settings);
365 usart_async_flush_rx_buffer(cuart->u.asf4.usa_pd);
366 break;
367 case CUART_CTL_POWER:
Eric Wildb39d8322019-11-27 16:50:28 +0100368 /* in us, 20Mhz with default ncn8025 divider 8, F=372, D=1*/
369 cuart->u.asf4.extrawait_after_rx = 1./(20./8/372);
Harald Welte03d6ebb2019-09-28 23:19:31 +0200370
371 // set USART baud rate to match the interface (f = 2.5 MHz) and card default settings (Fd = 372, Dd = 1)
372 if(arg)
373 slot_set_isorate(cuart->u.asf4.slot_nr, SIM_CLKDIV_8, ISO7816_3_DEFAULT_FD, ISO7816_3_DEFAULT_DD);
374
Eric Wildb39d8322019-11-27 16:50:28 +0100375 ncn8025_get(cuart->u.asf4.slot_nr, &settings);
376 settings.cmdvcc = arg ? true : false;
377 settings.led = arg ? true : false;
378 settings.vsel = SIM_VOLT_5V0;
Harald Welte03d6ebb2019-09-28 23:19:31 +0200379 ncn8025_set(cuart->u.asf4.slot_nr, &settings);
Eric Wildb39d8322019-11-27 16:50:28 +0100380
Harald Welte03d6ebb2019-09-28 23:19:31 +0200381 break;
382 case CUART_CTL_WTIME:
383 /* no driver-specific handling of this */
384 break;
385 case CUART_CTL_CLOCK:
Eric Wildb39d8322019-11-27 16:50:28 +0100386 /* no clock stop support */
387 break;
388 case CUART_CTL_CLOCK_FREQ:
389 ncn8025_get(cuart->u.asf4.slot_nr, &settings);
390
391 /* 2,5/5/10/20 supported by dividers */
392 enum ncn8025_sim_clkdiv clkdiv = SIM_CLKDIV_1;
393 if(arg < 20000000)
394 clkdiv = SIM_CLKDIV_2;
395 if(arg < 10000000)
396 clkdiv = SIM_CLKDIV_4;
397 if(arg < 5000000)
398 clkdiv = SIM_CLKDIV_8;
399 settings.clkdiv = clkdiv;
400 ncn8025_set(cuart->u.asf4.slot_nr, &settings);
401 break;
402 case CUART_CTL_FD:
403 ncn8025_get(cuart->u.asf4.slot_nr, &settings);
404 uint8_t divider = ncn8025_div_val[settings.clkdiv];
405 uint32_t baudrate = (20e6/divider)/arg;
406 cuart->u.asf4.extrawait_after_rx = 1./baudrate * 1000 * 1000;
407 slot_set_baudrate(cuart->u.asf4.slot_nr, baudrate);
408 break;
Harald Welte03d6ebb2019-09-28 23:19:31 +0200409 default:
410 return -EINVAL;
411 }
412 return 0;
413}
414
415static const struct card_uart_ops asf4_usart_ops = {
416 .open = asf4_usart_open,
417 .close = asf4_usart_close,
418 .async_tx = asf4_usart_async_tx,
419 .async_rx = asf4_usart_async_rx,
420 .ctrl = asf4_usart_ctrl,
421};
422
423static struct card_uart_driver asf4_usart_driver = {
424 .name = "asf4",
425 .ops = &asf4_usart_ops,
426};
427
428static __attribute__((constructor)) void on_dso_load_cuart_asf4(void)
429{
430 card_uart_driver_register(&asf4_usart_driver);
431}