blob: a895c140679b6028af13304816eaba77dcc82f60 [file] [log] [blame]
Kévin Redon69b92d92019-01-24 16:39:20 +01001/*
Kévin Redon78d2f442019-01-24 18:45:59 +01002 * Copyright (C) 2019 sysmocom -s.f.m.c. GmbH, Author: Kevin Redon <kredon@sysmocom.de>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17*/
Kévin Redon69b92d92019-01-24 16:39:20 +010018
Harald Welte1b9a5b82019-02-24 23:04:45 +010019#include <stdlib.h>
20#include <stdio.h>
Kévin Redon072951b2019-05-02 15:17:46 +020021#include <math.h>
Harald Weltef53f2262019-02-24 11:01:08 +010022#include <parts.h>
23#include <hal_cache.h>
Harald Welte93f628a2019-02-24 14:32:30 +010024#include <hri_port_e54.h>
Harald Weltef53f2262019-02-24 11:01:08 +010025
Kévin Redon69b92d92019-01-24 16:39:20 +010026#include "atmel_start.h"
27#include "atmel_start_pins.h"
Kévin Redon072951b2019-05-02 15:17:46 +020028#include "config/hpl_gclk_config.h"
Kévin Redon69b92d92019-01-24 16:39:20 +010029
Harald Weltec3f170d2019-02-24 09:06:59 +010030#include "i2c_bitbang.h"
31#include "octsim_i2c.h"
32#include "ncn8025.h"
Kévin Redon0f050722019-05-02 15:56:25 +020033#include "iso7816_3.h"
Harald Weltec3f170d2019-02-24 09:06:59 +010034
Harald Welteff9f4ce2019-02-24 22:51:09 +010035#include "command.h"
36
Kévin Redonc89bb8c2019-04-17 01:20:23 +020037// TODO put declaration in more global file
38// TODO for now SIM7 is not present because used for debug
39static struct usart_async_descriptor* SIM_peripheral_descriptors[] = {&SIM0, &SIM1, &SIM2, &SIM3, &SIM4, &SIM5, &SIM6, NULL};
40
41static void SIM_rx_cb(const struct usart_async_descriptor *const io_descr)
42{
43}
Kévin Redon78d2f442019-01-24 18:45:59 +010044
Kévin Redon072951b2019-05-02 15:17:46 +020045/** possible clock sources for the SERCOM peripheral
46 * warning: the definition must match the GCLK configuration
47 */
48static const uint8_t sercom_glck_sources[] = {GCLK_PCHCTRL_GEN_GCLK2_Val, GCLK_PCHCTRL_GEN_GCLK4_Val, GCLK_PCHCTRL_GEN_GCLK6_Val};
49
50/** possible clock frequencies in MHz for the SERCOM peripheral
51 * warning: the definition must match the GCLK configuration
52 */
53static const double sercom_glck_freqs[] = {100E6 / CONF_GCLK_GEN_2_DIV, 100E6 / CONF_GCLK_GEN_4_DIV, 120E6 / CONF_GCLK_GEN_6_DIV};
54
55/** the GCLK ID for the SERCOM SIM peripherals
56 * @note: used as index for PCHCTRL
57 */
58static 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};
59
Harald Weltec3f170d2019-02-24 09:06:59 +010060static void board_init()
61{
62 int i;
63
64 for (i = 0; i < 4; i++)
65 i2c_init(&i2c[i]);
66
Harald Welte255da5e2019-04-16 18:19:53 +020067 for (i = 0; i < 8; i++)
Harald Weltec3f170d2019-02-24 09:06:59 +010068 ncn8025_init(i);
Harald Weltef53f2262019-02-24 11:01:08 +010069
70 cache_init();
71 cache_enable(CMCC);
Harald Welte93f628a2019-02-24 14:32:30 +010072
73 /* increase drive strength of 20Mhz SIM clock output to 8mA
74 * (there are 8 inputs + traces to drive!) */
75 hri_port_set_PINCFG_DRVSTR_bit(PORT, 0, 11);
Kévin Redonc89bb8c2019-04-17 01:20:23 +020076
77 // enable SIM interfaces
78 for (uint8_t i = 0; i < ARRAY_SIZE(SIM_peripheral_descriptors); i++) {
79 if (NULL == SIM_peripheral_descriptors[i]) {
80 continue;
81 }
82 usart_async_register_callback(SIM_peripheral_descriptors[i], USART_ASYNC_RXC_CB, SIM_rx_cb); // required for RX to work, even if the callback does nothing
83 usart_async_enable(SIM_peripheral_descriptors[i]);
84 }
Harald Weltec3f170d2019-02-24 09:06:59 +010085}
86
Harald Welte1b9a5b82019-02-24 23:04:45 +010087static int validate_slotnr(int argc, char **argv, int idx)
88{
89 int slotnr;
90 if (argc < idx+1) {
91 printf("You have to specify the slot number (0..7)\r\n");
92 return -1;
93 }
94 slotnr = atoi(argv[idx]);
95 if (slotnr < 0 || slotnr > 7) {
96 printf("You have to specify the slot number (0..7)\r\n");
97 return -1;
98 }
99 return slotnr;
100}
101
Kévin Redon072951b2019-05-02 15:17:46 +0200102/** change baud rate of card slot
103 * @param[in] slotnr slot number for which the baud rate should be set
104 * @param[in] baudrate baud rate in bps to set
105 * @return if the baud rate has been set, else a parameter is out of range
106 */
107static bool slot_set_baudrate(uint8_t slotnr, uint32_t baudrate)
108{
109 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
110
111 // calculate the error corresponding to the clock sources
112 uint16_t bauds[ARRAY_SIZE(sercom_glck_freqs)];
113 double errors[ARRAY_SIZE(sercom_glck_freqs)];
114 for (uint8_t i = 0; i < ARRAY_SIZE(sercom_glck_freqs); i++) {
115 double freq = sercom_glck_freqs[i]; // remember possible SERCOM frequency
116 uint32_t min = freq / (2 * (255 + 1)); // calculate the minimum baud rate for this frequency
117 uint32_t max = freq / (2 * (0 + 1)); // calculate the maximum baud rate for this frequency
118 if (baudrate < min || baudrate > max) { // baud rate it out of supported range
119 errors[i] = NAN;
120 } else {
121 uint16_t baud = round(freq / (2 * baudrate) - 1);
122 bauds[i] = baud;
123 double actual = freq / (2 * (baud + 1));
124 errors[i] = fabs(1.0 - (actual / baudrate));
125 }
126 }
127
128 // find the smallest error
129 uint8_t best = ARRAY_SIZE(sercom_glck_freqs);
130 for (uint8_t i = 0; i < ARRAY_SIZE(sercom_glck_freqs); i++) {
131 if (isnan(errors[i])) {
132 continue;
133 }
134 if (best >= ARRAY_SIZE(sercom_glck_freqs)) {
135 best = i;
136 } else if (errors[i] < errors[best]) {
137 best = i;
138 }
139 }
140 if (best >= ARRAY_SIZE(sercom_glck_freqs)) { // found no clock supporting this baud rate
141 return false;
142 }
143
144 // set clock and baud rate
145 struct usart_async_descriptor* slot = SIM_peripheral_descriptors[slotnr]; // get slot
146 if (NULL == slot) {
147 return false;
148 }
149 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]);
150 while (!usart_async_is_tx_empty(slot)); // wait for transmission to complete (WARNING no timeout)
151 usart_async_disable(slot); // disable SERCOM peripheral
152 hri_gclk_clear_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], (1 << GCLK_PCHCTRL_CHEN_Pos)); // disable clock for this peripheral
153 while (hri_gclk_get_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], (1 << GCLK_PCHCTRL_CHEN_Pos))); // wait until clock is really disabled
154 // it does not seem we need to completely disable the peripheral using hri_mclk_clear_APBDMASK_SERCOMn_bit
155 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
156 usart_async_set_baud_rate(slot, bauds[best]); // set the new baud rate
157 usart_async_enable(slot); // re-enable SERCOM peripheral
158
159 return true;
160}
161
Kévin Redon0f050722019-05-02 15:56:25 +0200162/** change ISO baud rate of card slot
163 * @param[in] slotnr slot number for which the baud rate should be set
164 * @param[in] clkdiv can clock divider
165 * @param[in] f clock rate conversion integer F
166 * @param[in] d baud rate adjustment factor D
167 * @return if the baud rate has been set, else a parameter is out of range
168 */
169static bool slot_set_isorate(uint8_t slotnr, enum ncn8025_sim_clkdiv clkdiv, uint16_t f, uint8_t d)
170{
171 // input checks
172 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
173 if (clkdiv != SIM_CLKDIV_1 && clkdiv != SIM_CLKDIV_2 && clkdiv != SIM_CLKDIV_4 && clkdiv != SIM_CLKDIV_8) {
174 return false;
175 }
176 if (!iso7816_3_valid_f(f)) {
177 return false;
178 }
179 if (!iso7816_3_valid_d(d)) {
180 return false;
181 }
182
183 // set clockdiv
184 struct ncn8025_settings settings;
185 ncn8025_get(slotnr, &settings);
186 if (settings.clkdiv != clkdiv) {
187 settings.clkdiv = clkdiv;
188 ncn8025_set(slotnr, &settings);
189 }
190
191 // calculate desired frequency
192 uint32_t freq = 20000000UL; // maximum frequency
193 switch (clkdiv) {
194 case SIM_CLKDIV_1:
195 freq /= 1;
196 break;
197 case SIM_CLKDIV_2:
198 freq /= 2;
199 break;
200 case SIM_CLKDIV_4:
201 freq /= 4;
202 break;
203 case SIM_CLKDIV_8:
204 freq /= 8;
205 break;
206 }
207
208 // set baud rate
209 uint32_t baudrate = (freq * d) / f; // calculate actual baud rate
210 return slot_set_baudrate(slotnr, baudrate); // // set baud rate
211}
212
Harald Welte1b9a5b82019-02-24 23:04:45 +0100213DEFUN(sim_status, cmd_sim_status, "sim-status", "Get state of specified NCN8025")
214{
215 struct ncn8025_settings settings;
216 int slotnr = validate_slotnr(argc, argv, 1);
217 if (slotnr < 0)
218 return;
219 ncn8025_get(slotnr, &settings);
220 printf("SIM%d: ", slotnr);
221 ncn8025_dump(&settings);
222 printf("\r\n");
223}
224
225DEFUN(sim_power, cmd_sim_power, "sim-power", "Enable/disable SIM card power")
226{
227 struct ncn8025_settings settings;
228 int slotnr = validate_slotnr(argc, argv, 1);
229 int enable;
230
231 if (slotnr < 0)
232 return;
233
234 if (argc < 3) {
235 printf("You have to specify 0=disable or 1=enable\r\n");
236 return;
237 }
238 enable = atoi(argv[2]);
239 ncn8025_get(slotnr, &settings);
240 if (enable)
241 settings.cmdvcc = true;
242 else
243 settings.cmdvcc = false;
244 ncn8025_set(slotnr, &settings);
245}
246
247DEFUN(sim_reset, cmd_sim_reset, "sim-reset", "Enable/disable SIM reset")
248{
249 struct ncn8025_settings settings;
250 int slotnr = validate_slotnr(argc, argv, 1);
251 int enable;
252
253 if (slotnr < 0)
254 return;
255
256 if (argc < 3) {
257 printf("You have to specify 0=disable or 1=enable\r\n");
258 return;
259 }
260 enable = atoi(argv[2]);
261 ncn8025_get(slotnr, &settings);
262 if (enable)
263 settings.rstin = true;
264 else
265 settings.rstin = false;
266 ncn8025_set(slotnr, &settings);
267}
268
269DEFUN(sim_clkdiv, cmd_sim_clkdiv, "sim-clkdiv", "Set SIM clock divider (1,2,4,8)")
270{
271 struct ncn8025_settings settings;
272 int slotnr = validate_slotnr(argc, argv, 1);
273 int clkdiv;
274
275 if (slotnr < 0)
276 return;
277
278 if (argc < 3) {
279 printf("You have to specify a valid divider (1,2,4,8)\r\n");
280 return;
281 }
282 clkdiv = atoi(argv[2]);
283 if (clkdiv != 1 && clkdiv != 2 && clkdiv != 4 && clkdiv != 8) {
284 printf("You have to specify a valid divider (1,2,4,8)\r\n");
285 return;
286 }
287 ncn8025_get(slotnr, &settings);
288 switch (clkdiv) {
289 case 1:
290 settings.clkdiv = SIM_CLKDIV_1;
291 break;
292 case 2:
293 settings.clkdiv = SIM_CLKDIV_2;
294 break;
295 case 4:
296 settings.clkdiv = SIM_CLKDIV_4;
297 break;
298 case 8:
299 settings.clkdiv = SIM_CLKDIV_8;
300 break;
301 }
302 ncn8025_set(slotnr, &settings);
303}
304
305DEFUN(sim_voltage, cmd_sim_voltage, "sim-voltage", "Set SIM voltage (5/3/1.8)")
306{
307 struct ncn8025_settings settings;
308 int slotnr = validate_slotnr(argc, argv, 1);
309
310 if (slotnr < 0)
311 return;
312
313 if (argc < 3) {
314 printf("You have to specify a valid voltage (5/3/1.8)\r\n");
315 return;
316 }
317 ncn8025_get(slotnr, &settings);
318 if (!strcmp(argv[2], "5"))
319 settings.vsel = SIM_VOLT_5V0;
320 else if (!strcmp(argv[2], "3"))
321 settings.vsel = SIM_VOLT_3V0;
322 else if (!strcmp(argv[2], "1.8"))
323 settings.vsel = SIM_VOLT_1V8;
324 else {
325 printf("You have to specify a valid voltage (5/3/1.8)\r\n");
326 return;
327 }
328 ncn8025_set(slotnr, &settings);
329}
330
331DEFUN(sim_led, cmd_sim_led, "sim-led", "Set SIM LED (1=on, 0=off)")
332{
333 struct ncn8025_settings settings;
334 int slotnr = validate_slotnr(argc, argv, 1);
335
336 if (slotnr < 0)
337 return;
338
339 if (argc < 3) {
340 printf("You have to specify 0=disable or 1=enable\r\n");
341 return;
342 }
343 ncn8025_get(slotnr, &settings);
344 if (atoi(argv[2]))
345 settings.led = true;
346 else
347 settings.led = false;
348 ncn8025_set(slotnr, &settings);
349}
350
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200351DEFUN(sim_atr, cmd_sim_atr, "sim-atr", "Read ATR from SIM card")
352{
353 struct ncn8025_settings settings;
354 int slotnr = validate_slotnr(argc, argv, 1);
Harald Welte1b9a5b82019-02-24 23:04:45 +0100355
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200356 if (slotnr < 0 || slotnr >= ARRAY_SIZE(SIM_peripheral_descriptors) || NULL == SIM_peripheral_descriptors[slotnr]) {
357 return;
358 }
359
360 // check if card is present (and read current settings)
361 ncn8025_get(slotnr, &settings);
362 if (!settings.simpres) {
363 printf("no card present in slot %d, aborting\r\n", slotnr);
364 return;
365 }
366
367 // switch card off (assert reset and disable power)
368 // note: ISO/IEC 7816-3:2006 section 6.4 provides the deactivation sequence, but not the minimum corresponding times
369 settings.rstin = true;
370 settings.cmdvcc = false;
Harald Weltedcf57832019-04-17 17:29:41 +0200371 settings.led = true;
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200372 ncn8025_set(slotnr, &settings);
373
374 // TODO wait some time for card to be completely deactivated
375 usart_async_flush_rx_buffer(SIM_peripheral_descriptors[slotnr]); // flush RX buffer to start from scratch
376
Kévin Redon0f050722019-05-02 15:56:25 +0200377
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200378 // set clock to lowest frequency (20 MHz / 8 = 2.5 MHz)
379 // note: according to ISO/IEC 7816-3:2006 section 5.2.3 the minimum value is 1 MHz, and maximum is 5 MHz during activation
380 settings.clkdiv = SIM_CLKDIV_8;
Kévin Redon0f050722019-05-02 15:56:25 +0200381 // set USART baud rate to match the interface (f = 2.5 MHz) and card default settings (Fd = 372, Dd = 1)
382 slot_set_isorate(slotnr, settings.clkdiv, ISO7816_3_DEFAULT_FD, ISO7816_3_DEFAULT_DD);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200383 // set card voltage to 3.0 V (the most supported)
384 // note: according to ISO/IEC 7816-3:2006 no voltage should damage the card, and you should cycle from low to high
385 settings.vsel = SIM_VOLT_3V0;
386 // provide power (the NCN8025 should perform the activation according to spec)
387 // note: activation sequence is documented in ISO/IEC 7816-3:2006 section 6.2
388 settings.cmdvcc = true;
389 ncn8025_set(slotnr, &settings);
390
391 // wait for Tb=400 cycles before re-asserting reset
392 delay_us(400 * 10000 / 2500); // 400 cycles * 1000 for us, 2.5 MHz / 1000 for us
393
394 // de-assert reset to switch card back on
395 settings.rstin = false;
396 ncn8025_set(slotnr, &settings);
397
398 // wait for Tc=40000 cycles for transmission to start
399 uint32_t cycles = 40000;
400 while (cycles && !usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
401 delay_us(10);
402 cycles -= 25; // 10 us = 25 cycles at 2.5 MHz
403 }
404 if (!usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
405 delay_us(12 * 372 / 1 / 2); // wait more than one byte (approximate freq down to 2 MHz)
406 }
407 // verify if one byte has been received
408 if (!usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
409 printf("card in slot %d is not responding, aborting\r\n", slotnr);
410 return;
411 }
412
413 // read ATR (just do it until there is no traffic anymore)
414 // TODO the ATR should be parsed to read the right number of bytes
Harald Welte07725812019-04-17 17:30:28 +0200415 printf("(%d) ATR: ", slotnr);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200416 uint8_t atr_byte;
417 while (usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
418 if (1 == io_read(&SIM_peripheral_descriptors[slotnr]->io, &atr_byte, 1)) {
419 printf("%02x ", atr_byte);
420 }
421 uint16_t wt = 9600; // waiting time in ETU
422 while (wt && !usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
423 delay_us(149); // wait for 1 ETU (372 / 1 / 2.5 MHz = 148.8 us)
424 wt--;
425 }
426 }
427 printf("\r\n");
Harald Weltedcf57832019-04-17 17:29:41 +0200428
429 /* disable VCC and LED, re-enable RST */
430 settings.cmdvcc = false;
431 settings.rstin = true;
432 settings.led = false;
433 ncn8025_set(slotnr, &settings);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200434}
Harald Welte1b9a5b82019-02-24 23:04:45 +0100435
Harald Welte67b2aba2019-04-16 20:47:22 +0200436extern void testmode_init(void);
Harald Welte1b9a5b82019-02-24 23:04:45 +0100437
Kévin Redon69b92d92019-01-24 16:39:20 +0100438int main(void)
439{
440 atmel_start_init();
Kévin Redon78d2f442019-01-24 18:45:59 +0100441
Kévin Redon8e538002019-01-30 11:19:19 +0100442 usb_start();
443
Harald Weltec3f170d2019-02-24 09:06:59 +0100444 board_init();
Harald Welteff9f4ce2019-02-24 22:51:09 +0100445 command_init("sysmoOCTSIM> ");
Harald Welte1b9a5b82019-02-24 23:04:45 +0100446 command_register(&cmd_sim_status);
447 command_register(&cmd_sim_power);
448 command_register(&cmd_sim_reset);
449 command_register(&cmd_sim_clkdiv);
450 command_register(&cmd_sim_voltage);
451 command_register(&cmd_sim_led);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200452 command_register(&cmd_sim_atr);
Harald Welte67b2aba2019-04-16 20:47:22 +0200453 testmode_init();
Harald Weltec3f170d2019-02-24 09:06:59 +0100454
Harald Welte361ed202019-02-24 21:15:39 +0100455 printf("\r\n\r\nsysmocom sysmoOCTSIM\r\n");
Harald Weltee7aa5342019-04-16 21:11:14 +0200456
457 command_print_prompt();
Kévin Redon8e538002019-01-30 11:19:19 +0100458 while (true) { // main loop
Harald Welteff9f4ce2019-02-24 22:51:09 +0100459 command_try_recv();
Kévin Redon8e538002019-01-30 11:19:19 +0100460 }
Kévin Redon69b92d92019-01-24 16:39:20 +0100461}