/* Card (ICC) UART driver for the Atmel ASF4 asynchronous USART */ #include #include #include #include #include #include #include "driver_init.h" #include "ncn8025.h" #include "cuart.h" #ifndef ENABLE_DBG_UART7 static struct usart_async_descriptor* SIM_peripheral_descriptors[] = {&SIM0, &SIM1, &SIM2, &SIM3, &SIM4, &SIM5, &SIM6, &SIM7}; #else static struct usart_async_descriptor* SIM_peripheral_descriptors[] = {&SIM0, &SIM1, &SIM2, &SIM3, &SIM4, &SIM5, &SIM6, NULL}; #endif extern struct card_uart *cuart4slot_nr(uint8_t slot_nr); /*********************************************************************** * low-level helper routines ***********************************************************************/ static void _SIM_rx_cb(const struct usart_async_descriptor *const io_descr, uint8_t slot_nr) { struct card_uart *cuart = cuart4slot_nr(slot_nr); int rc; OSMO_ASSERT(cuart); if (cuart->rx_threshold == 1) { /* bypass ringbuffer and report byte directly */ uint8_t rx[1]; rc = io_read((struct io_descriptor * const)&io_descr->io, rx, sizeof(rx)); OSMO_ASSERT(rc == sizeof(rx)); card_uart_notification(cuart, CUART_E_RX_SINGLE, rx); } else { /* go via ringbuffer and notify only after threshold */ if (ringbuffer_num(&io_descr->rx) >= cuart->rx_threshold) card_uart_notification(cuart, CUART_E_RX_COMPLETE, NULL); } } static void _SIM_tx_cb(const struct usart_async_descriptor *const io_descr, uint8_t slot_nr) { struct card_uart *cuart = cuart4slot_nr(slot_nr); OSMO_ASSERT(cuart); card_uart_notification(cuart, CUART_E_TX_COMPLETE, io_descr->tx_buffer); } #include #include static void _SIM_error_cb(const struct usart_async_descriptor *const io_descr, uint8_t slot_nr) { struct card_uart *cuart = cuart4slot_nr(slot_nr); OSMO_ASSERT(cuart); card_uart_notification(cuart, CUART_E_HW_ERROR, 0); } /* the below ugli-ness is required as the usart_async_descriptor doesn't have * some kind of 'private' member that could provide the call-back anty kind of * context */ static void SIM0_rx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_rx_cb(io_descr, 0); } static void SIM1_rx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_rx_cb(io_descr, 1); } static void SIM2_rx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_rx_cb(io_descr, 2); } static void SIM3_rx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_rx_cb(io_descr, 3); } static void SIM4_rx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_rx_cb(io_descr, 4); } static void SIM5_rx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_rx_cb(io_descr, 5); } static void SIM6_rx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_rx_cb(io_descr, 6); } static void SIM7_rx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_rx_cb(io_descr, 7); } static usart_cb_t SIM_rx_cb[8] = { SIM0_rx_cb, SIM1_rx_cb, SIM2_rx_cb, SIM3_rx_cb, SIM4_rx_cb, SIM5_rx_cb, SIM6_rx_cb, SIM7_rx_cb, }; static void SIM0_tx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_tx_cb(io_descr, 0); } static void SIM1_tx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_tx_cb(io_descr, 1); } static void SIM2_tx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_tx_cb(io_descr, 2); } static void SIM3_tx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_tx_cb(io_descr, 3); } static void SIM4_tx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_tx_cb(io_descr, 4); } static void SIM5_tx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_tx_cb(io_descr, 5); } static void SIM6_tx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_tx_cb(io_descr, 6); } static void SIM7_tx_cb(const struct usart_async_descriptor *const io_descr) { _SIM_tx_cb(io_descr, 7); } static usart_cb_t SIM_tx_cb[8] = { SIM0_tx_cb, SIM1_tx_cb, SIM2_tx_cb, SIM3_tx_cb, SIM4_tx_cb, SIM5_tx_cb, SIM6_tx_cb, SIM7_tx_cb, }; static void SIM0_error_cb(const struct usart_async_descriptor *const io_descr) { _SIM_error_cb(io_descr, 0); } static void SIM1_error_cb(const struct usart_async_descriptor *const io_descr) { _SIM_error_cb(io_descr, 1); } static void SIM2_error_cb(const struct usart_async_descriptor *const io_descr) { _SIM_error_cb(io_descr, 2); } static void SIM3_error_cb(const struct usart_async_descriptor *const io_descr) { _SIM_error_cb(io_descr, 3); } static void SIM4_error_cb(const struct usart_async_descriptor *const io_descr) { _SIM_error_cb(io_descr, 4); } static void SIM5_error_cb(const struct usart_async_descriptor *const io_descr) { _SIM_error_cb(io_descr, 5); } static void SIM6_error_cb(const struct usart_async_descriptor *const io_descr) { _SIM_error_cb(io_descr, 6); } static void SIM7_error_cb(const struct usart_async_descriptor *const io_descr) { _SIM_error_cb(io_descr, 7); } static usart_cb_t SIM_error_cb[8] = { SIM0_error_cb, SIM1_error_cb, SIM2_error_cb, SIM3_error_cb, SIM4_error_cb, SIM5_error_cb, SIM6_error_cb, SIM7_error_cb, }; #include #include "atmel_start.h" #include "atmel_start_pins.h" #include "config/hpl_gclk_config.h" #include "iso7816_3.h" /** possible clock sources for the SERCOM peripheral * warning: the definition must match the GCLK configuration */ static const uint8_t sercom_glck_sources[] = {GCLK_PCHCTRL_GEN_GCLK2_Val, GCLK_PCHCTRL_GEN_GCLK4_Val, GCLK_PCHCTRL_GEN_GCLK6_Val}; /** possible clock frequencies in MHz for the SERCOM peripheral * warning: the definition must match the GCLK configuration */ static const double sercom_glck_freqs[] = {100E6 / CONF_GCLK_GEN_2_DIV, 100E6 / CONF_GCLK_GEN_4_DIV, 120E6 / CONF_GCLK_GEN_6_DIV}; /** the GCLK ID for the SERCOM SIM peripherals * @note: used as index for PCHCTRL */ static 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}; /** inverted signalling as per 7816-3 : inverted bit, inverted bit order */ static void set_inverted_signalling(void* hw, bool on) { hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); hri_sercomusart_write_CTRLA_DORD_bit(hw, !on); // inverted == msb first hri_sercomusart_write_CTRLA_TXINV_bit(hw, on); hri_sercomusart_write_CTRLA_RXINV_bit(hw, on); hri_sercomusart_write_CTRLB_PMODE_bit(hw, on); // inverted == even parity hri_sercomusart_set_CTRLA_ENABLE_bit(hw); } /** change baud rate of card slot * @param[in] slotnr slot number for which the baud rate should be set * @param[in] baudrate baud rate in bps to set * @return if the baud rate has been set, else a parameter is out of range */ static bool slot_set_baudrate(struct card_uart *cuart, uint32_t baudrate) { uint8_t slotnr = cuart->u.asf4.slot_nr; struct usart_async_descriptor* slot = SIM_peripheral_descriptors[slotnr]; Sercom *sercom = cuart->u.asf4.usa_pd->device.hw; ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors)); if (NULL == slot) { return false; } // calculate the error corresponding to the clock sources uint16_t bauds[ARRAY_SIZE(sercom_glck_freqs)]; double errors[ARRAY_SIZE(sercom_glck_freqs)]; for (uint8_t i = 0; i < ARRAY_SIZE(sercom_glck_freqs); i++) { double freq = sercom_glck_freqs[i]; // remember possible SERCOM frequency uint32_t min = freq/16. * (1. - 65535. / 65536.); // calculate the minimum baud rate for this frequency uint32_t max = freq/16. * (1. - 1. / 65536.); // calculate the maximum baud rate for this frequency if (baudrate < min || baudrate > max) { // baud rate it out of supported range errors[i] = NAN; } else { uint16_t baud = round(65536. * (1. - 16. * (baudrate/freq))); bauds[i] = baud; double actual = freq/16. * (1. - baud / 65536.); errors[i] = fabs(1.0 - (actual / baudrate)); } } // find the smallest error uint8_t best = ARRAY_SIZE(sercom_glck_freqs); for (uint8_t i = 0; i < ARRAY_SIZE(sercom_glck_freqs); i++) { if (isnan(errors[i])) { continue; } if (best >= ARRAY_SIZE(sercom_glck_freqs)) { best = i; } else if (errors[i] < errors[best]) { best = i; } } if (best >= ARRAY_SIZE(sercom_glck_freqs)) { // found no clock supporting this baud rate return false; } // update cached values cuart->u.asf4.current_baudrate = baudrate; cuart->u.asf4.extrawait_after_rx = 1./baudrate * 1000 * 1000; 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]); /* only wait if the uart is enabled.... */ if (hri_sercomusart_get_CTRLA_reg(sercom, SERCOM_USART_CTRLA_ENABLE)) { while (!usart_async_is_tx_empty(slot)); // wait for transmission to complete (WARNING no timeout) usart_async_disable(slot); // disable SERCOM peripheral } hri_gclk_clear_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], (1 << GCLK_PCHCTRL_CHEN_Pos)); // disable clock for this peripheral while (hri_gclk_get_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], (1 << GCLK_PCHCTRL_CHEN_Pos))); // wait until clock is really disabled // it does not seem we need to completely disable the peripheral using hri_mclk_clear_APBDMASK_SERCOMn_bit 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 usart_async_set_baud_rate(slot, bauds[best]); // set the new baud rate /* clear pending errors that happened while * - the interrupt was off (inverse ATR? -> parity error) * -- OR -- * - the uart was disabled due to a hw error * and enable it*/ hri_sercomusart_clear_interrupt_ERROR_bit(sercom); hri_sercomusart_clear_STATUS_reg(sercom, 0xff); volatile uint8_t dummy = hri_sercomusart_read_RXERRCNT_reg(sercom); usart_async_flush_rx_buffer(cuart->u.asf4.usa_pd); usart_async_enable(slot); // re-enable SERCOM peripheral return true; } /** change ISO baud rate of card slot * @param[in] slotnr slot number for which the baud rate should be set * @param[in] clkdiv can clock divider * @param[in] f clock rate conversion integer F * @param[in] d baud rate adjustment factor D * @return if the baud rate has been set, else a parameter is out of range */ static bool slot_set_isorate(struct card_uart *cuart, enum ncn8025_sim_clkdiv clkdiv, uint16_t f, uint8_t d) { uint8_t slotnr = cuart->u.asf4.slot_nr; struct usart_async_descriptor* slot = SIM_peripheral_descriptors[slotnr]; // input checks ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors)); if (clkdiv != SIM_CLKDIV_1 && clkdiv != SIM_CLKDIV_2 && clkdiv != SIM_CLKDIV_4 && clkdiv != SIM_CLKDIV_8) { return false; } if (!iso7816_3_valid_f(f)) { return false; } if (!iso7816_3_valid_d(d)) { return false; } // set clockdiv struct ncn8025_settings settings; ncn8025_get(slotnr, &settings); if (settings.clkdiv != clkdiv) { settings.clkdiv = clkdiv; ncn8025_set(slotnr, &settings); } // calculate desired frequency uint32_t freq = 20000000UL; // maximum frequency switch (clkdiv) { case SIM_CLKDIV_1: freq /= 1; break; case SIM_CLKDIV_2: freq /= 2; break; case SIM_CLKDIV_4: freq /= 4; break; case SIM_CLKDIV_8: freq /= 8; break; } /* error interrupt off after reset due to possbile inverted atr and accompanying parity error * this was automatically enabled during error callback registration */ hri_sercomusart_write_INTEN_ERROR_bit(slot->device.hw, 0); set_inverted_signalling(slot->device.hw, false); // set baud rate uint32_t baudrate = (freq * d) / f; // calculate actual baud rate return slot_set_baudrate(cuart, baudrate); // set baud rate } /*********************************************************************** * Interface with card_uart (cuart) core ***********************************************************************/ /* forward-declaration */ static struct card_uart_driver asf4_usart_driver; static int asf4_usart_close(struct card_uart *cuart); static int asf4_usart_open(struct card_uart *cuart, const char *device_name) { struct usart_async_descriptor *usa_pd; int slot_nr = atoi(device_name); if (slot_nr >= ARRAY_SIZE(SIM_peripheral_descriptors)) return -ENODEV; usa_pd = SIM_peripheral_descriptors[slot_nr]; if (!usa_pd) return -ENODEV; cuart->u.asf4.usa_pd = usa_pd; cuart->u.asf4.slot_nr = slot_nr; usart_async_register_callback(usa_pd, USART_ASYNC_RXC_CB, SIM_rx_cb[slot_nr]); usart_async_register_callback(usa_pd, USART_ASYNC_TXC_CB, SIM_tx_cb[slot_nr]); usart_async_register_callback(usa_pd, USART_ASYNC_ERROR_CB, SIM_error_cb[slot_nr]); usart_async_enable(usa_pd); // set USART baud rate to match the interface (f = 2.5 MHz) and card default settings (Fd = 372, Dd = 1) slot_set_isorate(cuart, SIM_CLKDIV_8, ISO7816_3_DEFAULT_FD, ISO7816_3_DEFAULT_DD); return 0; } static int asf4_usart_close(struct card_uart *cuart) { struct usart_async_descriptor *usa_pd = cuart->u.asf4.usa_pd; OSMO_ASSERT(cuart->driver == &asf4_usart_driver); usart_async_disable(usa_pd); return 0; } static int asf4_usart_async_tx(struct card_uart *cuart, const uint8_t *data, size_t len) { struct usart_async_descriptor *usa_pd = cuart->u.asf4.usa_pd; int rc; OSMO_ASSERT(cuart->driver == &asf4_usart_driver); OSMO_ASSERT(usart_async_is_tx_empty(usa_pd)); rc = io_write(&usa_pd->io, data, len); if (rc < 0) return rc; cuart->tx_busy = true; return rc; } static int asf4_usart_async_rx(struct card_uart *cuart, uint8_t *data, size_t len) { struct usart_async_descriptor *usa_pd = cuart->u.asf4.usa_pd; OSMO_ASSERT(cuart->driver == &asf4_usart_driver); return io_read(&usa_pd->io, data, len); } #include "ccid_device.h" #include "iso7816_3.h" static int asf4_usart_ctrl(struct card_uart *cuart, enum card_uart_ctl ctl, int arg) { struct ncn8025_settings settings; Sercom *sercom = cuart->u.asf4.usa_pd->device.hw; switch (ctl) { case CUART_CTL_NO_RXTX: /* immediately disables the uart, useful for hw errors (parity, colllision, ...) * uart is automatically re-enabled during slot_set_isorate which also clears the errors * when resetting the slot which happens automatically during error callback handling or powerup * so the uart usually does not stay disabled for a long time */ if (arg) _usart_async_disable(&cuart->u.asf4.usa_pd->device); break; case CUART_CTL_RX: if (arg){ /* no op */ } else { delay_us(cuart->u.asf4.extrawait_after_rx); } break; case CUART_CTL_RST: ncn8025_get(cuart->u.asf4.slot_nr, &settings); settings.rstin = arg ? true : false; ncn8025_set(cuart->u.asf4.slot_nr, &settings); usart_async_flush_rx_buffer(cuart->u.asf4.usa_pd); /* reset everything, card reset resets pps params */ if (arg) slot_set_isorate(cuart, SIM_CLKDIV_8, ISO7816_3_DEFAULT_FD, ISO7816_3_DEFAULT_DD); break; case CUART_CTL_POWER_5V0: case CUART_CTL_POWER_3V0: case CUART_CTL_POWER_1V8: /* reset everything */ slot_set_isorate(cuart, SIM_CLKDIV_8, ISO7816_3_DEFAULT_FD, ISO7816_3_DEFAULT_DD); enum ncn8025_sim_voltage v = CUART_CTL_POWER_5V0; switch (ctl) { case CUART_CTL_POWER_5V0: v = SIM_VOLT_5V0; break; case CUART_CTL_POWER_3V0: v = SIM_VOLT_3V0; break; case CUART_CTL_POWER_1V8: v = SIM_VOLT_1V8; break; default: break; } ncn8025_get(cuart->u.asf4.slot_nr, &settings); settings.cmdvcc = arg ? true : false; settings.led = arg ? true : false; settings.vsel = v; ncn8025_set(cuart->u.asf4.slot_nr, &settings); break; case CUART_CTL_WTIME: /* no driver-specific handling of this */ break; case CUART_CTL_CLOCK: /* no clock stop support */ break; case CUART_CTL_SET_CLOCK_FREQ: ncn8025_get(cuart->u.asf4.slot_nr, &settings); /* 2,5/5/10/20 supported by dividers */ enum ncn8025_sim_clkdiv clkdiv = SIM_CLKDIV_1; if(arg < 20000000) clkdiv = SIM_CLKDIV_2; if(arg < 10000000) clkdiv = SIM_CLKDIV_4; if(arg < 5000000) clkdiv = SIM_CLKDIV_8; settings.clkdiv = clkdiv; ncn8025_set(cuart->u.asf4.slot_nr, &settings); break; case CUART_CTL_SET_FD: ncn8025_get(cuart->u.asf4.slot_nr, &settings); uint8_t divider = ncn8025_div_val[settings.clkdiv]; uint32_t baudrate = (20e6/divider)/arg; slot_set_baudrate(cuart, baudrate); break; case CUART_CTL_GET_BAUDRATE: return cuart->u.asf4.current_baudrate; break; case CUART_CTL_GET_CLOCK_FREQ: ncn8025_get(cuart->u.asf4.slot_nr, &settings); return 20e6 / ncn8025_div_val[settings.clkdiv]; break; case CUART_CTL_ERROR_AND_INV: set_inverted_signalling(sercom, arg); /* clear pending errors that happened while the interrupt was off (ATR) and enable it*/ hri_sercomusart_clear_interrupt_ERROR_bit(sercom); hri_sercomusart_clear_STATUS_reg(sercom, 0xff); volatile uint8_t dummy = hri_sercomusart_read_RXERRCNT_reg(sercom); hri_sercomusart_set_INTEN_ERROR_bit(sercom); break; default: return 0; } return 0; } static const struct card_uart_ops asf4_usart_ops = { .open = asf4_usart_open, .close = asf4_usart_close, .async_tx = asf4_usart_async_tx, .async_rx = asf4_usart_async_rx, .ctrl = asf4_usart_ctrl, }; static struct card_uart_driver asf4_usart_driver = { .name = "asf4", .ops = &asf4_usart_ops, }; static __attribute__((constructor)) void on_dso_load_cuart_asf4(void) { card_uart_driver_register(&asf4_usart_driver); }