From patchwork Sun Nov 17 23:06:14 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gerhard Sittig X-Patchwork-Id: 3195111 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id A4CD29F3A0 for ; Sun, 17 Nov 2013 23:45:34 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 9583D206B0 for ; Sun, 17 Nov 2013 23:45:33 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 5AC1C206AF for ; Sun, 17 Nov 2013 23:45:32 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1ViBUh-00012x-BF; Sun, 17 Nov 2013 23:11:08 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1ViBTz-0007fw-5b; Sun, 17 Nov 2013 23:10:23 +0000 Received: from mail-out.m-online.net ([212.18.0.9]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1ViBSv-0007YF-RF for linux-arm-kernel@lists.infradead.org; Sun, 17 Nov 2013 23:09:20 +0000 Received: from frontend1.mail.m-online.net (unknown [192.168.8.180]) by mail-out.m-online.net (Postfix) with ESMTP id 3dN89k5F4Bz4KK6s; Mon, 18 Nov 2013 00:08:42 +0100 (CET) Received: from localhost (dynscan1.mnet-online.de [192.168.6.68]) by mail.m-online.net (Postfix) with ESMTP id 3dN89k4yjPzbbfs; Mon, 18 Nov 2013 00:08:42 +0100 (CET) X-Virus-Scanned: amavisd-new at mnet-online.de Received: from mail.mnet-online.de ([192.168.8.180]) by localhost (dynscan1.mail.m-online.net [192.168.6.68]) (amavisd-new, port 10024) with ESMTP id t-7ubOSGB7Qy; Mon, 18 Nov 2013 00:08:41 +0100 (CET) X-Auth-Info: XzbcEuCH9a69o2yuBwpQlZbH+t4WK1k12UWmJ5nnEVg= Received: from localhost (host-82-135-33-74.customer.m-online.net [82.135.33.74]) by mail.mnet-online.de (Postfix) with ESMTPA; Mon, 18 Nov 2013 00:08:41 +0100 (CET) From: Gerhard Sittig To: linuxppc-dev@lists.ozlabs.org, linux-arm-kernel@lists.infradead.org, Anatolij Gustschin , Mike Turquette Subject: [PATCH v5 14/17] net: can: mscan: adjust to common clock support for mpc512x Date: Mon, 18 Nov 2013 00:06:14 +0100 Message-Id: <1384729577-7336-15-git-send-email-gsi@denx.de> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1384729577-7336-1-git-send-email-gsi@denx.de> References: <1384729577-7336-1-git-send-email-gsi@denx.de> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20131117_180918_301545_3A09B531 X-CRM114-Status: GOOD ( 24.26 ) X-Spam-Score: -1.9 (-) Cc: Detlev Zundel , Gerhard Sittig , linux-can@vger.kernel.org, Marc Kleine-Budde , Scott Wood , Wolfgang Grandegger X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.7 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, 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 X-Virus-Scanned: ClamAV using ClamSMTP implement a .get_clock() callback for the MPC512x platform which uses the common clock infrastructure (eliminating direct access to the clock control registers from within the CAN network driver), and provide the corresponding .put_clock() callback to release resources after use acquire both the clock items for register access ("ipg") as well as for wire communication ("can") keep the previous implementation of MPC512x support in place during migration, this results in a readable diff of the change this change is neutral to the MPC5200 platform Cc: Wolfgang Grandegger Cc: Marc Kleine-Budde Cc: linux-can@vger.kernel.org Signed-off-by: Gerhard Sittig --- drivers/net/can/mscan/mpc5xxx_can.c | 179 +++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c index e59b3a392af6..f48f1297ff30 100644 --- a/drivers/net/can/mscan/mpc5xxx_can.c +++ b/drivers/net/can/mscan/mpc5xxx_can.c @@ -109,6 +109,177 @@ static u32 mpc52xx_can_get_clock(struct platform_device *ofdev, #endif /* CONFIG_PPC_MPC52xx */ #ifdef CONFIG_PPC_MPC512x + +#if IS_ENABLED(CONFIG_COMMON_CLK) + +static u32 mpc512x_can_get_clock(struct platform_device *ofdev, + const char *clock_source, int *mscan_clksrc) +{ + struct device_node *np; + u32 clockdiv; + enum { + CLK_FROM_AUTO, + CLK_FROM_IPS, + CLK_FROM_SYS, + CLK_FROM_REF, + } clk_from; + struct clk *clk_in, *clk_can; + unsigned long freq_calc; + struct mscan_priv *priv; + struct clk *clk_ipg; + + /* the caller passed in the clock source spec that was read from + * the device tree, get the optional clock divider as well + */ + np = ofdev->dev.of_node; + clockdiv = 1; + of_property_read_u32(np, "fsl,mscan-clock-divider", &clockdiv); + dev_dbg(&ofdev->dev, "device tree specs: clk src[%s] div[%d]\n", + clock_source ? clock_source : "", clockdiv); + + /* when clock-source is 'ip', the CANCTL1[CLKSRC] bit needs to + * get set, and the 'ips' clock is the input to the MSCAN + * component + * + * for clock-source values of 'ref' or 'sys' the CANCTL1[CLKSRC] + * bit needs to get cleared, an optional clock-divider may have + * been specified (the default value is 1), the appropriate + * MSCAN related MCLK is the input to the MSCAN component + * + * in the absence of a clock-source spec, first an optimal clock + * gets determined based on the 'sys' clock, if that fails the + * 'ref' clock is used + */ + clk_from = CLK_FROM_AUTO; + if (clock_source) { + /* interpret the device tree's spec for the clock source */ + if (!strcmp(clock_source, "ip")) + clk_from = CLK_FROM_IPS; + else if (!strcmp(clock_source, "sys")) + clk_from = CLK_FROM_SYS; + else if (!strcmp(clock_source, "ref")) + clk_from = CLK_FROM_REF; + else + goto err_invalid; + dev_dbg(&ofdev->dev, "got a clk source spec[%d]\n", clk_from); + } + if (clk_from == CLK_FROM_AUTO) { + /* no spec so far, try the 'sys' clock; round to the + * next MHz and see if we can get a multiple of 16MHz + */ + dev_dbg(&ofdev->dev, "no clk source spec, trying SYS\n"); + clk_in = devm_clk_get(&ofdev->dev, "sys"); + if (IS_ERR(clk_in)) + goto err_notavail; + freq_calc = clk_get_rate(clk_in); + freq_calc += 499999; + freq_calc /= 1000000; + freq_calc *= 1000000; + if ((freq_calc % 16000000) == 0) { + clk_from = CLK_FROM_SYS; + clockdiv = freq_calc / 16000000; + dev_dbg(&ofdev->dev, + "clk fit, sys[%lu] div[%d] freq[%lu]\n", + freq_calc, clockdiv, freq_calc / clockdiv); + } + } + if (clk_from == CLK_FROM_AUTO) { + /* no spec so far, use the 'ref' clock */ + dev_dbg(&ofdev->dev, "no clk source spec, trying REF\n"); + clk_in = devm_clk_get(&ofdev->dev, "ref"); + if (IS_ERR(clk_in)) + goto err_notavail; + clk_from = CLK_FROM_REF; + freq_calc = clk_get_rate(clk_in); + dev_dbg(&ofdev->dev, + "clk fit, ref[%lu] (no div) freq[%lu]\n", + freq_calc, freq_calc); + } + + /* select IPS or MCLK as the MSCAN input (returned to the caller), + * setup the MCLK mux source and rate if applicable, apply the + * optionally specified or derived above divider, and determine + * the actual resulting clock rate to return to the caller + */ + switch (clk_from) { + case CLK_FROM_IPS: + clk_can = devm_clk_get(&ofdev->dev, "ips"); + if (IS_ERR(clk_can)) + goto err_notavail; + priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); + priv->clk_can = clk_can; + freq_calc = clk_get_rate(clk_can); + *mscan_clksrc = MSCAN_CLKSRC_IPS; + dev_dbg(&ofdev->dev, "clk from IPS, clksrc[%d] freq[%lu]\n", + *mscan_clksrc, freq_calc); + break; + case CLK_FROM_SYS: + case CLK_FROM_REF: + clk_can = devm_clk_get(&ofdev->dev, "mclk"); + if (IS_ERR(clk_can)) + goto err_notavail; + priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); + priv->clk_can = clk_can; + if (clk_from == CLK_FROM_SYS) + clk_in = devm_clk_get(&ofdev->dev, "sys"); + if (clk_from == CLK_FROM_REF) + clk_in = devm_clk_get(&ofdev->dev, "ref"); + if (IS_ERR(clk_in)) + goto err_notavail; + clk_set_parent(clk_can, clk_in); + freq_calc = clk_get_rate(clk_in); + freq_calc /= clockdiv; + clk_set_rate(clk_can, freq_calc); + freq_calc = clk_get_rate(clk_can); + *mscan_clksrc = MSCAN_CLKSRC_BUS; + dev_dbg(&ofdev->dev, "clk from MCLK, clksrc[%d] freq[%lu]\n", + *mscan_clksrc, freq_calc); + break; + default: + goto err_invalid; + } + + /* the above clk_can item is used for the bitrate, access to + * the peripheral's register set needs the clk_ipg item + */ + clk_ipg = devm_clk_get(&ofdev->dev, "ipg"); + if (IS_ERR(clk_ipg)) + goto err_notavail_ipg; + if (clk_prepare_enable(clk_ipg)) + goto err_notavail_ipg; + priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); + priv->clk_ipg = clk_ipg; + + /* return the determined clock source rate */ + return freq_calc; + +err_invalid: + dev_err(&ofdev->dev, "invalid clock source specification\n"); + /* clock source rate could not get determined */ + return 0; + +err_notavail: + dev_err(&ofdev->dev, "cannot acquire or setup bitrate clock source\n"); + /* clock source rate could not get determined */ + return 0; + +err_notavail_ipg: + dev_err(&ofdev->dev, "cannot acquire or setup register clock\n"); + /* clock source rate could not get determined */ + return 0; +} + +static void mpc512x_can_put_clock(struct platform_device *ofdev) +{ + struct mscan_priv *priv; + + priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); + if (priv->clk_ipg) + clk_disable_unprepare(priv->clk_ipg); +} + +#else /* COMMON_CLK */ + struct mpc512x_clockctl { u32 spmr; /* System PLL Mode Reg */ u32 sccr[2]; /* System Clk Ctrl Reg 1 & 2 */ @@ -239,12 +410,18 @@ exit_put: of_node_put(np_clock); return freq; } + +#define mpc512x_can_put_clock NULL + +#endif /* COMMON_CLK */ + #else /* !CONFIG_PPC_MPC512x */ static u32 mpc512x_can_get_clock(struct platform_device *ofdev, const char *clock_name, int *mscan_clksrc) { return 0; } +#define mpc512x_can_put_clock NULL #endif /* CONFIG_PPC_MPC512x */ static const struct of_device_id mpc5xxx_can_table[]; @@ -386,11 +563,13 @@ static int mpc5xxx_can_resume(struct platform_device *ofdev) static const struct mpc5xxx_can_data mpc5200_can_data = { .type = MSCAN_TYPE_MPC5200, .get_clock = mpc52xx_can_get_clock, + /* .put_clock not applicable */ }; static const struct mpc5xxx_can_data mpc5121_can_data = { .type = MSCAN_TYPE_MPC5121, .get_clock = mpc512x_can_get_clock, + .put_clock = mpc512x_can_put_clock, }; static const struct of_device_id mpc5xxx_can_table[] = {