From patchwork Mon Feb 21 10:56:17 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: 544621 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 AFD38C433F5 for ; Mon, 21 Feb 2022 11:16:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1355778AbiBULRT (ORCPT ); Mon, 21 Feb 2022 06:17:19 -0500 Received: from mxb-00190b01.gslb.pphosted.com ([23.128.96.19]:46556 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1356099AbiBULPv (ORCPT ); Mon, 21 Feb 2022 06:15:51 -0500 Received: from eu-smtp-delivery-197.mimecast.com (eu-smtp-delivery-197.mimecast.com [185.58.85.197]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id E2C3B63E4 for ; Mon, 21 Feb 2022 02:56:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=camlingroup.com; s=mimecast20210310; t=1645441016; 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=ILFObbSPrLnDVEpPqIcjtiX3zy1tsruyADnD1Z0SxrjH2x9f5Uqa5j49elq8vUapWQCbd2 ZZmPbPnKsXBcP2+hoXwNrMtEE5AK6fiPkVJ1O0suGTNLcQ3Oqi7/hbnwn26hQI23mW8mSL +rWMhv2LRA3OQUz1VHm2aD/fsYC4FRo= Received: from GBR01-LO2-obe.outbound.protection.outlook.com (mail-lo2gbr01lp2057.outbound.protection.outlook.com [104.47.21.57]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id uk-mta-186-6sEc884mMz64zYl4nT9zAg-4; Mon, 21 Feb 2022 10:56:55 +0000 X-MC-Unique: 6sEc884mMz64zYl4nT9zAg-4 Received: from CWLP123MB5572.GBRP123.PROD.OUTLOOK.COM (2603:10a6:400:16b::6) by CWLP123MB6574.GBRP123.PROD.OUTLOOK.COM (2603:10a6:400:184::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4995.24; Mon, 21 Feb 2022 10:56:52 +0000 Received: from CWLP123MB5572.GBRP123.PROD.OUTLOOK.COM ([fe80::6dad:8602:45c5:6747]) by CWLP123MB5572.GBRP123.PROD.OUTLOOK.COM ([fe80::6dad:8602:45c5:6747%8]) with mapi id 15.20.4995.027; Mon, 21 Feb 2022 10:56:52 +0000 From: =?utf-8?q?Tomasz_Mo=C5=84?= To: linux-serial@vger.kernel.org CC: Phil Elwell , Daniel Mack , Jiri Slaby , Greg Kroah-Hartman , =?utf-8?q?Krzysztof_Drobi?= =?utf-8?q?=C5=84ski?= , Lech Perczak , =?utf-8?q?Tomasz_Mo=C5=84?= Subject: [PATCH 5/6] sc16is7xx: Handle modem status lines Date: Mon, 21 Feb 2022 11:56:17 +0100 Message-ID: <20220221105618.3503470-6-tomasz.mon@camlingroup.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220221105618.3503470-1-tomasz.mon@camlingroup.com> References: <20220221105618.3503470-1-tomasz.mon@camlingroup.com> X-ClientProxiedBy: LO4P123CA0469.GBRP123.PROD.OUTLOOK.COM (2603:10a6:600:1a8::6) 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: 36dc41ba-a9cc-4787-d5b4-08d9f528de0f X-MS-TrafficTypeDiagnostic: CWLP123MB6574: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: 2bh9EqyvekNanNcJfwx9CIGSqwR3yVirHojSPjMVOBl4c0vioU8lGaOdYxcZm5JltlJkyLeXOhabJOPSTlnLDcmbC/Y42H4YbiSo2jn4wAnkUVzfT1vmea7K0FoXMmCg635xmb/+11ffvFouTKvHNvSxy2cbHiAhzO7Mtt2uOof1u3ctOjHRMm0QlTc1+fJLVIUUXEY7/Jjyht4hXYLCnggdEM5xqiUXXMCDbTw8jHdmloG4ND8rpcjfpwSL/JUl8USMwAwYxJ/atpWvVqZ78fL08jje2UtI0R/puGfJGk0WtfUuO4RM8z8GMe5ivp3M7BqW//V1SJwuTXpXo90LmpMJ16JGyBiKbmk9NXSCd11JJknC/dpzq7euZGr6O1HsWYfNKyLyP2MMeca8UdBG+L3PrgZXDcQcwCsE/qUxMUHMYBONXM1LxgBiwo53gYb0vYVStRZAInX8hbhyCr/jHZ+AiVK8AgL5InjdmVWa5m7xUJoh7X6QfVZnq7M4IvuQK5i7cGGikN8GV7ZPSVKqz5emgbKzJnKch+g3s+0afrXJZ0i272MMTE7RWObNfBVKUdl/Gj9SR6F68WyHT4Z6eqUyS0K5xppYG31aXGTb8c0kjoAVeKdF58UDKxgF97fTBBVyWHr9WdRRvCRh95YxQXGv9LGGSg5xr9wiaR+63+bswB9loVZLjK7Muu39EolQVXAQTNiAm51LCvi7iVX/pZq/Mpf2b1fNdsNCkBeOupk= 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)(52116002)(6666004)(6512007)(6506007)(5660300002)(508600001)(8936002)(38350700002)(38100700002)(36756003)(2906002)(86362001)(4326008)(8676002)(6486002)(54906003)(2616005)(83380400001)(66946007)(66556008)(66476007)(6916009)(316002)(26005)(186003)(1076003)(326664003); DIR:OUT; SFP:1101 X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: wtT1dFveJpcfsrPt87n0uSij8LgZsgrtoX5dXScQuyggM2DiktZBADYDMDteLdlDUdgDfkPeenR+eeGvceaczywLcqBA5QWRkgkqKgmV9TeokrEsc76nx3ZZcLtQdx83MnvLy7qo5ZPIdDV18KcZtnapEFWDfa79744CrmTXvJZseJXHzSYED/YBiIxV2PHJ2pp0J5AG5NJ91fPafmBpVpHmP+BzrKPGYsvjr9U4v8GYiYT9FxXUIugynPoA33HIqbrgqQ6tdJTsXPHRnTn68YM3u1tjdv8DlEH7+Nk3Adp1dS08T2B3jVCOtWcAaBQ/U/wCRhVcol5yC3QYjDpLsQaFR2PgI73VnsBP0XPoXdmhpsZvKMXPcoYjOsn32k9uzIlMy+UydgvyaaG/RFZGnBDIMK2ujgJ2ROst1+Inq6wog3uuC/Yzy09liJ5E+72J5KgbV9vyDqlK94wKuScSEx2sdGuBd+m+FIud44RHbbbF4HEaei1vOK91iV8VU/tbFCKEvOYMDdagLcjannqoPyKOVi6vV6LzmXNhejAuLII/9QpgwzvfNgRor6/Tsd7LMFgH1EIl2D/t/1AEFQDDxXFd4LoUdALDUcLzKBeqlG25k1iavMddFuJRyfpJARuvnOU47Ws/HERxUvuQQR2SAT7EQ1Ky6TJNwb9CHuLMOHFRzSKgtrqsJ3tSTR/nzIDzFRri9tmMm7yjIF0SeMX7wv/kwMK74ajPGHKf2FRNcarC2+qkwmqhjFiFivQtwL3Jo+T1gfXq1DKOzHK4fjCH3/gEGyEEWoF2f/ceqCJhmzXzW8p6xsNehlxa9stuO885N+3W1j31k75q6P9042Jl5loL/Q2JWHXzOIO6wgTpIs1nY7lToVnqfgpz8M/93cltVR8KVR+1mWLNBzv1teY8xxO9hg48HB5IZSzvaBQeJ5kijZ/r7nxAyEETTqoSdioItUcAzgR9x7Tdc+evS6/A/gs9lyA6b/aF5O3R13sxtEH6Gwm+sjiUOoO/hm43tgCA5dhOeWXgoNNz+4PZjVtcc5U0jbRJc1jtS9cBmt/oL9WiaALp9Dx7+f+FisyVoSXmyuaSjg1gj1yVzzeq/IQBMLPXk777CqJ6V88jP14vu5Np0Mj7YoyLjmPmfkTWau5HdojNFCOf72EPmhIo5MMtDkMY0sYQkdBu7s8J1HucpUrRx3oK0H+Du/16ezyMytpOzhwzznJVCi+FsJNQF8HSZ0E74m8iNGFErsrc0SMvvAFj68h0F/yAN5fJ1K+dxKleBU3xWpW8eByjqPoix1UYyGQvLtpUWbhsFSGF6uVYbRjh5cgCCE0Ui9BIek7lghXekG9ViSgYw1nGolVM0Kh8q4Utm3p7AOqPuAKIWMbVLQNjavdmqFhZmnIMXPQOc0oxeWmS+b7NTgUOpQmIDdHWbcHwSa+xu+qKDXGyevRwkqHyWqmjfZeqeHONGOuEOZEk9H/d5J9GXELpxg8mxsJJS/9ry5hLpWxbZd5GVl919l+zY4/mOVLlP8l2UGOtffCzRZaU2SqXZDyI1+C2lKYNlWc4AU0Uq/X3tROtGJSFXGTeZsoxx/W+W8AvouV+d3M41drYf0jZw6OlGx+CKlmdBBc2Qe7NIBxL1F37ylHS1OFMf7LTsAD+6WfubQTTcz4Epl3QDqj/ttJ5BS8cEsACQuQZHnasNQp/9FHGY/uj2XQ= X-OriginatorOrg: camlingroup.com X-MS-Exchange-CrossTenant-Network-Message-Id: 36dc41ba-a9cc-4787-d5b4-08d9f528de0f X-MS-Exchange-CrossTenant-AuthSource: CWLP123MB5572.GBRP123.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 21 Feb 2022 10:56:52.3818 (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: peg+jPE7WDyscpk0wSPMfMs3bmvrmskzbEU13ezSX5bar2GxTdHRfmgKYJwY487mfWaBnEDNKOEiDLN2hKoWwNJSD6RpHiL4ike/tK6rrbo= X-MS-Exchange-Transport-CrossTenantHeadersStamped: CWLP123MB6574 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);