Received: by 2002:ab2:6309:0:b0:1fb:d597:ff75 with SMTP id s9csp902343lqt; Fri, 7 Jun 2024 02:03:06 -0700 (PDT) X-Forwarded-Encrypted: i=3; AJvYcCUFfbgwG4On1vLajQ+TGH6CSVIGRHKUqBL8+9+Z33H2hNhTIBrZS/KpXn/Oi5h/ulQqEJlt5cYO4QJo0AduFzt79XSUnQHTy7Zh7pqBNQ== X-Google-Smtp-Source: AGHT+IHxKnX4nGfnU6wBgCTolv84fPbscHxIZqFu1jGeqpfbj/XneVgoq8hVJc2dXbxw7iMDP5EH X-Received: by 2002:a05:6214:27ec:b0:6a9:fdfb:9877 with SMTP id 6a1803df08f44-6b04c009083mr92359736d6.12.1717750986405; Fri, 07 Jun 2024 02:03:06 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1717750986; cv=pass; d=google.com; s=arc-20160816; b=FyN49ViJX9Ws0ZWJiOErZAdg2gK23YtI65oDCbqu8KbRMG4/kGDJgn5smcHOcS/GT5 sUzGtPhWslwpaxmR3lTqeKvtTOr+KcahKHvAyu8VZt40D1svJJEY2ZJR/OUyfk1A7gFF m3r6UeyRE9X9TQCqjjRSuM4rKIscLs9KPo35unfA/RbJvgo7+7L84ge+LBwOlXkifOrp oHze6YZIr2KkxDzEzI+BfJlxCJ3ZAV3XrA8RJ6ekOBdyH7SumrWLoSdsyxb5Zj8viSh2 LSs3XBbLeHmjdWDI37kpQ24B8Pp68ZaFV6ORnjLSsu0Rkl3K1Sv/6bBbIjzuPoDS5UAC tUJQ== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=mime-version:list-unsubscribe:list-subscribe:list-id:precedence :references:message-id:in-reply-to:subject:cc:to:date:from :dkim-signature; bh=2CiZaUsSqQrTRln0ILlZUpwayk1nJD/yCXGFIlmwLVw=; fh=3IF1cz3KItUcvjpdv3TyW3tKeo46kc4tMBbQUJhdZ44=; b=mFLVntPWJlaWzX6GfJ7pNRCG/WIHQmLBITocLULyOTtEaBs6coCEFznWi9MdaL6/en EEfMVpytGOx/ahQ8+kNP0qPSjTwfPaY1BJoyXw9YKLrl29lg6ruWntVRCP3DmDZ2XXg+ p60J3v/Dt/j+U65pj9VzHlJL3UQI2xnjX8t1ih8BG13tLQpnbmy/4Gq/UCmeOg/QDLQK r9rbnl6geTLcYK3YB8wdAZ7p/Kbjn19iM09zv3O3L/qaP1/x5Lek+h1THRGXPkhwF+0q wPXQ9gqYSxYTfW/EvJkC1Y31ED5OW2mOAwKWtwsr7RnjYZ9WxAAio6NtE3RJ0P2SElqu qJmg==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=fZO+zAtL; arc=pass (i=1 dkim=pass dkdomain=intel.com dmarc=pass fromdomain=linux.intel.com); spf=pass (google.com: domain of linux-kernel+bounces-205555-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.199.223 as permitted sender) smtp.mailfrom="linux-kernel+bounces-205555-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from ny.mirrors.kernel.org (ny.mirrors.kernel.org. [147.75.199.223]) by mx.google.com with ESMTPS id 6a1803df08f44-6b04f63c91csi36515486d6.13.2024.06.07.02.03.06 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Jun 2024 02:03:06 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel+bounces-205555-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.199.223 as permitted sender) client-ip=147.75.199.223; Authentication-Results: mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=fZO+zAtL; arc=pass (i=1 dkim=pass dkdomain=intel.com dmarc=pass fromdomain=linux.intel.com); spf=pass (google.com: domain of linux-kernel+bounces-205555-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.199.223 as permitted sender) smtp.mailfrom="linux-kernel+bounces-205555-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ny.mirrors.kernel.org (Postfix) with ESMTPS id 7D6E41C23E79 for ; Fri, 7 Jun 2024 07:43:32 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 1DAE4156C6E; Fri, 7 Jun 2024 07:43:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="fZO+zAtL" Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.14]) (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 D7E3D155C93; Fri, 7 Jun 2024 07:43:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.14 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717746184; cv=none; b=qKhXCtmilFark/WWrM1al6Xtr5DFPD+YZVnjv0U6iFophDZ4xJUioPR5qyfkiSjEwlfCf6+1aG0q1jEDI8Mi3kvBYgl/oL+ifWpvYpT8Dzf3T+BuuDaLZ0TeYoHtspna2PixEgvnimQTPQWVtx7d6YTNwiuioosZzoCS4ngLH3Q= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717746184; c=relaxed/simple; bh=2Fj1r20Ipaa909NRGvY2/vVd1dWiYoUgS1kgPkKVQV0=; h=From:Date:To:cc:Subject:In-Reply-To:Message-ID:References: MIME-Version:Content-Type; b=gLvsWiPyQQogsUP0YTiZgzqSRw8u++Un/ay65wammbPMsAEtePrmJMhep7B2UDStNYIV7w9Haaj6+grm06wn7ZtfFNh76nikygbewJd3P5mNx3XwxHGvfknwxFU/zfgCSSu6kG9Snl9vsxdz3L2mMJe7nYAqjKAY/QWIGUklMkY= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=none smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=fZO+zAtL; arc=none smtp.client-ip=192.198.163.14 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.intel.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1717746183; x=1749282183; h=from:date:to:cc:subject:in-reply-to:message-id: references:mime-version; bh=2Fj1r20Ipaa909NRGvY2/vVd1dWiYoUgS1kgPkKVQV0=; b=fZO+zAtL5AClQB9pO1vMzbzowTJeAU2dXBuy/Ui1kmKok5RPlddNNHwb /FH58sRrZ/sTVPz2PgNUC1Zi2apu7rQNOw814nw4j0SxhEx3sdiotdz8L Smek/Xzs8JBIFkPPFmD7503qvdtPX9gVf7i4qU7dIs+cMlRI2YqrLWTQu yXqk6dC+xx1LVK9KRAanKYPP68CTc2PnFLHkfoiyaNysYWg1oC3OSRzrz Qv20XLKkn5NCpii/4qDTPSkbW8XZPVZMraUnK7ydzknwVyq+PCJpwHLDQ fqHy/HOEXhFrNOjOY0nnBLcFUwj15onOSZ5Ycgbyvn7hqWhoQ/022a2Xe g==; X-CSE-ConnectionGUID: Og1sWsHDRDOHpqIVeTMDlQ== X-CSE-MsgGUID: KAFc4zjxQha7yVAHRMCdMA== X-IronPort-AV: E=McAfee;i="6600,9927,11095"; a="14680065" X-IronPort-AV: E=Sophos;i="6.08,220,1712646000"; d="scan'208";a="14680065" Received: from fmviesa007.fm.intel.com ([10.60.135.147]) by fmvoesa108.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 07 Jun 2024 00:43:02 -0700 X-CSE-ConnectionGUID: dqZkUrA/QGGjaGFIncyaUA== X-CSE-MsgGUID: DmNFJIUVRFOrmEQ3ncBgxw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.08,220,1712646000"; d="scan'208";a="38096713" Received: from ijarvine-desk1.ger.corp.intel.com (HELO localhost) ([10.245.247.184]) by fmviesa007-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 07 Jun 2024 00:42:58 -0700 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Fri, 7 Jun 2024 10:42:54 +0300 (EEST) To: Douglas Anderson cc: Greg Kroah-Hartman , Jiri Slaby , =?ISO-8859-15?Q?Uwe_Kleine-K=F6nig?= , Andy Shevchenko , linux-arm-msm@vger.kernel.org, Konrad Dybcio , LKML , linux-serial , John Ogness , Yicong Yang , Tony Lindgren , Stephen Boyd , Johan Hovold , Bjorn Andersson , Thomas Gleixner , Vijaya Krishna Nivarthi Subject: Re: [PATCH v3 6/7] serial: qcom-geni: Fix suspend while active UART xfer In-Reply-To: <20240604090028.v3.6.I0f81a5baa37d368f291c96ee4830abca337e3c87@changeid> Message-ID: <2c5c3d46-5fe2-6678-34ea-647c28f5a4f0@linux.intel.com> References: <20240604160123.2029413-1-dianders@chromium.org> <20240604090028.v3.6.I0f81a5baa37d368f291c96ee4830abca337e3c87@changeid> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII On Tue, 4 Jun 2024, Douglas Anderson wrote: > On devices using Qualcomm's GENI UART it is possible to get the UART > stuck such that it no longer outputs data. Specifically, logging in > via an agetty on the debug serial port (which was _not_ used for > kernel console) and running: > cat /var/log/messages > ...and then (via an SSH session) forcing a few suspend/resume cycles > causes the UART to stop transmitting. > > The root of the problems was with qcom_geni_serial_stop_tx_fifo() > which is called as part of the suspend process. Specific problems with > that function: > - When an in-progress "tx" command is cancelled it doesn't appear to > fully drain the FIFO. That meant qcom_geni_serial_tx_empty() > continued to report that the FIFO wasn't empty. The > qcom_geni_serial_start_tx_fifo() function didn't re-enable > interrupts in this case so the driver would never start transferring > again. > - When the driver cancelled the current "tx" command but it forgot to > zero out "tx_remaining". This confused logic elsewhere in the > driver. > - From experimentation, it appears that cancelling the "tx" command > could drop some of the queued up bytes. > > While qcom_geni_serial_stop_tx_fifo() could be fixed to drain the FIFO > and shut things down properly, stop_tx() isn't supposed to be a slow > function. It is run with local interrupts off and is documented to > stop transmitting "as soon as possible". Change the function to just > stop new bytes from being queued. In order to make this work, change > qcom_geni_serial_start_tx_fifo() to remove some conditions. It's > always safe to enable the watermark interrupt and the IRQ handler will > disable it if it's not needed. > > For system suspend the queue still needs to be drained. Failure to do > so means that the hardware won't provide new interrupts until a > "cancel" command is sent. Add draining logic (fixing the issues noted > above) at suspend time. > > NOTE: It would be ideal if qcom_geni_serial_stop_tx_fifo() could > "pause" the transmitter right away. There is no obvious way to do this > in the docs and experimentation didn't find any tricks either, so > stopping TX "as soon as possible" isn't very soon but is the best > possible. > > Fixes: c4f528795d1a ("tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP") > Signed-off-by: Douglas Anderson > --- > There are still a number of problems with GENI UART after this but > I've kept this change separate to make it easier to understand. > Specifically on mainline just hitting "Ctrl-C" after dumping > /var/log/messages to the serial port hangs things after the kfifo > changes. Those issues will be addressed in future patches. > > It should also be noted that the "Fixes" tag here is a bit of a > swag. I haven't gone and tested on ancient code, but at least the > problems exist on kernel 5.15 and much of the code touched here has > been here since the beginning, or at least since as long as the driver > was stable. > > Changes in v3: > - 0xffffffff => GENMASK(31, 0) > - Reword commit message. > > Changes in v2: > - Totally rework / rename patch to handle suspend while active xfer > > drivers/tty/serial/qcom_geni_serial.c | 97 +++++++++++++++++++++------ > 1 file changed, 75 insertions(+), 22 deletions(-) > > diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c > index 4dbc59873b34..46b6674d90c5 100644 > --- a/drivers/tty/serial/qcom_geni_serial.c > +++ b/drivers/tty/serial/qcom_geni_serial.c > @@ -130,6 +130,7 @@ struct qcom_geni_serial_port { > bool brk; > > unsigned int tx_remaining; > + unsigned int tx_total; > int wakeup_irq; > bool rx_tx_swap; > bool cts_rts_swap; > @@ -311,11 +312,14 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport, > > static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size) > { > + struct qcom_geni_serial_port *port = to_dev_port(uport); > u32 m_cmd; > > writel(xmit_size, uport->membase + SE_UART_TX_TRANS_LEN); > m_cmd = UART_START_TX << M_OPCODE_SHFT; Unrelated to this patch and won't belong to this patch but I noticed it while reviewing. This could be converted into: m_cmd = FIELD_PREP(M_OPCODE_MSK, UART_START_TX); (and after converting the other use in the header file, the SHFT define becomes unused). > writel(m_cmd, uport->membase + SE_GENI_M_CMD0); > + > + port->tx_total = xmit_size; > } > > static void qcom_geni_serial_poll_tx_done(struct uart_port *uport) > @@ -335,6 +339,64 @@ static void qcom_geni_serial_poll_tx_done(struct uart_port *uport) > writel(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR); > } > > +static void qcom_geni_serial_drain_tx_fifo(struct uart_port *uport) > +{ > + struct qcom_geni_serial_port *port = to_dev_port(uport); > + > + /* > + * If the main sequencer is inactive it means that the TX command has > + * been completed and all bytes have been sent. Nothing to do in that > + * case. > + */ > + if (!qcom_geni_serial_main_active(uport)) > + return; > + > + /* > + * Wait until the FIFO has been drained. We've already taken bytes out > + * of the higher level queue in qcom_geni_serial_send_chunk_fifo() so > + * if we don't drain the FIFO but send the "cancel" below they seem to > + * get lost. > + */ > + qcom_geni_serial_poll_bitfield(uport, SE_GENI_M_GP_LENGTH, GENMASK(31, 0), That GENMASK(31, 0) is a field in a register (even if it covers the entire register)? It should be named with a define instead of creating the field mask here in an online fashion. > + port->tx_total - port->tx_remaining); > + > + /* > + * If clearing the FIFO made us inactive then we're done--no need for > + * a cancel. > + */ > + if (!qcom_geni_serial_main_active(uport)) > + return; > + > + /* > + * Cancel the current command. After this the main sequencer will > + * stop reporting that it's active and we'll have to start a new > + * transfer command. > + * > + * If we skip doing this cancel and then continue with a system > + * suspend while there's an active command in the main sequencer > + * then after resume time we won't get any more interrupts on the > + * main sequencer until we send the cancel. > + */ > + geni_se_cancel_m_cmd(&port->se); > + if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, > + M_CMD_CANCEL_EN, true)) { > + /* The cancel failed; try an abort as a fallback. */ > + geni_se_abort_m_cmd(&port->se); > + qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, > + M_CMD_ABORT_EN, true); Misaligned. -- i. > + writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); > + } > + writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); > + > + /* > + * We've cancelled the current command. "tx_remaining" stores how > + * many bytes are left to finish in the current command so we know > + * when to start a new command. Since the command was cancelled we > + * need to zero "tx_remaining". > + */ > + port->tx_remaining = 0; > +} > + > static void qcom_geni_serial_abort_rx(struct uart_port *uport) > { > u32 irq_clear = S_CMD_DONE_EN | S_CMD_ABORT_EN; > @@ -655,37 +717,18 @@ static void qcom_geni_serial_start_tx_fifo(struct uart_port *uport) > { > u32 irq_en; > > - if (qcom_geni_serial_main_active(uport) || > - !qcom_geni_serial_tx_empty(uport)) > - return; > - > irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); > irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN; > - > writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); > } > > static void qcom_geni_serial_stop_tx_fifo(struct uart_port *uport) > { > u32 irq_en; > - struct qcom_geni_serial_port *port = to_dev_port(uport); > > irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); > irq_en &= ~(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN); > writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); > - /* Possible stop tx is called multiple times. */ > - if (!qcom_geni_serial_main_active(uport)) > - return; > - > - geni_se_cancel_m_cmd(&port->se); > - if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, > - M_CMD_CANCEL_EN, true)) { > - geni_se_abort_m_cmd(&port->se); > - qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, > - M_CMD_ABORT_EN, true); > - writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); > - } > - writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); > } > > static void qcom_geni_serial_handle_rx_fifo(struct uart_port *uport, bool drop) > @@ -1067,7 +1110,15 @@ static int setup_fifos(struct qcom_geni_serial_port *port) > } > > > -static void qcom_geni_serial_shutdown(struct uart_port *uport) > +static void qcom_geni_serial_shutdown_dma(struct uart_port *uport) > +{ > + disable_irq(uport->irq); > + > + qcom_geni_serial_stop_tx(uport); > + qcom_geni_serial_stop_rx(uport); > +} > + > +static void qcom_geni_serial_shutdown_fifo(struct uart_port *uport) > { > disable_irq(uport->irq); > > @@ -1076,6 +1127,8 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport) > > qcom_geni_serial_stop_tx(uport); > qcom_geni_serial_stop_rx(uport); > + > + qcom_geni_serial_drain_tx_fifo(uport); > } > > static int qcom_geni_serial_port_setup(struct uart_port *uport) > @@ -1533,7 +1586,7 @@ static const struct uart_ops qcom_geni_console_pops = { > .startup = qcom_geni_serial_startup, > .request_port = qcom_geni_serial_request_port, > .config_port = qcom_geni_serial_config_port, > - .shutdown = qcom_geni_serial_shutdown, > + .shutdown = qcom_geni_serial_shutdown_fifo, > .type = qcom_geni_serial_get_type, > .set_mctrl = qcom_geni_serial_set_mctrl, > .get_mctrl = qcom_geni_serial_get_mctrl, > @@ -1555,7 +1608,7 @@ static const struct uart_ops qcom_geni_uart_pops = { > .startup = qcom_geni_serial_startup, > .request_port = qcom_geni_serial_request_port, > .config_port = qcom_geni_serial_config_port, > - .shutdown = qcom_geni_serial_shutdown, > + .shutdown = qcom_geni_serial_shutdown_dma, > .type = qcom_geni_serial_get_type, > .set_mctrl = qcom_geni_serial_set_mctrl, > .get_mctrl = qcom_geni_serial_get_mctrl, >