From patchwork Tue Mar 1 06:03:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Tomasz_Mo=C5=84?= X-Patchwork-Id: 547479 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8C56EC433EF for ; Tue, 1 Mar 2022 06:04:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229674AbiCAGEk (ORCPT ); Tue, 1 Mar 2022 01:04:40 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55652 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231262AbiCAGEj (ORCPT ); Tue, 1 Mar 2022 01:04:39 -0500 Received: from eu-smtp-delivery-197.mimecast.com (eu-smtp-delivery-197.mimecast.com [185.58.86.197]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 21BA160D84 for ; Mon, 28 Feb 2022 22:03:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=camlingroup.com; s=mimecast20210310; t=1646114636; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=9IN5E8svo2s9/jUURPs22fmQ6amy9o1Ey56PjI/awko=; b=J465ClMe0fUnLfQ3383Q7tiDNXWdl0GL6MgQRSluJYRupyktBjGttvsKOESU6JHk+NnxAc qeHFnhKfoS6kQSaw4ecuSX7beob+hUmvNxKqkXmXaREpGw3cckfOm81o0XXwapCglyLP8P NWAyR0K6WMKI42Mftbb/cUdow1VjJF0= Received: from GBR01-CWL-obe.outbound.protection.outlook.com (mail-cwlgbr01lp2058.outbound.protection.outlook.com [104.47.20.58]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id uk-mta-15-7CU9LN-tPTGROlumn7XZkQ-1; Tue, 01 Mar 2022 06:03:55 +0000 X-MC-Unique: 7CU9LN-tPTGROlumn7XZkQ-1 Received: from CWLP123MB5572.GBRP123.PROD.OUTLOOK.COM (2603:10a6:400:16b::6) by CWLP123MB2948.GBRP123.PROD.OUTLOOK.COM (2603:10a6:400:5b::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5017.21; Tue, 1 Mar 2022 06:03:54 +0000 Received: from CWLP123MB5572.GBRP123.PROD.OUTLOOK.COM ([fe80::6dad:8602:45c5:6747]) by CWLP123MB5572.GBRP123.PROD.OUTLOOK.COM ([fe80::6dad:8602:45c5:6747%7]) with mapi id 15.20.5017.027; Tue, 1 Mar 2022 06:03:54 +0000 From: =?utf-8?q?Tomasz_Mo=C5=84?= To: Greg Kroah-Hartman CC: linux-serial@vger.kernel.org, Phil Elwell , Daniel Mack , Jiri Slaby , =?utf-8?q?Krzysztof_Drobi=C5=84ski?= , Lech Perczak , =?utf-8?q?Tomasz_Mo=C5=84?= Subject: [PATCH RESEND 2/3] sc16is7xx: Handle modem status lines Date: Tue, 1 Mar 2022 07:03:31 +0100 Message-ID: <20220301060332.2561851-3-tomasz.mon@camlingroup.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220301060332.2561851-1-tomasz.mon@camlingroup.com> References: <20220301060332.2561851-1-tomasz.mon@camlingroup.com> X-ClientProxiedBy: LO4P265CA0018.GBRP265.PROD.OUTLOOK.COM (2603:10a6:600:2ad::11) To CWLP123MB5572.GBRP123.PROD.OUTLOOK.COM (2603:10a6:400:16b::6) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 1fcfa6b5-de6f-43c2-51af-08d9fb494432 X-MS-TrafficTypeDiagnostic: CWLP123MB2948:EE_ X-Microsoft-Antispam-PRVS: X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0 X-Microsoft-Antispam-Message-Info: LYQU9ieIi85BVHAdSK/hVAe9peHQ4KDxXhLAo/HUfYGuCD7qO8uTXGSzR9/kviilft+XpGCiRwEsvP2gJ9TTQHL3EmRO5pktGqNm+QzQg2WO9AaJnI22s1GR1Kwug39vinErr8Wv/l3NjU7O3007aJs8tcx00I5/CBVBYiJcQr6rz1zivfiR+3fdzzjmtWN9ZU80RPyDAuMBSqN7/5SHME1hKFlmAZgkjQN7LAzZPCfdeFM0EA+Vy5PvUsFH8z9wa128R4RZ8UK+P4qUzMJHNw3snaNEqa19Scss4h1vvKGu1F5XNJIV2FjOndPreWDk72ayg1BzhVkego8FFLlw2MXyhybgmZlAWXh/ogwdGLeVd6lBiihkCfc/kyMXfvLmsIUcc3viyQOjQYgSUt7AgkyGPQ7aoJzqLU2p9NWyhE/seauW1f/XBd2C7XaukBFetsyTLQjET+00GerUHaoAyvFwxumCXjpTzfyuFiVEVRoxQswD4OkRxJm6xmPoR8Mj1+Iw45iOjbzUaz+d0+EseI1LGiAhzZZ62s8WSXwgdqiII1+YCnr6JR0+Pkto26pm8nafkpqYsLLVaw/q4SIsFLAfHxjYWfoX84AJ2KMAZimEs4/XvfO8dxfjOq4A1+zTQE5Lkx+U3khwLLYxuUI8weFRXUotappyeuM/ob9ccCjvRaAFACcdiTzamXTgtKFOF1OyV091mv/Wpz2QL/0rMrfYECpZA4RGzqc6FtGPy7I= X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:CWLP123MB5572.GBRP123.PROD.OUTLOOK.COM; PTR:; CAT:NONE; SFS:(13230001)(366004)(107886003)(2906002)(1076003)(86362001)(26005)(186003)(52116002)(2616005)(6506007)(83380400001)(8936002)(6666004)(8676002)(4326008)(6512007)(66556008)(66946007)(5660300002)(66476007)(6916009)(316002)(54906003)(508600001)(6486002)(36756003)(38350700002)(38100700002)(326664003); DIR:OUT; SFP:1101 X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: 1mQsDhyPl7UAoEFTgT+9vTThGF8vGEz4EJq5DYbaN7Y1CjOcKDd57El4EUNsN7bZ3Zh2M6oaN5s04zj5biddtLxM2ZGAexsme+xQyfBr+SgXWDCqhE2DIk4781s0kHcHto8jRhKapaGO6hlpi0Clo0AREXSDnzZKIz1ESQq1m/F2EVj2GTqo5lfBgVSIt1fOiQE/GTKUjSejz3o15sdkvvRgQ8EEMTf/uEZ5nQq2ne/+ZObw0H/ywCKDkiJqq7wnaNUgLTzXSQz6ShEQcEGXIQOTk7guJGlIJaDCs1kIuS0yScgiBgf0ryP1PX5/P7WyOP5HpuNoVGGI07UdLh2QGve+m/7ciqGsTQqGDjYmWUF1Xx5VIP1OOIgrRE0m1AiyK1caivmSQ0Xet6d/qSWaktmVzV6McvnEgcmfYRoaixMvrtVHYIwCZI1+ykj7zDKaRaMdv2Fxhh/m7ESN0R7gDCQLHTmoo7gVudovU/x8WcGwpWBVZQhTtRR/36yX73tr2Flqoi5TjUWSmpazNZkl4ikxWfCEtt3/zdmrWCfPpCOtVZsUjZFtBIGJ/LPysLg1cTafNXvpQ1XUC705/t5vT0hLZYltnYSfB7l+vsJIPCn5gUAnbhLBTz6Svt/MAnMWAxbyBot4UkmnjZfu9ZnabEO+dPYNrXfUi29FGmPG7Nz1GtPzyMAQxDYD0FosFSfGeuFubtZXYiUjvW1/6jA4xAd+G6Dlero/S/ZrpuJdO/yFawabncJx2+v6KzRYSy53q+2sNJVSmag/G6OUnM7BCL/XMh4Ija+pJ5hcvcxjfWfs8uHE8iIrFZvyCYymmAmotItuq5lfiGvt7VRUevgaOsgkqKdctAMbQ01GrQ+g46TpOY2s6ToMiQRr2B2el9nGAkVSqMaV9T0fAGkpEIjqblJbI+1cR0yfpZUvSDuTebY6Dt7ytN78oJD1eh6M8OTmSqRtBVljKMN+06utQH4/2xH/KaP4fUEKCVOY6tdpL/knxc3DMgPQlmoiXxzmH+eU6Z1M1I5DhsfDwyONHUL6SLuMwuNIKrWK3sbw2MV5ED6feAu3xSAC6BcMMheCxdoj3+U0phOtpelvTohXeB+t+ggrhzkAdSTyAf3bySTL3P7lkgyl+c9KoihA1jpC4q9pr+I68BDXmhtZOjwTK3FuUND/3fXSbZRK22I0kb4RxdAG+vy0g9FGwRkJwy3GVIoY0e7kTkd6Npz7BYCbmfiFwi59JFz2ah1tg/H3dyclQpPr8QTzb0GUp5g1xxDw5giNrp/Sab9ZcFBxDMF2qg6DdrnfxFCvYkTJPkfjcjBlIx/YPr+sMmM7fJ+23qthAgY7dhPrXDYBRlcshvCuM/TLKEvYJGNridI8cBTHvTEXpXXY5tB9diaCgImjoT3uoiILNm3e/z4DSTYJ+hgCBvCg+TJMJK+RdtV8/G+EEtYjUyubpoP1P+BtrLoALVPvWq5SfcnWkoopOgwDQZ7ByNGtN5K85mwP4aGSdT9BQk6w/jOa5UWQnRYVoDLuPoBcwE5kY4Mn81tjpoVaI+QjKRLaufyQ+KnOspVKHFgtKepXWDm3gzSKD/CWXHA3Y+0+G079aDUAQnu7DbXPgOBmu2f+4KDNY5tp7xzCQwj5sNzW22LS+GKx8w7ZPEp7SNMk2ngMSDL6nP/t6fgqcxwbNf5iBj+6dlJ9w15vGATd4ivgcBc= X-OriginatorOrg: camlingroup.com X-MS-Exchange-CrossTenant-Network-Message-Id: 1fcfa6b5-de6f-43c2-51af-08d9fb494432 X-MS-Exchange-CrossTenant-AuthSource: CWLP123MB5572.GBRP123.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 01 Mar 2022 06:03:54.5905 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: fd4b1729-b18d-46d2-9ba0-2717b852b252 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: ujK9w3ZFc045W/zZmRAaPDYzbHoV1kepQ/UJsLY52ukq0qzdCpWFEER8wv4axba1WSCHCDg6RCdYEDRHGPNkvCoIXUIEYl8aGKH/OlnMDcY= X-MS-Exchange-Transport-CrossTenantHeadersStamped: CWLP123MB2948 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUK97A341 smtp.mailfrom=tomasz.mon@camlingroup.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: camlingroup.com Precedence: bulk List-ID: X-Mailing-List: linux-serial@vger.kernel.org The uart_handle_cts_change() and uart_handle_dcd_change() must be called with port lock being held. Acquire the lock after reading MSR register. Do not acquire spin lock when reading MSR register because I2C/SPI port functions cannot be called with spinlocks held. Update rng and dsr counters. Wake up delta_msr_wait to allow tty notice modem status change. Co-developed-by: Lech Perczak Signed-off-by: Lech Perczak Co-developed-by: Tomasz Moń Signed-off-by: Tomasz Moń --- drivers/tty/serial/sc16is7xx.c | 120 +++++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 17e79af36604..5c247b4a01a9 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -324,8 +324,10 @@ struct sc16is7xx_one { u8 line; struct kthread_work tx_work; struct kthread_work reg_work; + struct kthread_delayed_work ms_work; struct sc16is7xx_one_config config; bool irda_mode; + unsigned int old_mctrl; }; struct sc16is7xx_port { @@ -705,12 +707,56 @@ static void sc16is7xx_handle_tx(struct uart_port *port) spin_unlock_irqrestore(&port->lock, flags); } +static unsigned int sc16is7xx_get_hwmctrl(struct uart_port *port) +{ + u8 msr = sc16is7xx_port_read(port, SC16IS7XX_MSR_REG); + unsigned int mctrl = 0; + + mctrl |= (msr & SC16IS7XX_MSR_CTS_BIT) ? TIOCM_CTS : 0; + mctrl |= (msr & SC16IS7XX_MSR_DSR_BIT) ? TIOCM_DSR : 0; + mctrl |= (msr & SC16IS7XX_MSR_CD_BIT) ? TIOCM_CAR : 0; + mctrl |= (msr & SC16IS7XX_MSR_RI_BIT) ? TIOCM_RNG : 0; + return mctrl; +} + +static void sc16is7xx_update_mlines(struct sc16is7xx_one *one) +{ + struct uart_port *port = &one->port; + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + unsigned long flags; + unsigned int status, changed; + + lockdep_assert_held_once(&s->efr_lock); + + status = sc16is7xx_get_hwmctrl(port); + changed = status ^ one->old_mctrl; + + if (changed == 0) + return; + + one->old_mctrl = status; + + spin_lock_irqsave(&port->lock, flags); + if ((changed & TIOCM_RNG) && (status & TIOCM_RNG)) + port->icount.rng++; + if (changed & TIOCM_DSR) + port->icount.dsr++; + if (changed & TIOCM_CAR) + uart_handle_dcd_change(port, status & TIOCM_CAR); + if (changed & TIOCM_CTS) + uart_handle_cts_change(port, status & TIOCM_CTS); + + wake_up_interruptible(&port->state->port.delta_msr_wait); + spin_unlock_irqrestore(&port->lock, flags); +} + static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) { struct uart_port *port = &s->p[portno].port; do { unsigned int iir, rxlen; + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG); if (iir & SC16IS7XX_IIR_NO_INT_BIT) @@ -727,6 +773,11 @@ static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) if (rxlen) sc16is7xx_handle_rx(port, rxlen, iir); break; + /* CTSRTS interrupt comes only when CTS goes inactive */ + case SC16IS7XX_IIR_CTSRTS_SRC: + case SC16IS7XX_IIR_MSI_SRC: + sc16is7xx_update_mlines(one); + break; case SC16IS7XX_IIR_THRI_SRC: sc16is7xx_handle_tx(port); break; @@ -874,6 +925,30 @@ static void sc16is7xx_stop_rx(struct uart_port *port) sc16is7xx_ier_clear(port, SC16IS7XX_IER_RDI_BIT); } +static void sc16is7xx_ms_proc(struct kthread_work *ws) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(ws, ms_work.work); + struct sc16is7xx_port *s = dev_get_drvdata(one->port.dev); + + if (one->port.state) { + mutex_lock(&s->efr_lock); + sc16is7xx_update_mlines(one); + mutex_unlock(&s->efr_lock); + + kthread_queue_delayed_work(&s->kworker, &one->ms_work, HZ); + } +} + +static void sc16is7xx_enable_ms(struct uart_port *port) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + + lockdep_assert_held_once(&port->lock); + + kthread_queue_delayed_work(&s->kworker, &one->ms_work, 0); +} + static void sc16is7xx_start_tx(struct uart_port *port) { struct sc16is7xx_port *s = dev_get_drvdata(port->dev); @@ -893,10 +968,10 @@ static unsigned int sc16is7xx_tx_empty(struct uart_port *port) static unsigned int sc16is7xx_get_mctrl(struct uart_port *port) { - /* DCD and DSR are not wired and CTS/RTS is handled automatically - * so just indicate DSR and CAR asserted - */ - return TIOCM_DSR | TIOCM_CAR; + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + /* Called with port lock taken so we can only return cached value */ + return one->old_mctrl; } static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl) @@ -920,8 +995,12 @@ static void sc16is7xx_set_termios(struct uart_port *port, struct ktermios *old) { struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); unsigned int lcr, flow = 0; int baud; + unsigned long flags; + + kthread_cancel_delayed_work_sync(&one->ms_work); /* Mask termios capabilities we don't support */ termios->c_cflag &= ~CMSPAR; @@ -1010,8 +1089,15 @@ static void sc16is7xx_set_termios(struct uart_port *port, /* Setup baudrate generator */ baud = sc16is7xx_set_baud(port, baud); + spin_lock_irqsave(&port->lock, flags); + /* Update timeout according to new baud rate */ uart_update_timeout(port, termios->c_cflag, baud); + + if (UART_ENABLE_MS(port, termios->c_cflag)) + sc16is7xx_enable_ms(port); + + spin_unlock_irqrestore(&port->lock, flags); } static int sc16is7xx_config_rs485(struct uart_port *port, @@ -1052,6 +1138,7 @@ static int sc16is7xx_startup(struct uart_port *port) struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); struct sc16is7xx_port *s = dev_get_drvdata(port->dev); unsigned int val; + unsigned long flags; sc16is7xx_power(port, 1); @@ -1102,16 +1189,25 @@ static int sc16is7xx_startup(struct uart_port *port) SC16IS7XX_EFCR_TXDISABLE_BIT, 0); - /* Enable RX interrupt */ - val = SC16IS7XX_IER_RDI_BIT; + /* Enable RX, CTS change and modem lines interrupts */ + val = SC16IS7XX_IER_RDI_BIT | SC16IS7XX_IER_CTSI_BIT | + SC16IS7XX_IER_MSI_BIT; sc16is7xx_port_write(port, SC16IS7XX_IER_REG, val); + /* Enable modem status polling */ + spin_lock_irqsave(&port->lock, flags); + sc16is7xx_enable_ms(port); + spin_unlock_irqrestore(&port->lock, flags); + return 0; } static void sc16is7xx_shutdown(struct uart_port *port) { struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + kthread_cancel_delayed_work_sync(&one->ms_work); /* Disable all interrupts */ sc16is7xx_port_write(port, SC16IS7XX_IER_REG, 0); @@ -1175,6 +1271,7 @@ static const struct uart_ops sc16is7xx_ops = { .stop_tx = sc16is7xx_stop_tx, .start_tx = sc16is7xx_start_tx, .stop_rx = sc16is7xx_stop_rx, + .enable_ms = sc16is7xx_enable_ms, .break_ctl = sc16is7xx_break_ctl, .startup = sc16is7xx_startup, .shutdown = sc16is7xx_shutdown, @@ -1341,7 +1438,9 @@ static int sc16is7xx_probe(struct device *dev, s->p[i].port.uartclk = freq; s->p[i].port.rs485_config = sc16is7xx_config_rs485; s->p[i].port.ops = &sc16is7xx_ops; + s->p[i].old_mctrl = 0; s->p[i].port.line = sc16is7xx_alloc_line(); + if (s->p[i].port.line >= SC16IS7XX_MAX_DEVS) { ret = -ENOMEM; goto out_ports; @@ -1353,9 +1452,17 @@ static int sc16is7xx_probe(struct device *dev, sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFCR_REG, SC16IS7XX_EFCR_RXDISABLE_BIT | SC16IS7XX_EFCR_TXDISABLE_BIT); + + /* Use GPIO lines as modem status registers */ + if (devtype->has_mctrl) + sc16is7xx_port_write(&s->p[i].port, + SC16IS7XX_IOCONTROL_REG, + SC16IS7XX_IOCONTROL_MODEM_BIT); + /* Initialize kthread work structs */ kthread_init_work(&s->p[i].tx_work, sc16is7xx_tx_proc); kthread_init_work(&s->p[i].reg_work, sc16is7xx_reg_proc); + kthread_init_delayed_work(&s->p[i].ms_work, sc16is7xx_ms_proc); /* Register port */ uart_add_one_port(&sc16is7xx_uart, &s->p[i].port); @@ -1439,6 +1546,7 @@ static void sc16is7xx_remove(struct device *dev) #endif for (i = 0; i < s->devtype->nr_uart; i++) { + kthread_cancel_delayed_work_sync(&s->p[i].ms_work); uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); clear_bit(s->p[i].port.line, &sc16is7xx_lines); sc16is7xx_power(&s->p[i].port, 0);