Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tty/serial: atmel: add ISO7816 support

When mode is set in atmel_config_iso7816() we backup last RS232 mode
for coming back to this mode if requested.
Also allow setup of T=0 and T=1 parameter and basic support in set_termios
function as well.

Signed-off-by: Nicolas Ferre <nicolas.ferre@microchip.com>
[ludovic.desroches@microchip.com: rebase, add check on fidi ratio, checkpatch fixes]
Signed-off-by: Ludovic Desroches <ludovic.desroches@microchip.com>
Acked-by: Richard Genoud <richard.genoud@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Nicolas Ferre and committed by
Greg Kroah-Hartman
377fedd1 ad8c0eaa

+181 -12
+179 -11
drivers/tty/serial/atmel_serial.c
··· 34 34 #include <linux/suspend.h> 35 35 #include <linux/mm.h> 36 36 37 + #include <asm/div64.h> 37 38 #include <asm/io.h> 38 39 #include <asm/ioctls.h> 39 40 ··· 148 147 struct circ_buf rx_ring; 149 148 150 149 struct mctrl_gpios *gpios; 150 + u32 backup_mode; /* MR saved during iso7816 operations */ 151 + u32 backup_brgr; /* BRGR saved during iso7816 operations */ 151 152 unsigned int tx_done_mask; 152 153 u32 fifo_size; 153 154 u32 rts_high; ··· 165 162 unsigned int pending; 166 163 unsigned int pending_status; 167 164 spinlock_t lock_suspended; 165 + 166 + /* ISO7816 */ 167 + unsigned int fidi_min; 168 + unsigned int fidi_max; 168 169 169 170 #ifdef CONFIG_PM 170 171 struct { ··· 368 361 return 0; 369 362 } 370 363 364 + static unsigned int atmel_calc_cd(struct uart_port *port, 365 + struct serial_iso7816 *iso7816conf) 366 + { 367 + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); 368 + unsigned int cd; 369 + u64 mck_rate; 370 + 371 + mck_rate = (u64)clk_get_rate(atmel_port->clk); 372 + do_div(mck_rate, iso7816conf->clk); 373 + cd = mck_rate; 374 + return cd; 375 + } 376 + 377 + static unsigned int atmel_calc_fidi(struct uart_port *port, 378 + struct serial_iso7816 *iso7816conf) 379 + { 380 + u64 fidi = 0; 381 + 382 + if (iso7816conf->sc_fi && iso7816conf->sc_di) { 383 + fidi = (u64)iso7816conf->sc_fi; 384 + do_div(fidi, iso7816conf->sc_di); 385 + } 386 + return (u32)fidi; 387 + } 388 + 389 + /* Enable or disable the iso7816 support */ 390 + /* Called with interrupts disabled */ 391 + static int atmel_config_iso7816(struct uart_port *port, 392 + struct serial_iso7816 *iso7816conf) 393 + { 394 + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); 395 + unsigned int mode; 396 + unsigned int cd, fidi; 397 + int ret = 0; 398 + 399 + /* Disable interrupts */ 400 + atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask); 401 + 402 + mode = atmel_uart_readl(port, ATMEL_US_MR); 403 + 404 + if (iso7816conf->flags & SER_ISO7816_ENABLED) { 405 + mode &= ~ATMEL_US_USMODE; 406 + 407 + if (iso7816conf->tg > 255) { 408 + dev_err(port->dev, "ISO7816: Timeguard exceeding 255\n"); 409 + memset(iso7816conf, 0, sizeof(struct serial_iso7816)); 410 + ret = -EINVAL; 411 + goto err_out; 412 + } 413 + 414 + if ((iso7816conf->flags & SER_ISO7816_T_PARAM) 415 + == SER_ISO7816_T(0)) { 416 + mode |= ATMEL_US_USMODE_ISO7816_T0 | ATMEL_US_DSNACK; 417 + } else if ((iso7816conf->flags & SER_ISO7816_T_PARAM) 418 + == SER_ISO7816_T(1)) { 419 + mode |= ATMEL_US_USMODE_ISO7816_T1 | ATMEL_US_INACK; 420 + } else { 421 + dev_err(port->dev, "ISO7816: Type not supported\n"); 422 + memset(iso7816conf, 0, sizeof(struct serial_iso7816)); 423 + ret = -EINVAL; 424 + goto err_out; 425 + } 426 + 427 + mode &= ~(ATMEL_US_USCLKS | ATMEL_US_NBSTOP | ATMEL_US_PAR); 428 + 429 + /* select mck clock, and output */ 430 + mode |= ATMEL_US_USCLKS_MCK | ATMEL_US_CLKO; 431 + /* set parity for normal/inverse mode + max iterations */ 432 + mode |= ATMEL_US_PAR_EVEN | ATMEL_US_NBSTOP_1 | ATMEL_US_MAX_ITER(3); 433 + 434 + cd = atmel_calc_cd(port, iso7816conf); 435 + fidi = atmel_calc_fidi(port, iso7816conf); 436 + if (fidi == 0) { 437 + dev_warn(port->dev, "ISO7816 fidi = 0, Generator generates no signal\n"); 438 + } else if (fidi < atmel_port->fidi_min 439 + || fidi > atmel_port->fidi_max) { 440 + dev_err(port->dev, "ISO7816 fidi = %u, value not supported\n", fidi); 441 + memset(iso7816conf, 0, sizeof(struct serial_iso7816)); 442 + ret = -EINVAL; 443 + goto err_out; 444 + } 445 + 446 + if (!(port->iso7816.flags & SER_ISO7816_ENABLED)) { 447 + /* port not yet in iso7816 mode: store configuration */ 448 + atmel_port->backup_mode = atmel_uart_readl(port, ATMEL_US_MR); 449 + atmel_port->backup_brgr = atmel_uart_readl(port, ATMEL_US_BRGR); 450 + } 451 + 452 + atmel_uart_writel(port, ATMEL_US_TTGR, iso7816conf->tg); 453 + atmel_uart_writel(port, ATMEL_US_BRGR, cd); 454 + atmel_uart_writel(port, ATMEL_US_FIDI, fidi); 455 + 456 + atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS | ATMEL_US_RXEN); 457 + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY | ATMEL_US_NACK | ATMEL_US_ITERATION; 458 + } else { 459 + dev_dbg(port->dev, "Setting UART back to RS232\n"); 460 + /* back to last RS232 settings */ 461 + mode = atmel_port->backup_mode; 462 + memset(iso7816conf, 0, sizeof(struct serial_iso7816)); 463 + atmel_uart_writel(port, ATMEL_US_TTGR, 0); 464 + atmel_uart_writel(port, ATMEL_US_BRGR, atmel_port->backup_brgr); 465 + atmel_uart_writel(port, ATMEL_US_FIDI, 0x174); 466 + 467 + if (atmel_use_pdc_tx(port)) 468 + atmel_port->tx_done_mask = ATMEL_US_ENDTX | 469 + ATMEL_US_TXBUFE; 470 + else 471 + atmel_port->tx_done_mask = ATMEL_US_TXRDY; 472 + } 473 + 474 + port->iso7816 = *iso7816conf; 475 + 476 + atmel_uart_writel(port, ATMEL_US_MR, mode); 477 + 478 + err_out: 479 + /* Enable interrupts */ 480 + atmel_uart_writel(port, ATMEL_US_IER, atmel_port->tx_done_mask); 481 + 482 + return ret; 483 + } 484 + 371 485 /* 372 486 * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. 373 487 */ ··· 608 480 /* Disable interrupts */ 609 481 atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask); 610 482 611 - if ((port->rs485.flags & SER_RS485_ENABLED) && 612 - !(port->rs485.flags & SER_RS485_RX_DURING_TX)) 483 + if (((port->rs485.flags & SER_RS485_ENABLED) && 484 + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) || 485 + port->iso7816.flags & SER_ISO7816_ENABLED) 613 486 atmel_start_rx(port); 614 487 } 615 488 ··· 628 499 return; 629 500 630 501 if (atmel_use_pdc_tx(port) || atmel_use_dma_tx(port)) 631 - if ((port->rs485.flags & SER_RS485_ENABLED) && 632 - !(port->rs485.flags & SER_RS485_RX_DURING_TX)) 502 + if (((port->rs485.flags & SER_RS485_ENABLED) && 503 + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) || 504 + port->iso7816.flags & SER_ISO7816_ENABLED) 633 505 atmel_stop_rx(port); 634 506 635 507 if (atmel_use_pdc_tx(port)) ··· 928 798 */ 929 799 if (!uart_circ_empty(xmit)) 930 800 atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx); 931 - else if ((port->rs485.flags & SER_RS485_ENABLED) && 932 - !(port->rs485.flags & SER_RS485_RX_DURING_TX)) { 801 + else if (((port->rs485.flags & SER_RS485_ENABLED) && 802 + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) || 803 + port->iso7816.flags & SER_ISO7816_ENABLED) { 933 804 /* DMA done, stop TX, start RX for RS485 */ 934 805 atmel_start_rx(port); 935 806 } ··· 1413 1282 wake_up_interruptible(&port->state->port.delta_msr_wait); 1414 1283 } 1415 1284 } 1285 + 1286 + if (pending & (ATMEL_US_NACK | ATMEL_US_ITERATION)) 1287 + dev_dbg(port->dev, "ISO7816 ERROR (0x%08x)\n", pending); 1416 1288 } 1417 1289 1418 1290 /* ··· 1508 1374 atmel_uart_writel(port, ATMEL_US_IER, 1509 1375 atmel_port->tx_done_mask); 1510 1376 } else { 1511 - if ((port->rs485.flags & SER_RS485_ENABLED) && 1512 - !(port->rs485.flags & SER_RS485_RX_DURING_TX)) { 1377 + if (((port->rs485.flags & SER_RS485_ENABLED) && 1378 + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) || 1379 + port->iso7816.flags & SER_ISO7816_ENABLED) { 1513 1380 /* DMA done, stop TX, start RX for RS485 */ 1514 1381 atmel_start_rx(port); 1515 1382 } ··· 1862 1727 atmel_port->has_frac_baudrate = true; 1863 1728 atmel_port->has_hw_timer = true; 1864 1729 atmel_port->rtor = ATMEL_US_RTOR; 1730 + version = atmel_uart_readl(port, ATMEL_US_VERSION); 1731 + switch (version) { 1732 + case 0x814: /* sama5d2 */ 1733 + /* fall through */ 1734 + case 0x701: /* sama5d4 */ 1735 + atmel_port->fidi_min = 3; 1736 + atmel_port->fidi_max = 65535; 1737 + break; 1738 + case 0x502: /* sam9x5, sama5d3 */ 1739 + atmel_port->fidi_min = 3; 1740 + atmel_port->fidi_max = 2047; 1741 + break; 1742 + default: 1743 + atmel_port->fidi_min = 1; 1744 + atmel_port->fidi_max = 2047; 1745 + } 1865 1746 } else if (name == dbgu_uart) { 1866 1747 dev_dbg(port->dev, "Dbgu or uart without hw timer\n"); 1867 1748 } else { ··· 2251 2100 atmel_uart_writel(port, ATMEL_US_TTGR, 2252 2101 port->rs485.delay_rts_after_send); 2253 2102 mode |= ATMEL_US_USMODE_RS485; 2103 + } else if (port->iso7816.flags & SER_ISO7816_ENABLED) { 2104 + atmel_uart_writel(port, ATMEL_US_TTGR, port->iso7816.tg); 2105 + /* select mck clock, and output */ 2106 + mode |= ATMEL_US_USCLKS_MCK | ATMEL_US_CLKO; 2107 + /* set max iterations */ 2108 + mode |= ATMEL_US_MAX_ITER(3); 2109 + if ((port->iso7816.flags & SER_ISO7816_T_PARAM) 2110 + == SER_ISO7816_T(0)) 2111 + mode |= ATMEL_US_USMODE_ISO7816_T0; 2112 + else 2113 + mode |= ATMEL_US_USMODE_ISO7816_T1; 2254 2114 } else if (termios->c_cflag & CRTSCTS) { 2255 2115 /* RS232 with hardware handshake (RTS/CTS) */ 2256 2116 if (atmel_use_fifo(port) && ··· 2338 2176 } 2339 2177 quot = cd | fp << ATMEL_US_FP_OFFSET; 2340 2178 2341 - atmel_uart_writel(port, ATMEL_US_BRGR, quot); 2179 + if (!(port->iso7816.flags & SER_ISO7816_ENABLED)) 2180 + atmel_uart_writel(port, ATMEL_US_BRGR, quot); 2342 2181 atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); 2343 2182 atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN); 2344 2183 atmel_port->tx_stopped = false; ··· 2520 2357 port->mapbase = mpdev->resource[0].start; 2521 2358 port->irq = mpdev->resource[1].start; 2522 2359 port->rs485_config = atmel_config_rs485; 2360 + port->iso7816_config = atmel_config_iso7816; 2523 2361 port->membase = NULL; 2524 2362 2525 2363 memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring)); ··· 2544 2380 /* only enable clock when USART is in use */ 2545 2381 } 2546 2382 2547 - /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */ 2548 - if (port->rs485.flags & SER_RS485_ENABLED) 2383 + /* 2384 + * Use TXEMPTY for interrupt when rs485 or ISO7816 else TXRDY or 2385 + * ENDTX|TXBUFE 2386 + */ 2387 + if (port->rs485.flags & SER_RS485_ENABLED || 2388 + port->iso7816.flags & SER_ISO7816_ENABLED) 2549 2389 atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; 2550 2390 else if (atmel_use_pdc_tx(port)) { 2551 2391 port->fifosize = PDC_BUFFER_SIZE;
+2 -1
drivers/tty/serial/atmel_serial.h
··· 78 78 #define ATMEL_US_OVER BIT(19) /* Oversampling Mode */ 79 79 #define ATMEL_US_INACK BIT(20) /* Inhibit Non Acknowledge */ 80 80 #define ATMEL_US_DSNACK BIT(21) /* Disable Successive NACK */ 81 - #define ATMEL_US_MAX_ITER GENMASK(26, 24) /* Max Iterations */ 81 + #define ATMEL_US_MAX_ITER_MASK GENMASK(26, 24) /* Max Iterations */ 82 + #define ATMEL_US_MAX_ITER(n) (((n) << 24) & ATMEL_US_MAX_ITER_MASK) 82 83 #define ATMEL_US_FILTER BIT(28) /* Infrared Receive Line Filter */ 83 84 84 85 #define ATMEL_US_IER 0x08 /* Interrupt Enable Register */