From nobody Tue Sep 29 06:59:42 2026 Received: from PA4PR04CU001.outbound.protection.outlook.com (mail-francecentralazon11023114.outbound.protection.outlook.com [40.107.162.114]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2F3253F4856; Tue, 11 Aug 2026 06:30:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.162.114 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786429849; cv=fail; b=txljPFPKGmrNnDNoNdJmYQpQicXL8hg2d2EJ/VqpRP8gfaQVkGLHbZw4nEpEOINFWdcaHG3Ha/E0y79JSzDU0CKLjKQw9VOzBmNu9TwcS/ZL4z+aOhj66eWUGMK/X0EV7PdhubSQj1awO8oQDZPTCd7H4roCGET5j2KwfYC4Ztg= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786429849; c=relaxed/simple; bh=M/kR3T4nQykEZ95QqRT/t6+mpBZerEkhyzERcdJTCMs=; h=From:Date:Subject:Content-Type:Message-Id:To:Cc:MIME-Version; b=QDnhY4Hh+gc+gDJcaavF2QZ0no11ZIkTX3DbCjLL9MLVooDiE25foHAjAx9SaKNwBpQqFdi+v7tFO09dsrmWVFLx54dv9WClKUPhRhNbVfExdwfPuqgP1HhuCc1oRVuAzioBOe5ofha4iipzgUXkYDoARKmuLWTBfcFJYk4+/+4= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=vaisala.com; spf=pass smtp.mailfrom=vaisala.com; dkim=pass (2048-bit key) header.d=vaisala.com header.i=@vaisala.com header.b=CXmldcPx; arc=fail smtp.client-ip=40.107.162.114 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=vaisala.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=vaisala.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=vaisala.com header.i=@vaisala.com header.b="CXmldcPx" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=VE4f+3HSfmX0TUvM4hqU+UqyxFu0DHyZz0EwNN/R979m+HpUMh4ZvKL+ZFCbj/8+F0u+cA4oegipP6gDznxxxIw99t9zSBTlTe0Ape6eOAdZdgIaoLRBmty1i0m+p4RLV+kQ0dZKPi/Ye/X1PTvMgxsC4u6m7LNOuII5DLXPxhA9h/Qy7hrFEOcvXFTBpOW8qVzQI6G+biXeskpYwVeyclVuSb28U3FvQolOeFHRYyiGd1262hVGhtHpTvOV6mGv2p9UHTjierqpqwq8os6gaJihncEKFNI+kOfbgXhv/LLuh283mpwORAhHbBTcEdiXSYxyuJu0KOXOtYo0LKAxkw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=ltqOH/FiZk0r8fqe6fDlJ51I8IvEXfDd8DbemtvkCt0=; b=SxSZhYAc9uScSBuMf6bHoc/ZJshV0i3e4V5XaTNGJPh+NDiBclsXG3uMWEtlHyWQrTv+zFL/cdNqqoRcSHvReNzWwTctawJhkkRdn1LE8k6ZGsRz7BH9kyKgDb/OatsX/sDlgD9db3s0cT/Xz0NPE9yF2T6qUgQIIRwiD4GlmYJgZgMLuRBDC4lkZ8ej6tF+q7Uo9wuZONBqGiEXqnvWiQnKhEeadaxyE2fmEbGBQapuJs1rdad3yBuLx8z80qKqHeEDoyuBC3+fxY99O2kBje11xIyafFDsN+KIC7LOc5wsm0MAx1RqRhu3ZRaz2GAUICbJOvRECMlyK5jHgcoH7A== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=vaisala.com; dmarc=pass action=none header.from=vaisala.com; dkim=pass header.d=vaisala.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=vaisala.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=ltqOH/FiZk0r8fqe6fDlJ51I8IvEXfDd8DbemtvkCt0=; b=CXmldcPxik/e+DKa8Q4NKPpZ/vzHXtYiXPVIP7D1z/a4V9zv/7FRvuEntHqomSqNMZBztZ3InAaI8JErG1oIqAlMKAOFnTp7exFazF/Vg2ZwQysq+Tc+tUDUhSV5/cJu9TERRCl5XCRNV33hrDeYq3gPqFaOr5ZWsGFQZyqOmBpqJ87RuNib3cU1hhTOWNEslknCsbhFN9+mCx2+crPRh86OCEDzrKne2nkONw5y8phrzkaZ4JjROcSkTpD7yA5AO+MWM/X6zyOn7IgcE1NybvDTKhGz215TWXuYR677krk8VXqRM8B5Inx4ynI/Hrg9QnojhjNzNZz3ZXmcEARpyg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=vaisala.com; Received: from AS5PR06MB9040.eurprd06.prod.outlook.com (2603:10a6:20b:676::22) by AS8PR06MB7862.eurprd06.prod.outlook.com (2603:10a6:20b:3c8::22) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.292.25; Tue, 11 Aug 2026 06:30:37 +0000 Received: from AS5PR06MB9040.eurprd06.prod.outlook.com ([fe80::923e:d30b:98c0:c69]) by AS5PR06MB9040.eurprd06.prod.outlook.com ([fe80::923e:d30b:98c0:c69%7]) with mapi id 15.21.0292.024; Tue, 11 Aug 2026 06:30:36 +0000 From: Tapio Reijonen Date: Tue, 11 Aug 2026 06:30:27 +0000 Subject: [PATCH v2] serial: max310x: drive RTS in software when hardware delays are too short Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260811-max310x-rs485-sw-delay-v2-1-e34283205789@vaisala.com> X-B4-Tracking: v=1; b=H4sIAILBemoC/3WNyw6CMBBFf4XM2jF9UB6u/A/DYqRVJuGV1lQI4 d+tuHZ5TnLP3SA4zy7AJdvAu8iBpzGBOmXQdjQ+HbJNDEqoQhipcaBFS7GgD3llMLzRup5WJC0 KW1baGlVDGs/ePXg5wrcmccfhNfn1+Inya3/JUtT/klGixNzk1Epxr3VJ10gcqKdzOw3Q7Pv+A fNTDrW+AAAA X-Change-ID: 20260513-max310x-rs485-sw-delay-a306d783d529 To: Greg Kroah-Hartman , Jiri Slaby Cc: linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org, Tapio Reijonen X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786429836; l=24070; i=tapio.reijonen@vaisala.com; s=20250903; h=from:subject:message-id; bh=M/kR3T4nQykEZ95QqRT/t6+mpBZerEkhyzERcdJTCMs=; b=/G183GCJITOWqtYvC3ltKvSeml/w7Cmt6sEPBBIbEYj3PN2SiksuxqutYVdxQUACGmwLBFvT4 2VpVXeX0u/wBdKlMF5eZvpamxgKOg+lFZfriWrThikJh3JzJPaNCDDq X-Developer-Key: i=tapio.reijonen@vaisala.com; a=ed25519; pk=jWBz3VD84WbWgfEgIqB5iFFiyVIHZr52zVBPOm7qiGo= X-ClientProxiedBy: GV3P280CA0047.SWEP280.PROD.OUTLOOK.COM (2603:10a6:150:9::15) To AS5PR06MB9040.eurprd06.prod.outlook.com (2603:10a6:20b:676::22) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AS5PR06MB9040:EE_|AS8PR06MB7862:EE_ X-MS-Office365-Filtering-Correlation-Id: 3a25f806-94ef-43f4-41a6-08def7720e11 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|52116014|376014|23010399003|18002099003|38350700014|10067099003|5023799004|56012099006|11063799006|6133799003|3023799007; X-Microsoft-Antispam-Message-Info: jFgPdRk9L3YXlts2sZHgvo+z394WBx8iuN9lJfqFQm6akJXLnfp2adR1Fbx8d1xB5q2P44Ldg4CaXUB1Q2AvbsUr/bJMnPjEalZ4etScGAX2pUVf6mL1wyHDwrQoBaKVoztkGY/Yjpts6tK4gSfXbDlRrGtFPpb6Uo7f72FV+NamdfsRTZJqZiHQRM1HcWEPw0/Rsw/KvMdewHFwliDZlN+qnU1plb5S4iGB/3A5VRaKAQRNLnEHxxLMmrtj+1szeC/sF6C9Z2KrPHov7Q3eYZUITtQAYaXG9R6Gwe3iqVaqO+wEnoxo5kMLDrTO3H6Q0yKioNhWg7/F/8Fo6BTTq71GcLiDLJfuJXoY6Rphf3guICZbQFG0NDfaEMcBqAD2aKkQGH/afIZdt288zrowAv/gmluHLzs5HvS/JdHA0PQ1+XzLgusvJlsMGa9tnygxvrrrANogIaZIuUFDqxqW59epd/YoiUH8lJn+fLeHLveLCDnJFK4f/iitkZqpST721b9DR3ki6qABaFU+V6/RlZZEBUCaaJWQwnr5zlC5yroD1cBnox/42DxjdOnCpPdYTFyEV/2d1H1O3MyPZwc0QlMiX65GgEq+vEx4F262UlB0j2sukN4iqkVDGGlBc8/wu+M+CW3RQeoww+tu1KSqMJhFOqk17gtjzYf56NnEmMy4UTcEQ1LD5U+9HoLMSOUlLkwi+41LuoLhG7TyuJbXbz2zNSiExkTZ3T1/FLTwGrY= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:AS5PR06MB9040.eurprd06.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(52116014)(376014)(23010399003)(18002099003)(38350700014)(10067099003)(5023799004)(56012099006)(11063799006)(6133799003)(3023799007);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?NFNtTm1OaTlHSVdBaHpqL0NYclF1NXhDRzRDS2VmWG91WTZScUpPeTJyaDJ0?= =?utf-8?B?RERXeFo5THk5a3VHTEZiWWNodS9oZVg5K3NaNlNRNzYwU1BXVkNkTUpKUENK?= =?utf-8?B?VnBxTTlXTkpLazA5TmwvQXQrM0lOcHZQMGFaaW1FVnVyUkxoTjR2RkpaeUhn?= =?utf-8?B?QVFYMmNwam13dlc4a083SVhuOVE0RjRkNFdyY0dIdzdMYitwQ2ZPN3hRcWp3?= =?utf-8?B?VFZBSmFVd2ZsMTIvMU5CWHljSy9GVnhYUkhZQ2NoUGRUOE9salYxUTZoYkFM?= =?utf-8?B?U3RUTURJNTZHVGZmVTFIb25iU1dVQ2tERkJuV083dkNTSkl4d3pJd2ptWCtp?= =?utf-8?B?RHIzQmwrVmh0YVg4VTVxSTJxbk5wSGlrQzYxZU9uM3JJY3ZIZkduMXJCOG5F?= =?utf-8?B?ZmdidjdOcjVjdkJXVWpOa1lnRnpLZTJiSXpJNVplN01pY2FBVk0rMDhJOGwy?= =?utf-8?B?dEhFQW1Bc2s2bGQ5cXZzcWQwSStDZGFBcldoT1l3MlpIa1djb2oxaUNnVVd6?= =?utf-8?B?bXVDY3FzZm43Q2l1RWFUZEpJbGpRTVRkaXIxajgrYUpkcU9GODA5TGc3RlFP?= =?utf-8?B?eXlHbVpjbGljOGdJVlpsTFduNjMzcEYzSUJnODVxeEFwSzA1OGcxamRLMlJP?= =?utf-8?B?S050N3U1cWFGZEZIY3lTNDJYWXhNcTgxY0d5NE95blF2bDk4ZzdFeHZNdDUy?= =?utf-8?B?RGRPUDdPRFBRa0tPcG5ScWdkcC9OQi9SQnlKdFF0Rko2UUtMZVBsNzE5TmNt?= =?utf-8?B?bStmTktmdTJ2SDRCOXlUUFNPMHJiNVdmbGRNclBoWGtmMjVqOHByRUtwOExW?= =?utf-8?B?cDJWSUh1VEFYQTBDZU03QWZoUEpsTlRQcHljdHozODB1VVg2R0dCRE1jTnRk?= =?utf-8?B?RWRBb0N6OE1Lc0U2SlNtejloTkVpUEdzZjBreUR3T1F6RFNobENFcmZHOWgx?= =?utf-8?B?RUM1Sk5jVEtxMmNtMUVHMisrREp1YW5QS2UzeFdkOVBLdDVpckRSY1RGeVdx?= =?utf-8?B?RG1EVjFJRko1dTNDcUlseUR5cnVYYkozZ0tSQkc1N0g5bExCcmV2a3lQVlN6?= =?utf-8?B?ZmZhLytVOEJOTUl1UDhwaGJjN1VtOEhENU9QLzlrNkU0bmhzdzVkejRMYkZT?= =?utf-8?B?T3h0WnJpTXlZdzRtamNuT1ZWYUM3U0RsUmI2R0hXZE94MjNOUEZWeTVyV0to?= =?utf-8?B?TFh3SjI0ZVBPT0VhVWZQRHJDdGJFM2YxT25FdzB5T1M5b09qTkRSNjZROUlu?= =?utf-8?B?aXRXVUgwMFkyYlV3TFBDTVBpNGJpZklYMDBxR2VjSE9LWlp4MnZPWmFtTVVZ?= =?utf-8?B?ZXFVZmY1a2xiK3o4di8vRUJEL0VPVlM4RW83NDVOWXJtd1dEQ01XMURHbmFy?= =?utf-8?B?Y2NFMEhTcXd2d0FqajBFM3NRRVpaV24rL2pHQ2IwN1dlUjVOVUxzaEd1RFBP?= =?utf-8?B?RzluaXJPNXdBWXZmUmdnZ0JYejNvNkllUTJSb2k5V0hyNVJidFRuS01tZjM3?= =?utf-8?B?eUZIdUNkWE02VXorT1hKWHdVblNLWmFtYjdlMjI5TlhQa3dMMGJnVUVCcjV2?= =?utf-8?B?RFZ5Y1FPZWx4b20yTDltbVFXcmdXcFNpWUVMY01IWnJuNHVEZzVyWDh5cGUr?= =?utf-8?B?Mk9uRG16VmR0emEzQ295ZWJsbEY2Zk1BYkZBdmxhbDRpZ2Zid1RnKzQ5bkZj?= =?utf-8?B?dHdoc1hNcVBpa2ZoY3AxM3lFS1YvcG1zS1l2ZUpxZ0Y5K0RLYkhleW5MTytI?= =?utf-8?B?OEJZZEdRa2Z4czBTL2N6VmNTTjRLdEJBbFU5VzhiMGxZKytFaDFicDlZVWhi?= =?utf-8?B?bi9UYXJ6YURJemNpMGx6cldPTzZsbmhrS2o1ZktLOEc4ZmNUUC9uZnp5OHRL?= =?utf-8?B?eGY0NHl2enBEV3V3bCtMWWZGMHNiY2RFa1RNQUFVQ2hWM2V1cmhJcmVBYnhK?= =?utf-8?B?eVFVazJCOWhlZE9QeXFxSmkwY2dMbmJzTUhPUEdVd3Y0bFRiOVMrOEFEVTZo?= =?utf-8?B?MHhsOTFxTnJVVk1URHJWMWg3ekN3T2xLWk5CZ1FpZWNXMTdlbGVRSnhIczVn?= =?utf-8?B?bFVoUFE3WFRzZkFRRG5US2lqN3ErektLbW9ObHJNaHNQRjFjaHJoRjlJQ3Yw?= =?utf-8?B?RXNwNFh1aFVJVjYvTnBNMlVsVTJJOGFIMndUMFJBeCtzc1FGcnBTYlNEbU1P?= =?utf-8?B?SW1STFJHc2ZEeWNzWFNxY2Nzb3ZlNC9HVkZnUjNhanlqQmtlMCtYajFpNXZO?= =?utf-8?B?ZngyYmZ2Nk1PdzRTcVNsTi9PdUZtaEJRR2NZTlI2RFo3L1dyNTJIbHhHN2JI?= =?utf-8?B?ZnhxRGNMWDNKeDdGQlYycFJMSFRnZFM5dk4xRFJQanpEQnR5YnVvT0hCUG5H?= =?utf-8?Q?Bgtp/aXwD80D3K50=3D?= X-OriginatorOrg: vaisala.com X-MS-Exchange-CrossTenant-Network-Message-Id: 3a25f806-94ef-43f4-41a6-08def7720e11 X-MS-Exchange-CrossTenant-AuthSource: AS5PR06MB9040.eurprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 11 Aug 2026 06:30:36.8960 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 6d7393e0-41f5-4c2e-9b12-4c2be5da5c57 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: PdvOfe5Ml8V/fWE/1FFSQ0278S/QquFLI2VB8qE69D90V70DCJTSfVo/vVawNNMd0VvwwWfWzewBLEuRB9YOXbaODpJQN3B6o4iSQctrMq4= X-MS-Exchange-Transport-CrossTenantHeadersStamped: AS8PR06MB7862 max310x_rs485_config() rejected delay_rts_before_send and delay_rts_after_send values larger than 0x0f with -ERANGE, which made the UART core wipe port->rs485 in uart_rs485_config() and silently disable RS485. The HDPIXDELAY register holds the setup and hold delays in 4-bit-per-direction bit-times, so even values inside that range only encode a fraction of a millisecond at typical baud rates and the chip's hardware auto-RTS path cannot cover the millisecond range the kernel UART layer expresses. Add a software-driven RTS path that takes over whenever the hardware cannot represent the requested timing: * Cache the current baud rate and the per-character on-the-wire duration in max310x_set_termios() so the decision below can use them. * max310x_set_rts_ctl_params() picks software or hardware timing: - software if delay_rts_before_send or delay_rts_after_send in milliseconds exceeds what 15 bit-times can encode at the current baud, or if the requested RTS polarity cannot be produced by the chip's auto-RTS engine; - hardware otherwise, converting the millisecond delays to bit-times (rounded up, capped at 15) and programming MODE1.TRNSCVCTRL plus IRDA.RTSINVERT to drive RTS with the requested polarity. RTS is left deasserted at idle; the chip's auto-RTS engine owns the transceiver during transmission. * When software timing is selected the RTS envelope is driven by a single hrtimer, re-used for the before- and after-send phases (the phase is tracked in tx_state), plus a single rts_work that toggles RTS. max310x_start_tx() queues rts_work to assert RTS; rts_work arms the timer for the before-send delay only after the RTS edge is on the wire, so data is never shifted before RTS is asserted. The timer expiry kicks tx_work to fill the chip FIFO; once that FIFO is empty (max310x_handle_tx()) the same timer is re-armed for one character duration plus the after-send delay, after which rts_work releases RTS. Using one timer and one rts_work keeps the before- and after-send phases mutually exclusive and the RTS toggles ordered, which matters for back-to-back writes and on SMP. * max310x_shutdown() waits for transmission to finish before powering the port down, so close() cannot truncate the final byte. On the software path it waits out any in-flight RTS envelope (bounded) so the last character and its after-send hold complete. On the hardware path it lets the chip FIFO drain and the last character clock out of the shift register: tx_empty only reports the TX FIFO empty, not the shift register, so without this the port could be powered down mid-character. * max310x_rs485_config() now clamps the delays to the UART core's RS485_MAX_RTS_DELAY (100 ms) instead of rejecting them, and cancels the pending delay timer when RS485 is disabled. * max310x_break_ctl() drives RTS manually for the break duration. The chip's auto-RTS only asserts the transceiver while FIFO data is shifting out, and a break is not FIFO data, so on the hardware path it also disables auto-RTS for the break and restores it when the break ends. Signed-off-by: Tapio Reijonen --- Changes in v2: - Use tty_get_frame_size() instead of open-coding the frame size (Jiri Slaby). The helper was left with a single expression and one caller, so it is dropped and the calculation now lives in max310x_set_termios(). tty_get_frame_size() additionally accounts for ADDRB, which the open-coded version ignored. - Use guard(spinlock_irqsave) instead of spin_lock_irqsave() and spin_unlock_irqrestore() (Jiri Slaby), in max310x_rts_work_proc(), max310x_tmr_tx() and max310x_delayed_stop_tx(). The bare spin_lock()/spin_unlock() pairs in max310x_start_tx() and max310x_rs485_config() are left alone: both are called with port->lock held by the caller and only drop it around hrtimer_cancel(), which guard() cannot express. - Link to v1: https://lore.kernel.org/r/20260709-max310x-rs485-sw-delay-v1-= 1-454ac10b937a@vaisala.com --- drivers/tty/serial/max310x.c | 383 +++++++++++++++++++++++++++++++++++++++= ---- 1 file changed, 349 insertions(+), 34 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 022502986c5fcf1ff4de9328746ddc71677be730..453c4aca2f2d9901f410d1851c1= 1d45c1403d309 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -162,6 +163,11 @@ /* IRDA register bits */ #define MAX310X_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */ #define MAX310X_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */ +#define MAX310X_IRDA_RTSINVERT_BIT (1 << 2) /* Invert auto RTS output */ + +/* HDPIXDELAY accessor macros */ +#define MAX310X_HDPIXDELAY_SETUP(val) (((val) & 0x0f) << 4) +#define MAX310X_HDPIXDELAY_HOLD(val) ((val) & 0x0f) =20 /* Flow control trigger level register masks */ #define MAX310X_FLOWLVL_HALT_MASK GENMASK(3, 0) /* Flow control halt level= */ @@ -290,12 +296,27 @@ struct max310x_devtype { u8 power_bit; /* Bit for sleep or power-off mode (active high). */ }; =20 +/* Software-timed RS485 RTS envelope phase */ +enum max310x_tx_state { + MAX310X_TX_OFF, /* idle, RTS released */ + MAX310X_TX_WAIT_BEFORE_SEND, /* RTS asserted, before-send delay */ + MAX310X_TX_SEND, /* data in flight, awaiting TX-empty */ + MAX310X_TX_WAIT_AFTER_SEND, /* data drained, after-send hold */ +}; + struct max310x_one { struct uart_port port; struct work_struct tx_work; struct work_struct md_work; struct work_struct rs_work; + struct work_struct rts_work; + struct hrtimer tx_delay_tmr; struct regmap *regmap; + ktime_t one_character_duration; + unsigned int baud; + bool sw_rts_during_tx; + bool cancel_tx_delay_tmr; + enum max310x_tx_state tx_state; =20 u8 rx_buf[MAX310X_FIFO_SIZE]; }; @@ -680,6 +701,37 @@ static void max310x_batch_read(struct uart_port *port,= u8 *rxbuf, unsigned int l regmap_noinc_read(one->regmap, MAX310X_RHR_REG, rxbuf, len); } =20 +static void max310x_rts_ctl(struct uart_port *port, bool rts_state) +{ + max310x_port_update(port, MAX310X_LCR_REG, MAX310X_LCR_RTS_BIT, + rts_state ? MAX310X_LCR_RTS_BIT : 0); +} + +/* + * Drive the RS485 RTS line to match the current tx_state. This is the only + * place that touches RTS, and it reads tx_state rather than a fixed + * assert/deassert intent, so a newer assert is never clobbered by a stale + * release. It also arms the before-send timer once the RTS edge is on the= wire, + * so data is never shifted before RTS is asserted. + */ +static void max310x_rts_work_proc(struct work_struct *ws) +{ + struct max310x_one *one =3D container_of(ws, struct max310x_one, rts_work= ); + struct uart_port *port =3D &one->port; + bool rts_on =3D READ_ONCE(one->tx_state) !=3D MAX310X_TX_OFF; + + max310x_rts_ctl(port, rts_on ? + (port->rs485.flags & SER_RS485_RTS_ON_SEND) : + (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)); + + guard(spinlock_irqsave)(&port->lock); + if (READ_ONCE(one->tx_state) =3D=3D MAX310X_TX_WAIT_BEFORE_SEND && + !one->cancel_tx_delay_tmr && !hrtimer_active(&one->tx_delay_tmr)) + hrtimer_start(&one->tx_delay_tmr, + ms_to_ktime(port->rs485.delay_rts_before_send), + HRTIMER_MODE_REL); +} + static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen) { struct max310x_one *one =3D to_max310x_port(port); @@ -776,6 +828,71 @@ static void max310x_handle_rx(struct uart_port *port, = unsigned int rxlen) tty_flip_buffer_push(&port->state->port); } =20 +static enum hrtimer_restart max310x_tmr_tx(struct hrtimer *timer) +{ + struct max310x_one *one =3D container_of(timer, struct max310x_one, + tx_delay_tmr); + + guard(spinlock_irqsave)(&one->port.lock); + if (!one->cancel_tx_delay_tmr) { + if (READ_ONCE(one->tx_state) =3D=3D MAX310X_TX_WAIT_AFTER_SEND) { + /* After-send hold elapsed: drop RTS via the rts worker. */ + WRITE_ONCE(one->tx_state, MAX310X_TX_OFF); + schedule_work(&one->rts_work); + } else { + WRITE_ONCE(one->tx_state, MAX310X_TX_SEND); + schedule_work(&one->tx_work); + } + } + + return HRTIMER_NORESTART; +} + +static void max310x_delayed_stop_tx(struct uart_port *port) +{ + struct max310x_one *one =3D to_max310x_port(port); + unsigned int txlvl; + + if (READ_ONCE(one->tx_state) =3D=3D MAX310X_TX_OFF) + return; + + /* + * The kfifo can be empty while the chip TX FIFO is still draining, so arm + * the after-send hold only once the chip FIFO is empty too - the TX-empty + * interrupt re-invokes us then. Otherwise the hold starts early and RTS + * drops mid-character, clipping the last byte(s). + */ + txlvl =3D max310x_port_read(port, MAX310X_TXFIFOLVL_REG); + if (txlvl) + return; + + /* + * Runs from the tx_work worker, which does not hold port->lock. Take it + * here so cancel_tx_delay_tmr, tx_state and the timer are updated + * atomically against start_tx() and the timer callback. + */ + guard(spinlock_irqsave)(&one->port.lock); + one->cancel_tx_delay_tmr =3D false; + /* + * Do not arm the after-send hold if a before-send phase is pending: a + * new burst's start_tx() may have set WAIT_BEFORE_SEND while its timer is + * not yet armed (max310x_rts_work_proc() arms it after the RTS edge). + * That burst owns the line and runs its own envelope. + */ + if (READ_ONCE(one->tx_state) !=3D MAX310X_TX_WAIT_BEFORE_SEND && + !hrtimer_active(&one->tx_delay_tmr)) { + /* + * Add one character for the byte still in the shift register - + * TX-empty fires as it enters, not as it leaves. + */ + ktime_t delay =3D ktime_add_ms(one->one_character_duration, + port->rs485.delay_rts_after_send); + + WRITE_ONCE(one->tx_state, MAX310X_TX_WAIT_AFTER_SEND); + hrtimer_start(&one->tx_delay_tmr, delay, HRTIMER_MODE_REL); + } +} + static void max310x_handle_tx(struct uart_port *port) { struct tty_port *tport =3D &port->state->port; @@ -787,8 +904,13 @@ static void max310x_handle_tx(struct uart_port *port) return; } =20 - if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { + struct max310x_one *one =3D to_max310x_port(port); + + if (one->sw_rts_during_tx) + max310x_delayed_stop_tx(port); return; + } =20 /* * It's a circ buffer -- wrap around. @@ -813,11 +935,48 @@ static void max310x_handle_tx(struct uart_port *port) uart_write_wakeup(port); } =20 +/* + * Begin a software-timed RTS envelope: set the before-send phase and queu= e the + * rts worker to assert RTS. tx_state is set synchronously here (start_tx(= ) holds + * port.lock) so close()/shutdown can see an envelope is in flight; rts_wo= rk then + * asserts RTS and arms the before-send timer (see there). + */ +static void max310x_delayed_start_tx(struct uart_port *port) +{ + struct max310x_one *one =3D to_max310x_port(port); + + WRITE_ONCE(one->tx_state, MAX310X_TX_WAIT_BEFORE_SEND); + one->cancel_tx_delay_tmr =3D false; + schedule_work(&one->rts_work); +} + +/* called with port.lock taken and irqs off */ static void max310x_start_tx(struct uart_port *port) { struct max310x_one *one =3D to_max310x_port(port); =20 - schedule_work(&one->tx_work); + if (one->sw_rts_during_tx) { + /* + * The before- and after-send phases share one delay timer. If an + * after-send release is pending, cancel it before starting a new + * TX so the just-asserted RTS is not yanked; re-arming the timer + * for the before-send phase then supersedes the release. + */ + int res =3D 0; + + if (READ_ONCE(one->tx_state) =3D=3D MAX310X_TX_WAIT_AFTER_SEND) + res =3D hrtimer_try_to_cancel(&one->tx_delay_tmr); + if (unlikely(res =3D=3D -1)) { + one->cancel_tx_delay_tmr =3D true; + spin_unlock(&one->port.lock); + hrtimer_cancel(&one->tx_delay_tmr); + spin_lock(&one->port.lock); + } + + max310x_delayed_start_tx(port); + } else { + schedule_work(&one->tx_work); + } } =20 static irqreturn_t max310x_port_irq(struct max310x_port *s, int portno) @@ -843,7 +1002,7 @@ static irqreturn_t max310x_port_irq(struct max310x_por= t *s, int portno) if (rxlen) max310x_handle_rx(port, rxlen); if (ists & MAX310X_IRQ_TXEMPTY_BIT) - max310x_start_tx(port); + schedule_work(&s->p[portno].tx_work); } while (1); =20 return res; @@ -927,15 +1086,101 @@ static void max310x_set_mctrl(struct uart_port *por= t, unsigned int mctrl) =20 static void max310x_break_ctl(struct uart_port *port, int break_state) { + struct max310x_one *one =3D to_max310x_port(port); + max310x_port_update(port, MAX310X_LCR_REG, MAX310X_LCR_TXBREAK_BIT, break_state ? MAX310X_LCR_TXBREAK_BIT : 0); + + if (!(port->rs485.flags & SER_RS485_ENABLED)) + return; + + /* + * Drive RTS manually for the break duration. HW auto-RTS only asserts + * the transceiver while FIFO data is shifting out, and a break is not + * FIFO data, so on the HW path also disable auto-RTS for the break and + * restore it when the break ends. + */ + if (!one->sw_rts_during_tx) + max310x_port_update(port, MAX310X_MODE1_REG, + MAX310X_MODE1_TRNSCVCTRL_BIT, + break_state ? 0 : MAX310X_MODE1_TRNSCVCTRL_BIT); + + max310x_rts_ctl(port, break_state); +} + +/* + * Pick hardware or software RTS timing for the current port. The chip can + * deliver up to 15 bit-times of setup/hold delay via HDPIXDELAY; anything + * longer (or any RTS polarity the chip cannot produce automatically) must + * be driven by software via tx_delay_tmr and rts_work. + */ +static void max310x_set_rts_ctl_params(struct max310x_one *one) +{ + const unsigned int max_bit_dly =3D 15; + struct uart_port *port =3D &one->port; + unsigned long max_hw_delay_ns =3D 0; + unsigned int setup =3D 0, hold =3D 0; + u8 mode1 =3D 0, irda =3D 0; + + one->sw_rts_during_tx =3D false; + + if (!(port->rs485.flags & SER_RS485_ENABLED)) + goto out; + + if (one->baud) + max_hw_delay_ns =3D NSEC_PER_SEC / one->baud * max_bit_dly; + + if ((u64)port->rs485.delay_rts_before_send * NSEC_PER_MSEC > max_hw_delay= _ns || + (u64)port->rs485.delay_rts_after_send * NSEC_PER_MSEC > max_hw_delay= _ns || + !!(port->rs485.flags & SER_RS485_RTS_ON_SEND) =3D=3D + !!(port->rs485.flags & SER_RS485_RTS_AFTER_SEND)) + one->sw_rts_during_tx =3D true; + + if (one->sw_rts_during_tx) { + setup =3D 0; + hold =3D 0; + goto out; + } + + /* Convert milliseconds to bit-times, rounding up. */ + setup =3D DIV_ROUND_UP(one->baud * port->rs485.delay_rts_before_send, + MSEC_PER_SEC); + hold =3D DIV_ROUND_UP(one->baud * port->rs485.delay_rts_after_send, + MSEC_PER_SEC); + setup =3D min(setup, max_bit_dly); + hold =3D min(hold, max_bit_dly); + +out: + max310x_port_write(port, MAX310X_HDPIXDELAY_REG, + MAX310X_HDPIXDELAY_SETUP(setup) | + MAX310X_HDPIXDELAY_HOLD(hold)); + + if (port->rs485.flags & SER_RS485_ENABLED) { + if (one->sw_rts_during_tx) { + max310x_rts_ctl(port, + port->rs485.flags & + SER_RS485_RTS_AFTER_SEND); + } else { + mode1 =3D MAX310X_MODE1_TRNSCVCTRL_BIT; + if (!(port->rs485.flags & SER_RS485_RTS_ON_SEND)) + irda =3D MAX310X_IRDA_RTSINVERT_BIT; + } + } else { + max310x_rts_ctl(port, 0); + } + + max310x_port_update(port, MAX310X_MODE1_REG, + MAX310X_MODE1_TRNSCVCTRL_BIT, mode1); + max310x_port_update(port, MAX310X_IRDA_REG, + MAX310X_IRDA_RTSINVERT_BIT, irda); } =20 static void max310x_set_termios(struct uart_port *port, struct ktermios *termios, const struct ktermios *old) { + unsigned int frame_bits =3D tty_get_frame_size(termios->c_cflag); unsigned int lcr =3D 0, flow =3D 0; int baud; =20 @@ -1042,38 +1287,55 @@ static void max310x_set_termios(struct uart_port *p= ort, =20 /* Update timeout according to new baud rate */ uart_update_timeout(port, termios->c_cflag, baud); + + /* + * Cache the new baud rate and the time it takes to clock out one + * character so the RTS-timing decision in max310x_set_rts_ctl_params() + * and the post-TX delay in max310x_delayed_stop_tx() can use them. + */ + to_max310x_port(port)->baud =3D baud; + to_max310x_port(port)->one_character_duration =3D + us_to_ktime(DIV_ROUND_UP(USEC_PER_SEC * frame_bits, baud)); + max310x_set_rts_ctl_params(to_max310x_port(port)); } =20 static void max310x_rs_proc(struct work_struct *ws) { struct max310x_one *one =3D container_of(ws, struct max310x_one, rs_work); - unsigned int delay, mode1 =3D 0, mode2 =3D 0; + unsigned int mode2 =3D 0; =20 - delay =3D (one->port.rs485.delay_rts_before_send << 4) | - one->port.rs485.delay_rts_after_send; - max310x_port_write(&one->port, MAX310X_HDPIXDELAY_REG, delay); + max310x_set_rts_ctl_params(one); =20 - if (one->port.rs485.flags & SER_RS485_ENABLED) { - mode1 =3D MAX310X_MODE1_TRNSCVCTRL_BIT; + if (one->port.rs485.flags & SER_RS485_ENABLED && + !(one->port.rs485.flags & SER_RS485_RX_DURING_TX)) + mode2 =3D MAX310X_MODE2_ECHOSUPR_BIT; =20 - if (!(one->port.rs485.flags & SER_RS485_RX_DURING_TX)) - mode2 =3D MAX310X_MODE2_ECHOSUPR_BIT; - } - - max310x_port_update(&one->port, MAX310X_MODE1_REG, - MAX310X_MODE1_TRNSCVCTRL_BIT, mode1); max310x_port_update(&one->port, MAX310X_MODE2_REG, - MAX310X_MODE2_ECHOSUPR_BIT, mode2); + MAX310X_MODE2_ECHOSUPR_BIT, mode2); } =20 +/* called with port.lock taken and irqs off */ static int max310x_rs485_config(struct uart_port *port, struct ktermios *t= ermios, struct serial_rs485 *rs485) { struct max310x_one *one =3D to_max310x_port(port); =20 - if ((rs485->delay_rts_before_send > 0x0f) || - (rs485->delay_rts_after_send > 0x0f)) - return -ERANGE; + rs485->delay_rts_before_send =3D min(rs485->delay_rts_before_send, 100U); + rs485->delay_rts_after_send =3D min(rs485->delay_rts_after_send, 100U); + + /* + * Make sure no SW-timed RTS toggle survives an RS485 disable, even + * if the delay timer happens to be running right now. + */ + if (!(rs485->flags & SER_RS485_ENABLED)) { + one->cancel_tx_delay_tmr =3D true; + if (hrtimer_try_to_cancel(&one->tx_delay_tmr) =3D=3D -1) { + spin_unlock(&port->lock); + hrtimer_cancel(&one->tx_delay_tmr); + spin_lock(&port->lock); + } + WRITE_ONCE(one->tx_state, MAX310X_TX_OFF); + } =20 port->rs485 =3D *rs485; =20 @@ -1084,6 +1346,7 @@ static int max310x_rs485_config(struct uart_port *por= t, struct ktermios *termios =20 static int max310x_startup(struct uart_port *port) { + struct max310x_one *one =3D to_max310x_port(port); unsigned int val; =20 max310x_power(port, 1); @@ -1098,21 +1361,20 @@ static int max310x_startup(struct uart_port *port) max310x_port_update(port, MAX310X_MODE2_REG, MAX310X_MODE2_FIFORST_BIT, 0); =20 - /* Configure mode1/mode2 to have rs485/rs232 enabled at startup */ - val =3D (clamp(port->rs485.delay_rts_before_send, 0U, 15U) << 4) | - clamp(port->rs485.delay_rts_after_send, 0U, 15U); - max310x_port_write(port, MAX310X_HDPIXDELAY_REG, val); - - if (port->rs485.flags & SER_RS485_ENABLED) { - max310x_port_update(port, MAX310X_MODE1_REG, - MAX310X_MODE1_TRNSCVCTRL_BIT, - MAX310X_MODE1_TRNSCVCTRL_BIT); + /* + * Configure RTS timing (HW auto-RTS vs software-driven) and the + * RS485/RS232 mode bits. Don't hardcode HW auto-RTS here - let + * max310x_set_rts_ctl_params() pick HW or SW per the configured + * delays, otherwise the chip's auto-RTS would override the + * software RTS hold and the after-send delay is lost. + */ + max310x_set_rts_ctl_params(one); =20 - if (!(port->rs485.flags & SER_RS485_RX_DURING_TX)) - max310x_port_update(port, MAX310X_MODE2_REG, - MAX310X_MODE2_ECHOSUPR_BIT, - MAX310X_MODE2_ECHOSUPR_BIT); - } + if (port->rs485.flags & SER_RS485_ENABLED && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) + max310x_port_update(port, MAX310X_MODE2_REG, + MAX310X_MODE2_ECHOSUPR_BIT, + MAX310X_MODE2_ECHOSUPR_BIT); =20 /* * Configure flow control levels: @@ -1134,9 +1396,54 @@ static int max310x_startup(struct uart_port *port) =20 static void max310x_shutdown(struct uart_port *port) { + struct max310x_one *one =3D to_max310x_port(port); + + /* + * Drain any in-flight software-timed RTS envelope before the port is + * powered down, so the last character and its after-send hold complete + * - close() can reach shutdown with data still queued and a before-send + * delay pending. The loop ends when the envelope does (tx_state =3D=3D O= FF); + * the bound is just a worst-case safety cap. Then stop the timer and work + * so neither runs against a powered-off port. + */ + if (one->sw_rts_during_tx) { + unsigned int loops =3D port->rs485.delay_rts_before_send + + port->rs485.delay_rts_after_send + + DIV_ROUND_UP_ULL((kfifo_len(&port->state->port.xmit_fifo) + + port->fifosize) * + ktime_to_us(one->one_character_duration), + USEC_PER_MSEC); + + while (READ_ONCE(one->tx_state) !=3D MAX310X_TX_OFF && loops-- > 0) + fsleep(USEC_PER_MSEC); + + one->cancel_tx_delay_tmr =3D true; + hrtimer_cancel(&one->tx_delay_tmr); + cancel_work_sync(&one->rts_work); + WRITE_ONCE(one->tx_state, MAX310X_TX_OFF); + } else { + /* + * HW auto-RTS path: the tty layer waits for tx_empty before + * close(), but tx_empty only reflects the chip TX FIFO - the + * last character may still be in the transmit shift register. + * Let the FIFO drain and the final character clock out before + * the port is powered down, otherwise close() truncates the last + * byte on the wire as the chip auto-RTS turnaround clips it. + */ + unsigned int loops =3D port->fifosize + 1; + + while (!max310x_tx_empty(port) && loops-- > 0) + fsleep(ktime_to_us(one->one_character_duration)); + fsleep(ktime_to_us(one->one_character_duration)); + } + /* Disable all interrupts */ max310x_port_write(port, MAX310X_IRQEN_REG, 0); =20 + if (one->sw_rts_during_tx) + max310x_rts_ctl(port, + port->rs485.flags & SER_RS485_RTS_AFTER_SEND); + max310x_power(port, 0); } =20 @@ -1291,7 +1598,8 @@ static int max310x_gpio_set_config(struct gpio_chip *= chip, unsigned int offset, #endif =20 static const struct serial_rs485 max310x_rs485_supported =3D { - .flags =3D SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURIN= G_TX, + .flags =3D SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND | SER_RS485_RX_DURING_TX, .delay_rts_before_send =3D 1, .delay_rts_after_send =3D 1, }; @@ -1425,6 +1733,11 @@ static int max310x_probe(struct device *dev, const s= truct max310x_devtype *devty INIT_WORK(&s->p[i].md_work, max310x_md_proc); /* Initialize queue for changing RS485 mode */ INIT_WORK(&s->p[i].rs_work, max310x_rs_proc); + /* Initialize queue for software-driven RTS toggling */ + INIT_WORK(&s->p[i].rts_work, max310x_rts_work_proc); + hrtimer_setup(&s->p[i].tx_delay_tmr, max310x_tmr_tx, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + s->p[i].tx_state =3D MAX310X_TX_OFF; } =20 #ifdef CONFIG_GPIOLIB @@ -1535,6 +1848,8 @@ static void max310x_remove(struct device *dev) int i; =20 for (i =3D 0; i < s->devtype->nr; i++) { + hrtimer_cancel(&s->p[i].tx_delay_tmr); + cancel_work_sync(&s->p[i].rts_work); cancel_work_sync(&s->p[i].tx_work); cancel_work_sync(&s->p[i].md_work); cancel_work_sync(&s->p[i].rs_work); --- base-commit: 9505146e885b1a842118aa6410f737290c4a5a32 change-id: 20260513-max310x-rs485-sw-delay-a306d783d529 Best regards, --=20 Tapio Reijonen