Skip to content

Commit 5da25c6

Browse files
noglitchldesroches
authored andcommitted
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>
1 parent 99e406d commit 5da25c6

2 files changed

Lines changed: 181 additions & 12 deletions

File tree

drivers/tty/serial/atmel_serial.c

Lines changed: 179 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include <linux/suspend.h>
4949
#include <linux/mm.h>
5050

51+
#include <asm/div64.h>
5152
#include <asm/io.h>
5253
#include <asm/ioctls.h>
5354

@@ -161,6 +162,8 @@ struct atmel_uart_port {
161162
struct circ_buf rx_ring;
162163

163164
struct mctrl_gpios *gpios;
165+
u32 backup_mode; /* MR saved during iso7816 operations */
166+
u32 backup_brgr; /* BRGR saved during iso7816 operations */
164167
unsigned int tx_done_mask;
165168
u32 fifo_size;
166169
u32 rts_high;
@@ -176,6 +179,10 @@ struct atmel_uart_port {
176179
unsigned int pending_status;
177180
spinlock_t lock_suspended;
178181

182+
/* ISO7816 */
183+
unsigned int fidi_min;
184+
unsigned int fidi_max;
185+
179186
#ifdef CONFIG_PM
180187
struct {
181188
u32 cr;
@@ -375,6 +382,127 @@ static int atmel_config_rs485(struct uart_port *port,
375382
return 0;
376383
}
377384

385+
static unsigned int atmel_calc_cd(struct uart_port *port,
386+
struct serial_iso7816 *iso7816conf)
387+
{
388+
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
389+
unsigned int cd;
390+
u64 mck_rate;
391+
392+
mck_rate = (u64)clk_get_rate(atmel_port->clk);
393+
do_div(mck_rate, iso7816conf->clk);
394+
cd = mck_rate;
395+
return cd;
396+
}
397+
398+
static unsigned int atmel_calc_fidi(struct uart_port *port,
399+
struct serial_iso7816 *iso7816conf)
400+
{
401+
u64 fidi = 0;
402+
403+
if (iso7816conf->sc_fi && iso7816conf->sc_di) {
404+
fidi = (u64)iso7816conf->sc_fi;
405+
do_div(fidi, iso7816conf->sc_di);
406+
}
407+
return (u32)fidi;
408+
}
409+
410+
/* Enable or disable the iso7816 support */
411+
/* Called with interrupts disabled */
412+
static int atmel_config_iso7816(struct uart_port *port,
413+
struct serial_iso7816 *iso7816conf)
414+
{
415+
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
416+
unsigned int mode;
417+
unsigned int cd, fidi;
418+
int ret = 0;
419+
420+
/* Disable interrupts */
421+
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
422+
423+
mode = atmel_uart_readl(port, ATMEL_US_MR);
424+
425+
if (iso7816conf->flags & SER_ISO7816_ENABLED) {
426+
mode &= ~ATMEL_US_USMODE;
427+
428+
if (iso7816conf->tg > 255) {
429+
dev_err(port->dev, "ISO7816: Timeguard exceeding 255\n");
430+
memset(iso7816conf, 0, sizeof(struct serial_iso7816));
431+
ret = -EINVAL;
432+
goto err_out;
433+
}
434+
435+
if ((iso7816conf->flags & SER_ISO7816_T_PARAM)
436+
== SER_ISO7816_T(0)) {
437+
mode |= ATMEL_US_USMODE_ISO7816_T0 | ATMEL_US_DSNACK;
438+
} else if ((iso7816conf->flags & SER_ISO7816_T_PARAM)
439+
== SER_ISO7816_T(1)) {
440+
mode |= ATMEL_US_USMODE_ISO7816_T1 | ATMEL_US_INACK;
441+
} else {
442+
dev_err(port->dev, "ISO7816: Type not supported\n");
443+
memset(iso7816conf, 0, sizeof(struct serial_iso7816));
444+
ret = -EINVAL;
445+
goto err_out;
446+
}
447+
448+
mode &= ~(ATMEL_US_USCLKS | ATMEL_US_NBSTOP | ATMEL_US_PAR);
449+
450+
/* select mck clock, and output */
451+
mode |= ATMEL_US_USCLKS_MCK | ATMEL_US_CLKO;
452+
/* set parity for normal/inverse mode + max iterations */
453+
mode |= ATMEL_US_PAR_EVEN | ATMEL_US_NBSTOP_1 | ATMEL_US_MAX_ITER(3);
454+
455+
cd = atmel_calc_cd(port, iso7816conf);
456+
fidi = atmel_calc_fidi(port, iso7816conf);
457+
if (fidi == 0) {
458+
dev_warn(port->dev, "ISO7816 fidi = 0, Generator generates no signal\n");
459+
} else if (fidi < atmel_port->fidi_min
460+
|| fidi > atmel_port->fidi_max) {
461+
dev_err(port->dev, "ISO7816 fidi = %u, value not supported\n", fidi);
462+
memset(iso7816conf, 0, sizeof(struct serial_iso7816));
463+
ret = -EINVAL;
464+
goto err_out;
465+
}
466+
467+
if (!(port->iso7816.flags & SER_ISO7816_ENABLED)) {
468+
/* port not yet in iso7816 mode: store configuration */
469+
atmel_port->backup_mode = atmel_uart_readl(port, ATMEL_US_MR);
470+
atmel_port->backup_brgr = atmel_uart_readl(port, ATMEL_US_BRGR);
471+
}
472+
473+
atmel_uart_writel(port, ATMEL_US_TTGR, iso7816conf->tg);
474+
atmel_uart_writel(port, ATMEL_US_BRGR, cd);
475+
atmel_uart_writel(port, ATMEL_US_FIDI, fidi);
476+
477+
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS | ATMEL_US_RXEN);
478+
atmel_port->tx_done_mask = ATMEL_US_TXEMPTY | ATMEL_US_NACK | ATMEL_US_ITERATION;
479+
} else {
480+
dev_dbg(port->dev, "Setting UART back to RS232\n");
481+
/* back to last RS232 settings */
482+
mode = atmel_port->backup_mode;
483+
memset(iso7816conf, 0, sizeof(struct serial_iso7816));
484+
atmel_uart_writel(port, ATMEL_US_TTGR, 0);
485+
atmel_uart_writel(port, ATMEL_US_BRGR, atmel_port->backup_brgr);
486+
atmel_uart_writel(port, ATMEL_US_FIDI, 0x174);
487+
488+
if (atmel_use_pdc_tx(port))
489+
atmel_port->tx_done_mask = ATMEL_US_ENDTX |
490+
ATMEL_US_TXBUFE;
491+
else
492+
atmel_port->tx_done_mask = ATMEL_US_TXRDY;
493+
}
494+
495+
port->iso7816 = *iso7816conf;
496+
497+
atmel_uart_writel(port, ATMEL_US_MR, mode);
498+
499+
err_out:
500+
/* Enable interrupts */
501+
atmel_uart_writel(port, ATMEL_US_IER, atmel_port->tx_done_mask);
502+
503+
return ret;
504+
}
505+
378506
/*
379507
* Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty.
380508
*/
@@ -489,8 +617,9 @@ static void atmel_stop_tx(struct uart_port *port)
489617
/* Disable interrupts */
490618
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
491619

492-
if ((port->rs485.flags & SER_RS485_ENABLED) &&
493-
!(port->rs485.flags & SER_RS485_RX_DURING_TX))
620+
if (((port->rs485.flags & SER_RS485_ENABLED) &&
621+
!(port->rs485.flags & SER_RS485_RX_DURING_TX)) ||
622+
port->iso7816.flags & SER_ISO7816_ENABLED)
494623
atmel_start_rx(port);
495624
}
496625

@@ -508,8 +637,9 @@ static void atmel_start_tx(struct uart_port *port)
508637
return;
509638

510639
if (atmel_use_pdc_tx(port) || atmel_use_dma_tx(port))
511-
if ((port->rs485.flags & SER_RS485_ENABLED) &&
512-
!(port->rs485.flags & SER_RS485_RX_DURING_TX))
640+
if (((port->rs485.flags & SER_RS485_ENABLED) &&
641+
!(port->rs485.flags & SER_RS485_RX_DURING_TX)) ||
642+
port->iso7816.flags & SER_ISO7816_ENABLED)
513643
atmel_stop_rx(port);
514644

515645
if (atmel_use_pdc_tx(port))
@@ -806,8 +936,9 @@ static void atmel_complete_tx_dma(void *arg)
806936
*/
807937
if (!uart_circ_empty(xmit))
808938
atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx);
809-
else if ((port->rs485.flags & SER_RS485_ENABLED) &&
810-
!(port->rs485.flags & SER_RS485_RX_DURING_TX)) {
939+
else if (((port->rs485.flags & SER_RS485_ENABLED) &&
940+
!(port->rs485.flags & SER_RS485_RX_DURING_TX)) ||
941+
port->iso7816.flags & SER_ISO7816_ENABLED) {
811942
/* DMA done, stop TX, start RX for RS485 */
812943
atmel_start_rx(port);
813944
}
@@ -1287,6 +1418,9 @@ atmel_handle_status(struct uart_port *port, unsigned int pending,
12871418
wake_up_interruptible(&port->state->port.delta_msr_wait);
12881419
}
12891420
}
1421+
1422+
if (pending & (ATMEL_US_NACK | ATMEL_US_ITERATION))
1423+
dev_dbg(port->dev, "ISO7816 ERROR (0x%08x)\n", pending);
12901424
}
12911425

12921426
/*
@@ -1379,8 +1513,9 @@ static void atmel_tx_pdc(struct uart_port *port)
13791513
atmel_uart_writel(port, ATMEL_US_IER,
13801514
atmel_port->tx_done_mask);
13811515
} else {
1382-
if ((port->rs485.flags & SER_RS485_ENABLED) &&
1383-
!(port->rs485.flags & SER_RS485_RX_DURING_TX)) {
1516+
if (((port->rs485.flags & SER_RS485_ENABLED) &&
1517+
!(port->rs485.flags & SER_RS485_RX_DURING_TX)) ||
1518+
port->iso7816.flags & SER_ISO7816_ENABLED) {
13841519
/* DMA done, stop TX, start RX for RS485 */
13851520
atmel_start_rx(port);
13861521
}
@@ -1755,6 +1890,22 @@ static void atmel_get_ip_name(struct uart_port *port)
17551890
atmel_port->has_frac_baudrate = true;
17561891
atmel_port->has_hw_timer = true;
17571892
atmel_port->rtor = ATMEL_US_RTOR;
1893+
version = atmel_uart_readl(port, ATMEL_US_VERSION);
1894+
switch (version) {
1895+
case 0x814: /* sama5d2 */
1896+
/* fall through */
1897+
case 0x701: /* sama5d4 */
1898+
atmel_port->fidi_min = 3;
1899+
atmel_port->fidi_max = 65535;
1900+
break;
1901+
case 0x502: /* sam9x5, sama5d3 */
1902+
atmel_port->fidi_min = 3;
1903+
atmel_port->fidi_max = 2047;
1904+
break;
1905+
default:
1906+
atmel_port->fidi_min = 1;
1907+
atmel_port->fidi_max = 2047;
1908+
}
17581909
} else if (name == dbgu_uart) {
17591910
dev_dbg(port->dev, "Dbgu or uart without hw timer\n");
17601911
} else {
@@ -2128,6 +2279,17 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
21282279
atmel_uart_writel(port, ATMEL_US_TTGR,
21292280
port->rs485.delay_rts_after_send);
21302281
mode |= ATMEL_US_USMODE_RS485;
2282+
} else if (port->iso7816.flags & SER_ISO7816_ENABLED) {
2283+
atmel_uart_writel(port, ATMEL_US_TTGR, port->iso7816.tg);
2284+
/* select mck clock, and output */
2285+
mode |= ATMEL_US_USCLKS_MCK | ATMEL_US_CLKO;
2286+
/* set max iterations */
2287+
mode |= ATMEL_US_MAX_ITER(3);
2288+
if ((port->iso7816.flags & SER_ISO7816_T_PARAM)
2289+
== SER_ISO7816_T(0))
2290+
mode |= ATMEL_US_USMODE_ISO7816_T0;
2291+
else
2292+
mode |= ATMEL_US_USMODE_ISO7816_T1;
21312293
} else if (termios->c_cflag & CRTSCTS) {
21322294
/* RS232 with hardware handshake (RTS/CTS) */
21332295
if (atmel_use_fifo(port) &&
@@ -2204,7 +2366,8 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
22042366
}
22052367
quot = cd | fp << ATMEL_US_FP_OFFSET;
22062368

2207-
atmel_uart_writel(port, ATMEL_US_BRGR, quot);
2369+
if (!(port->iso7816.flags & SER_ISO7816_ENABLED))
2370+
atmel_uart_writel(port, ATMEL_US_BRGR, quot);
22082371
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
22092372
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
22102373

@@ -2383,6 +2546,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
23832546
port->mapbase = pdev->resource[0].start;
23842547
port->irq = pdev->resource[1].start;
23852548
port->rs485_config = atmel_config_rs485;
2549+
port->iso7816_config = atmel_config_iso7816;
23862550
port->membase = NULL;
23872551

23882552
memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring));
@@ -2406,8 +2570,12 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
24062570
/* only enable clock when USART is in use */
24072571
}
24082572

2409-
/* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */
2410-
if (port->rs485.flags & SER_RS485_ENABLED)
2573+
/*
2574+
* Use TXEMPTY for interrupt when rs485 or ISO7816 else TXRDY or
2575+
* ENDTX|TXBUFE
2576+
*/
2577+
if (port->rs485.flags & SER_RS485_ENABLED ||
2578+
port->iso7816.flags & SER_ISO7816_ENABLED)
24112579
atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
24122580
else if (atmel_use_pdc_tx(port)) {
24132581
port->fifosize = PDC_BUFFER_SIZE;

drivers/tty/serial/atmel_serial.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@
8282
#define ATMEL_US_OVER BIT(19) /* Oversampling Mode */
8383
#define ATMEL_US_INACK BIT(20) /* Inhibit Non Acknowledge */
8484
#define ATMEL_US_DSNACK BIT(21) /* Disable Successive NACK */
85-
#define ATMEL_US_MAX_ITER GENMASK(26, 24) /* Max Iterations */
85+
#define ATMEL_US_MAX_ITER_MASK GENMASK(26, 24) /* Max Iterations */
86+
#define ATMEL_US_MAX_ITER(n) (((n) << 24) & ATMEL_US_MAX_ITER_MASK)
8687
#define ATMEL_US_FILTER BIT(28) /* Infrared Receive Line Filter */
8788

8889
#define ATMEL_US_IER 0x08 /* Interrupt Enable Register */

0 commit comments

Comments
 (0)