@@ -330,8 +330,9 @@ struct sc16is7xx_one {
330
330
struct kthread_work reg_work ;
331
331
struct kthread_delayed_work ms_work ;
332
332
struct sc16is7xx_one_config config ;
333
- bool irda_mode ;
334
333
unsigned int old_mctrl ;
334
+ u8 old_lcr ; /* Value before EFR access. */
335
+ bool irda_mode ;
335
336
};
336
337
337
338
struct sc16is7xx_port {
@@ -412,6 +413,49 @@ static void sc16is7xx_power(struct uart_port *port, int on)
412
413
on ? 0 : SC16IS7XX_IER_SLEEP_BIT );
413
414
}
414
415
416
+ /*
417
+ * In an amazing feat of design, the Enhanced Features Register (EFR)
418
+ * shares the address of the Interrupt Identification Register (IIR).
419
+ * Access to EFR is switched on by writing a magic value (0xbf) to the
420
+ * Line Control Register (LCR). Any interrupt firing during this time will
421
+ * see the EFR where it expects the IIR to be, leading to
422
+ * "Unexpected interrupt" messages.
423
+ *
424
+ * Prevent this possibility by claiming a mutex while accessing the EFR,
425
+ * and claiming the same mutex from within the interrupt handler. This is
426
+ * similar to disabling the interrupt, but that doesn't work because the
427
+ * bulk of the interrupt processing is run as a workqueue job in thread
428
+ * context.
429
+ */
430
+ static void sc16is7xx_efr_lock (struct uart_port * port )
431
+ {
432
+ struct sc16is7xx_one * one = to_sc16is7xx_one (port , port );
433
+
434
+ mutex_lock (& one -> efr_lock );
435
+
436
+ /* Backup content of LCR. */
437
+ one -> old_lcr = sc16is7xx_port_read (port , SC16IS7XX_LCR_REG );
438
+
439
+ /* Enable access to Enhanced register set */
440
+ sc16is7xx_port_write (port , SC16IS7XX_LCR_REG , SC16IS7XX_LCR_CONF_MODE_B );
441
+
442
+ /* Disable cache updates when writing to EFR registers */
443
+ regcache_cache_bypass (one -> regmap , true);
444
+ }
445
+
446
+ static void sc16is7xx_efr_unlock (struct uart_port * port )
447
+ {
448
+ struct sc16is7xx_one * one = to_sc16is7xx_one (port , port );
449
+
450
+ /* Re-enable cache updates when writing to normal registers */
451
+ regcache_cache_bypass (one -> regmap , false);
452
+
453
+ /* Restore original content of LCR */
454
+ sc16is7xx_port_write (port , SC16IS7XX_LCR_REG , one -> old_lcr );
455
+
456
+ mutex_unlock (& one -> efr_lock );
457
+ }
458
+
415
459
static void sc16is7xx_ier_clear (struct uart_port * port , u8 bit )
416
460
{
417
461
struct sc16is7xx_port * s = dev_get_drvdata (port -> dev );
@@ -522,45 +566,19 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
522
566
div /= 4 ;
523
567
}
524
568
525
- /* In an amazing feat of design, the Enhanced Features Register shares
526
- * the address of the Interrupt Identification Register, and is
527
- * switched in by writing a magic value (0xbf) to the Line Control
528
- * Register. Any interrupt firing during this time will see the EFR
529
- * where it expects the IIR to be, leading to "Unexpected interrupt"
530
- * messages.
531
- *
532
- * Prevent this possibility by claiming a mutex while accessing the
533
- * EFR, and claiming the same mutex from within the interrupt handler.
534
- * This is similar to disabling the interrupt, but that doesn't work
535
- * because the bulk of the interrupt processing is run as a workqueue
536
- * job in thread context.
537
- */
538
- mutex_lock (& one -> efr_lock );
539
-
540
- lcr = sc16is7xx_port_read (port , SC16IS7XX_LCR_REG );
541
-
542
- /* Open the LCR divisors for configuration */
543
- sc16is7xx_port_write (port , SC16IS7XX_LCR_REG ,
544
- SC16IS7XX_LCR_CONF_MODE_B );
545
-
546
569
/* Enable enhanced features */
547
- regcache_cache_bypass ( one -> regmap , true );
570
+ sc16is7xx_efr_lock ( port );
548
571
sc16is7xx_port_update (port , SC16IS7XX_EFR_REG ,
549
572
SC16IS7XX_EFR_ENABLE_BIT ,
550
573
SC16IS7XX_EFR_ENABLE_BIT );
551
-
552
- regcache_cache_bypass (one -> regmap , false);
553
-
554
- /* Put LCR back to the normal mode */
555
- sc16is7xx_port_write (port , SC16IS7XX_LCR_REG , lcr );
556
-
557
- mutex_unlock (& one -> efr_lock );
574
+ sc16is7xx_efr_unlock (port );
558
575
559
576
sc16is7xx_port_update (port , SC16IS7XX_MCR_REG ,
560
577
SC16IS7XX_MCR_CLKSEL_BIT ,
561
578
prescaler );
562
579
563
- /* Open the LCR divisors for configuration */
580
+ /* Backup LCR and access special register set (DLL/DLH) */
581
+ lcr = sc16is7xx_port_read (port , SC16IS7XX_LCR_REG );
564
582
sc16is7xx_port_write (port , SC16IS7XX_LCR_REG ,
565
583
SC16IS7XX_LCR_CONF_MODE_A );
566
584
@@ -570,7 +588,7 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
570
588
sc16is7xx_port_write (port , SC16IS7XX_DLL_REG , div % 256 );
571
589
regcache_cache_bypass (one -> regmap , false);
572
590
573
- /* Put LCR back to the normal mode */
591
+ /* Restore LCR and access to general register set */
574
592
sc16is7xx_port_write (port , SC16IS7XX_LCR_REG , lcr );
575
593
576
594
return DIV_ROUND_CLOSEST (clk / 16 , div );
@@ -1049,17 +1067,7 @@ static void sc16is7xx_set_termios(struct uart_port *port,
1049
1067
if (!(termios -> c_cflag & CREAD ))
1050
1068
port -> ignore_status_mask |= SC16IS7XX_LSR_BRK_ERROR_MASK ;
1051
1069
1052
- /* As above, claim the mutex while accessing the EFR. */
1053
- mutex_lock (& one -> efr_lock );
1054
-
1055
- sc16is7xx_port_write (port , SC16IS7XX_LCR_REG ,
1056
- SC16IS7XX_LCR_CONF_MODE_B );
1057
-
1058
1070
/* Configure flow control */
1059
- regcache_cache_bypass (one -> regmap , true);
1060
- sc16is7xx_port_write (port , SC16IS7XX_XON1_REG , termios -> c_cc [VSTART ]);
1061
- sc16is7xx_port_write (port , SC16IS7XX_XOFF1_REG , termios -> c_cc [VSTOP ]);
1062
-
1063
1071
port -> status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS );
1064
1072
if (termios -> c_cflag & CRTSCTS ) {
1065
1073
flow |= SC16IS7XX_EFR_AUTOCTS_BIT |
@@ -1071,16 +1079,16 @@ static void sc16is7xx_set_termios(struct uart_port *port,
1071
1079
if (termios -> c_iflag & IXOFF )
1072
1080
flow |= SC16IS7XX_EFR_SWFLOW1_BIT ;
1073
1081
1074
- sc16is7xx_port_update (port ,
1075
- SC16IS7XX_EFR_REG ,
1076
- SC16IS7XX_EFR_FLOWCTRL_BITS ,
1077
- flow );
1078
- regcache_cache_bypass (one -> regmap , false);
1079
-
1080
1082
/* Update LCR register */
1081
1083
sc16is7xx_port_write (port , SC16IS7XX_LCR_REG , lcr );
1082
1084
1083
- mutex_unlock (& one -> efr_lock );
1085
+ /* Update EFR registers */
1086
+ sc16is7xx_efr_lock (port );
1087
+ sc16is7xx_port_write (port , SC16IS7XX_XON1_REG , termios -> c_cc [VSTART ]);
1088
+ sc16is7xx_port_write (port , SC16IS7XX_XOFF1_REG , termios -> c_cc [VSTOP ]);
1089
+ sc16is7xx_port_update (port , SC16IS7XX_EFR_REG ,
1090
+ SC16IS7XX_EFR_FLOWCTRL_BITS , flow );
1091
+ sc16is7xx_efr_unlock (port );
1084
1092
1085
1093
/* Get baud rate generator configuration */
1086
1094
baud = uart_get_baud_rate (port , termios , old ,
0 commit comments