From patchwork Fri Dec 13 08:21:38 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Sangorrin X-Patchwork-Id: 3339011 Return-Path: X-Original-To: patchwork-ltsi-dev@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 09BE4C0D4A for ; Fri, 13 Dec 2013 08:40:55 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 81BD7205B4 for ; Fri, 13 Dec 2013 08:40:53 +0000 (UTC) Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) by mail.kernel.org (Postfix) with ESMTP id 11F83205C1 for ; Fri, 13 Dec 2013 08:40:52 +0000 (UTC) Received: from mail.linux-foundation.org (localhost [IPv6:::1]) by mail.linuxfoundation.org (Postfix) with ESMTP id C14759A0; Fri, 13 Dec 2013 08:40:29 +0000 (UTC) X-Original-To: ltsi-dev@lists.linuxfoundation.org Delivered-To: ltsi-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTP id E890F9B1 for ; Fri, 13 Dec 2013 08:40:28 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from imx9.toshiba.co.jp (imx9.toshiba.co.jp [202.33.96.51]) by smtp1.linuxfoundation.org (Postfix) with ESMTP id 2ABFF1F88E for ; Fri, 13 Dec 2013 08:40:28 +0000 (UTC) Received: from imx2.toshiba.co.jp (imx2 [202.33.96.24]) by imx9.toshiba.co.jp with ESMTP id rBD8NNsh024994 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Fri, 13 Dec 2013 17:23:23 +0900 (JST) Received: from arc1.toshiba.co.jp ([133.199.194.235]) by imx2.toshiba.co.jp with ESMTP id rBD8NKD1015687; Fri, 13 Dec 2013 17:23:20 +0900 (JST) Received: (from root@localhost) by arc1.toshiba.co.jp id rBD8NKPs000297; Fri, 13 Dec 2013 17:23:20 +0900 (JST) Received: from unknown [133.199.192.144] by arc1.toshiba.co.jp with ESMTP id TAA00296; Fri, 13 Dec 2013 17:23:20 +0900 Received: from mx2.toshiba.co.jp (localhost [127.0.0.1]) by ovp2.toshiba.co.jp with ESMTP id rBD8NJxH015992; Fri, 13 Dec 2013 17:23:19 +0900 (JST) Received: from BK2211.rdc.toshiba.co.jp by toshiba.co.jp id rBD8NIxR022478; Fri, 13 Dec 2013 17:23:18 +0900 (JST) Received: from island.swc.toshiba.co.jp (localhost [127.0.0.1]) by BK2211.rdc.toshiba.co.jp (8.13.8+Sun/8.13.8) with ESMTP id rBD8NIrn027198; Fri, 13 Dec 2013 17:23:18 +0900 (JST) Received: from ubuntu.swc.toshiba.co.jp (unknown [133.196.174.184]) by island.swc.toshiba.co.jp (Postfix) with ESMTP id BE99A40011; Fri, 13 Dec 2013 17:22:31 +0900 (JST) From: Daniel Sangorrin To: ltsi-dev@lists.linuxfoundation.org Date: Fri, 13 Dec 2013 17:21:38 +0900 Message-Id: <1386922983-22135-24-git-send-email-daniel.sangorrin@toshiba.co.jp> X-Mailer: git-send-email 1.8.5 In-Reply-To: <1386922983-22135-1-git-send-email-daniel.sangorrin@toshiba.co.jp> References: <1386922983-22135-1-git-send-email-daniel.sangorrin@toshiba.co.jp> X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org Cc: michal.simek@xilinx.com Subject: [LTSI-dev] [PATCH 023/108] tty: xuartps: Dynamically adjust to input frequency changes X-BeenThere: ltsi-dev@lists.linuxfoundation.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: "A list to discuss patches, development, and other things related to the LTSI project" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ltsi-dev-bounces@lists.linuxfoundation.org Errors-To: ltsi-dev-bounces@lists.linuxfoundation.org X-Virus-Scanned: ClamAV using ClamSMTP From: Soren Brinkmann Add a clock notifier to dynamically handle frequency changes of the input clock by reprogramming the UART in order to keep the baud rate constant. Signed-off-by: Soren Brinkmann Signed-off-by: Greg Kroah-Hartman (cherry picked from commit c4b0510cc1571ff44e1d6024d92683d49a8bcfde) Signed-off-by: Daniel Sangorrin Signed-off-by: Yoshitake Kobayashi --- drivers/tty/serial/xilinx_uartps.c | 125 +++++++++++++++++++++++++++++++++++-- 1 file changed, 120 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 3f15e80..8219504 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -163,13 +163,20 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); /** * struct xuartps - device data - * @refclk Reference clock - * @aperclk APB clock + * @port Pointer to the UART port + * @refclk Reference clock + * @aperclk APB clock + * @baud Current baud rate + * @clk_rate_change_nb Notifier block for clock changes */ struct xuartps { + struct uart_port *port; struct clk *refclk; struct clk *aperclk; + unsigned int baud; + struct notifier_block clk_rate_change_nb; }; +#define to_xuartps(_nb) container_of(_nb, struct xuartps, clk_rate_change_nb); /** * xuartps_isr - Interrupt handler @@ -385,6 +392,7 @@ static unsigned int xuartps_set_baud_rate(struct uart_port *port, u32 cd, bdiv; u32 mreg; int div8; + struct xuartps *xuartps = port->private_data; calc_baud = xuartps_calc_baud_divs(port->uartclk, baud, &bdiv, &cd, &div8); @@ -398,10 +406,105 @@ static unsigned int xuartps_set_baud_rate(struct uart_port *port, xuartps_writel(mreg, XUARTPS_MR_OFFSET); xuartps_writel(cd, XUARTPS_BAUDGEN_OFFSET); xuartps_writel(bdiv, XUARTPS_BAUDDIV_OFFSET); + xuartps->baud = baud; return calc_baud; } +/** + * xuartps_clk_notitifer_cb - Clock notifier callback + * @nb: Notifier block + * @event: Notify event + * @data: Notifier data + * Returns NOTIFY_OK on success, NOTIFY_BAD on error. + */ +static int xuartps_clk_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + u32 ctrl_reg; + struct uart_port *port; + int locked = 0; + struct clk_notifier_data *ndata = data; + unsigned long flags = 0; + struct xuartps *xuartps = to_xuartps(nb); + + port = xuartps->port; + if (port->suspended) + return NOTIFY_OK; + + switch (event) { + case PRE_RATE_CHANGE: + { + u32 bdiv; + u32 cd; + int div8; + + /* + * Find out if current baud-rate can be achieved with new clock + * frequency. + */ + if (!xuartps_calc_baud_divs(ndata->new_rate, xuartps->baud, + &bdiv, &cd, &div8)) + return NOTIFY_BAD; + + spin_lock_irqsave(&xuartps->port->lock, flags); + + /* Disable the TX and RX to set baud rate */ + xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | + (XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS), + XUARTPS_CR_OFFSET); + + spin_unlock_irqrestore(&xuartps->port->lock, flags); + + return NOTIFY_OK; + } + case POST_RATE_CHANGE: + /* + * Set clk dividers to generate correct baud with new clock + * frequency. + */ + + spin_lock_irqsave(&xuartps->port->lock, flags); + + locked = 1; + port->uartclk = ndata->new_rate; + + xuartps->baud = xuartps_set_baud_rate(xuartps->port, + xuartps->baud); + /* fall through */ + case ABORT_RATE_CHANGE: + if (!locked) + spin_lock_irqsave(&xuartps->port->lock, flags); + + /* Set TX/RX Reset */ + xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | + (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST), + XUARTPS_CR_OFFSET); + + while (xuartps_readl(XUARTPS_CR_OFFSET) & + (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST)) + cpu_relax(); + + /* + * Clear the RX disable and TX disable bits and then set the TX + * enable bit and RX enable bit to enable the transmitter and + * receiver. + */ + xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET); + ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); + xuartps_writel( + (ctrl_reg & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) | + (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN), + XUARTPS_CR_OFFSET); + + spin_unlock_irqrestore(&xuartps->port->lock, flags); + + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} + /*----------------------Uart Operations---------------------------*/ /** @@ -1164,13 +1267,19 @@ static int xuartps_probe(struct platform_device *pdev) goto err_out_clk_disable; } + xuartps_data->clk_rate_change_nb.notifier_call = + xuartps_clk_notifier_cb; + if (clk_notifier_register(xuartps_data->refclk, + &xuartps_data->clk_rate_change_nb)) + dev_warn(&pdev->dev, "Unable to register clock notifier.\n"); + /* Initialize the port structure */ port = xuartps_get_port(); if (!port) { dev_err(&pdev->dev, "Cannot get uart_port structure\n"); rc = -ENODEV; - goto err_out_clk_disable; + goto err_out_notif_unreg; } else { /* Register the port. * This function also registers this device with the tty layer @@ -1181,16 +1290,20 @@ static int xuartps_probe(struct platform_device *pdev) port->dev = &pdev->dev; port->uartclk = clk_get_rate(xuartps_data->refclk); port->private_data = xuartps_data; - platform_set_drvdata(pdev, port); + xuartps_data->port = port; + platform_set_drvdata(pdev, port); rc = uart_add_one_port(&xuartps_uart_driver, port); if (rc) { dev_err(&pdev->dev, "uart_add_one_port() failed; err=%i\n", rc); - goto err_out_clk_disable; + goto err_out_notif_unreg; } return 0; } +err_out_notif_unreg: + clk_notifier_unregister(xuartps_data->refclk, + &xuartps_data->clk_rate_change_nb); err_out_clk_disable: clk_disable_unprepare(xuartps_data->refclk); err_out_clk_dis_aper: @@ -1212,6 +1325,8 @@ static int xuartps_remove(struct platform_device *pdev) int rc; /* Remove the xuartps port from the serial core */ + clk_notifier_unregister(xuartps_data->refclk, + &xuartps_data->clk_rate_change_nb); rc = uart_remove_one_port(&xuartps_uart_driver, port); port->mapbase = 0; clk_disable_unprepare(xuartps_data->refclk);