From mboxrd@z Thu Jan 1 00:00:00 1970 From: Annaliese McDermond Subject: [PATCH v3] i2c: bcm2835: Model Divider in CCF Date: Tue, 28 May 2019 21:29:12 -0700 Message-ID: <20190529042912.12956-1-nh6z@nh6z.net> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=m.gmane.org@lists.infradead.org To: eric@anholt.net, stefan.wahren@i2se.com, f.fainelli@gmail.com, wsa@the-dreams.de, swarren@wwwdotorg.org, linux-i2c@vger.kernel.org, linux-rpi-kernel@lists.infradead.org, linux-arm-kernel@lists.infradead.org Cc: team@nwdigitalradio.com, Annaliese McDermond List-Id: linux-i2c@vger.kernel.org Model the I2C bus clock divider as a part of the Core Clock Framework. Primarily this removes the clk_get_rate() call from each transfer. This call causes problems for slave drivers that themselves have internal clock components that are controlled by an I2C interface. When the slave's internal clock component is prepared, the prepare lock is obtained, and it makes calls to the I2C subsystem to command the hardware to activate the clock. In order to perform the I2C transfer, this driver sets the divider, which requires it to get the parent clock rate, which it does with clk_get_rate(). Unfortunately, this function will try to take the clock prepare lock, which is already held by the slave's internal clock calls creating a deadlock. Modeling the divider in the CCF natively removes this dependency and the divider value is only set upon changing the bus clock frequency or changes in the parent clock that cascade down to this divisor. This obviates the need to set the divider with every transfer and avoids the deadlock described above. It also should provide better clock debugging and save a few cycles on each transfer due to not having to recalcuate the divider value. Signed-off-by: Annaliese McDermond --- Changes in v2: - Remove empty whitespace between declarations - Preserve comment regarding register rounding - Consolodate declarations and initial assignments - Return proper error pointer from bcm2835_i2c_register_div() - Properly handle return value from bcm2835_i2c_register_div() in bcm2835_i2c_probe() - Refactor divider calulation code into separate function - Use clk_set_rate_exclusive() to ensure the clock is not modified by other consumers - Deregister clock in bcm2835_i2c_remove() to properly clean up after ourselves Changes in v3: - Changed name of clock to include the i2c device name to make for easier debugging when using /sys/kernel/debug/clk/clk_summary drivers/i2c/busses/i2c-bcm2835.c | 146 ++++++++++++++++++++++++------- 1 file changed, 115 insertions(+), 31 deletions(-) diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c index d2fbb4bb4a43..fc6ae024d0b3 100644 --- a/drivers/i2c/busses/i2c-bcm2835.c +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -4,6 +4,8 @@ */ #include +#include +#include #include #include #include @@ -51,9 +53,7 @@ struct bcm2835_i2c_dev { struct device *dev; void __iomem *regs; - struct clk *clk; int irq; - u32 bus_clk_rate; struct i2c_adapter adapter; struct completion completion; struct i2c_msg *curr_msg; @@ -74,12 +74,17 @@ static inline u32 bcm2835_i2c_readl(struct bcm2835_i2c_dev *i2c_dev, u32 reg) return readl(i2c_dev->regs + reg); } -static int bcm2835_i2c_set_divider(struct bcm2835_i2c_dev *i2c_dev) +#define to_clk_bcm2835_i2c(_hw) container_of(_hw, struct clk_bcm2835_i2c, hw) +struct clk_bcm2835_i2c { + struct clk_hw hw; + struct bcm2835_i2c_dev *i2c_dev; +}; + +static int clk_bcm2835_i2c_calc_divider(unsigned long rate, + unsigned long parent_rate) { - u32 divider, redl, fedl; + u32 divider = DIV_ROUND_UP(parent_rate, rate); - divider = DIV_ROUND_UP(clk_get_rate(i2c_dev->clk), - i2c_dev->bus_clk_rate); /* * Per the datasheet, the register is always interpreted as an even * number, by rounding down. In other words, the LSB is ignored. So, @@ -88,12 +93,23 @@ static int bcm2835_i2c_set_divider(struct bcm2835_i2c_dev *i2c_dev) if (divider & 1) divider++; if ((divider < BCM2835_I2C_CDIV_MIN) || - (divider > BCM2835_I2C_CDIV_MAX)) { - dev_err_ratelimited(i2c_dev->dev, "Invalid clock-frequency\n"); + (divider > BCM2835_I2C_CDIV_MAX)) return -EINVAL; - } - bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider); + return divider; +} + +static int clk_bcm2835_i2c_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_bcm2835_i2c *div = to_clk_bcm2835_i2c(hw); + u32 redl, fedl; + u32 divider = clk_bcm2835_i2c_calc_divider(rate, parent_rate); + + if (divider == -EINVAL) + return -EINVAL; + + bcm2835_i2c_writel(div->i2c_dev, BCM2835_I2C_DIV, divider); /* * Number of core clocks to wait after falling edge before @@ -108,12 +124,62 @@ static int bcm2835_i2c_set_divider(struct bcm2835_i2c_dev *i2c_dev) */ redl = max(divider / 4, 1u); - bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DEL, + bcm2835_i2c_writel(div->i2c_dev, BCM2835_I2C_DEL, (fedl << BCM2835_I2C_FEDL_SHIFT) | (redl << BCM2835_I2C_REDL_SHIFT)); return 0; } +static long clk_bcm2835_i2c_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u32 divider = clk_bcm2835_i2c_calc_divider(rate, *parent_rate); + + return DIV_ROUND_UP(*parent_rate, divider); +} + +static unsigned long clk_bcm2835_i2c_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_bcm2835_i2c *div = to_clk_bcm2835_i2c(hw); + u32 divider = bcm2835_i2c_readl(div->i2c_dev, BCM2835_I2C_DIV); + + return DIV_ROUND_UP(parent_rate, divider); +} + +static const struct clk_ops clk_bcm2835_i2c_ops = { + .set_rate = clk_bcm2835_i2c_set_rate, + .round_rate = clk_bcm2835_i2c_round_rate, + .recalc_rate = clk_bcm2835_i2c_recalc_rate, +}; + +static struct clk *bcm2835_i2c_register_div(struct device *dev, + const char *mclk_name, + struct bcm2835_i2c_dev *i2c_dev) +{ + struct clk_init_data init; + struct clk_bcm2835_i2c *priv; + char name[32]; + + snprintf(name, sizeof(name), "%s_div", dev_name(dev)); + + init.ops = &clk_bcm2835_i2c_ops; + init.name = name; + init.parent_names = (const char* []) { mclk_name }; + init.num_parents = 1; + init.flags = 0; + + priv = devm_kzalloc(dev, sizeof(struct clk_bcm2835_i2c), GFP_KERNEL); + if (priv == NULL) + return ERR_PTR(-ENOMEM); + + priv->hw.init = &init; + priv->i2c_dev = i2c_dev; + + clk_hw_register_clkdev(&priv->hw, "div", dev_name(dev)); + return devm_clk_register(dev, &priv->hw); +} + static void bcm2835_fill_txfifo(struct bcm2835_i2c_dev *i2c_dev) { u32 val; @@ -271,7 +337,7 @@ static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], { struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap); unsigned long time_left; - int i, ret; + int i; for (i = 0; i < (num - 1); i++) if (msgs[i].flags & I2C_M_RD) { @@ -280,10 +346,6 @@ static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], return -EOPNOTSUPP; } - ret = bcm2835_i2c_set_divider(i2c_dev); - if (ret) - return ret; - i2c_dev->curr_msg = msgs; i2c_dev->num_msgs = num; reinit_completion(&i2c_dev->completion); @@ -338,6 +400,9 @@ static int bcm2835_i2c_probe(struct platform_device *pdev) struct resource *mem, *irq; int ret; struct i2c_adapter *adap; + const char *mclk_name; + struct clk *bus_clk; + u32 bus_clk_rate; i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); if (!i2c_dev) @@ -351,21 +416,6 @@ static int bcm2835_i2c_probe(struct platform_device *pdev) if (IS_ERR(i2c_dev->regs)) return PTR_ERR(i2c_dev->regs); - i2c_dev->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(i2c_dev->clk)) { - if (PTR_ERR(i2c_dev->clk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get clock\n"); - return PTR_ERR(i2c_dev->clk); - } - - ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", - &i2c_dev->bus_clk_rate); - if (ret < 0) { - dev_warn(&pdev->dev, - "Could not read clock-frequency property\n"); - i2c_dev->bus_clk_rate = 100000; - } - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq) { dev_err(&pdev->dev, "No IRQ resource\n"); @@ -380,6 +430,35 @@ static int bcm2835_i2c_probe(struct platform_device *pdev) return -ENODEV; } + mclk_name = of_clk_get_parent_name(pdev->dev.of_node, 0); + + bus_clk = bcm2835_i2c_register_div(&pdev->dev, mclk_name, i2c_dev); + + if (IS_ERR(bus_clk)) { + dev_err(&pdev->dev, "Could not register clock\n"); + return PTR_ERR(bus_clk); + } + + ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &bus_clk_rate); + if (ret < 0) { + dev_warn(&pdev->dev, + "Could not read clock-frequency property\n"); + bus_clk_rate = 100000; + } + + ret = clk_set_rate_exclusive(bus_clk, bus_clk_rate); + if (ret < 0) { + dev_err(&pdev->dev, "Could not set clock frequency\n"); + return ret; + } + + ret = clk_prepare_enable(bus_clk); + if (ret) { + dev_err(&pdev->dev, "Couldn't prepare clock"); + return ret; + } + adap = &i2c_dev->adapter; i2c_set_adapdata(adap, i2c_dev); adap->owner = THIS_MODULE; @@ -402,6 +481,11 @@ static int bcm2835_i2c_probe(struct platform_device *pdev) static int bcm2835_i2c_remove(struct platform_device *pdev) { struct bcm2835_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + struct clk *bus_clk = devm_clk_get(i2c_dev->dev, "div"); + + clk_rate_exclusive_put(bus_clk); + clk_disable_unprepare(bus_clk); + devm_clk_unregister(i2c_dev->dev, bus_clk); free_irq(i2c_dev->irq, i2c_dev); i2c_del_adapter(&i2c_dev->adapter); -- 2.19.1 From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.0 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,T_DKIMWL_WL_HIGH,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3FA4DC28CC0 for ; Wed, 29 May 2019 04:29:29 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 0DBBF21721 for ; Wed, 29 May 2019 04:29:28 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="Ddl+BfvF"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=g001.emailsrvr.com header.i=@g001.emailsrvr.com header.b="JK/iv6oV" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 0DBBF21721 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=nh6z.net Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+infradead-linux-arm-kernel=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:Message-Id:Date:Subject:To :From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References: List-Owner; bh=LfiilXnDiLqIqURccuEFQANCPzj5uZ6sgkudBE+59tQ=; b=Ddl+BfvFQDKkqB FdaxL04XPc2zmyFONOiz0p98KAu+PlzKSP9xKf1BJKI5AOmfqEyny29FKoiIQphFqRaQUuN4Sl8Dr W/V545LhDLdL7OyuV3ilX2cXrBNBM6sU6D7EVh1yhfh9HskGo2smvlVCsXSgGcjIyN+qaHHh1C0K+ rLzbKgmdUmRmkiUFLyO29Qqt1RBTTpjuVDa0ldRPA4R1R76KC7j2OhmuJKbm8AJoefEyjYDP2hVr2 GN49tMC3nXZVvswZkIGG2AzdxJADNAa7boG4PXyToIHRDDduAYH5GpuTYLbi0FWTISDblvywy2cOJ LXSqO8tCmvwLlA8sT4Tg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1hVqDN-0005mW-1q; Wed, 29 May 2019 04:29:25 +0000 Received: from smtp73.ord1d.emailsrvr.com ([184.106.54.73]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1hVqDJ-0005m4-Hi; Wed, 29 May 2019 04:29:23 +0000 Received: from smtp10.relay.ord1d.emailsrvr.com (localhost [127.0.0.1]) by smtp10.relay.ord1d.emailsrvr.com (SMTP Server) with ESMTP id 45C81A0116; Wed, 29 May 2019 00:29:20 -0400 (EDT) X-SMTPDoctor-Processed: csmtpprox beta Received: from smtp10.relay.ord1d.emailsrvr.com (localhost [127.0.0.1]) by smtp10.relay.ord1d.emailsrvr.com (SMTP Server) with ESMTP id 3A0C6A011F; Wed, 29 May 2019 00:29:20 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=g001.emailsrvr.com; s=20190322-9u7zjiwi; t=1559104160; bh=mM7hi/iMm0gaPUAQlzy+XSLccfIJ932Jxhv7KVFLUik=; h=From:To:Subject:Date:From; b=JK/iv6oV0b3E3fym0aJkxGD32D7X860bRqPlDWAzq6E3OYiqT/BtWsqNWoykQxWfV v//MhmZX4+vOdlLY/eXskZDdn+Nkh2og89tLcIKOkVDOj8q4AMLpS3xHXVpg/N1tkX +W87ORbZc/Li1vIjqKn7FTibQegsI9jtun9MU1LM= X-Auth-ID: mcdermj@xenotropic.com Received: by smtp10.relay.ord1d.emailsrvr.com (Authenticated sender: mcdermj-AT-xenotropic.com) with ESMTPSA id 17173A0116; Wed, 29 May 2019 00:29:19 -0400 (EDT) X-Sender-Id: mcdermj@xenotropic.com Received: from commune.xenotropic.com (c-73-96-52-102.hsd1.or.comcast.net [73.96.52.102]) (using TLSv1.2 with cipher DHE-RSA-AES128-GCM-SHA256) by 0.0.0.0:25 (trex/5.7.12); Wed, 29 May 2019 00:29:20 -0400 From: Annaliese McDermond To: eric@anholt.net, stefan.wahren@i2se.com, f.fainelli@gmail.com, wsa@the-dreams.de, swarren@wwwdotorg.org, linux-i2c@vger.kernel.org, linux-rpi-kernel@lists.infradead.org, linux-arm-kernel@lists.infradead.org Subject: [PATCH v3] i2c: bcm2835: Model Divider in CCF Date: Tue, 28 May 2019 21:29:12 -0700 Message-Id: <20190529042912.12956-1-nh6z@nh6z.net> X-Mailer: git-send-email 2.19.1 MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190528_212921_693051_532B6B4C X-CRM114-Status: GOOD ( 20.19 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: team@nwdigitalradio.com, Annaliese McDermond Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+infradead-linux-arm-kernel=archiver.kernel.org@lists.infradead.org Model the I2C bus clock divider as a part of the Core Clock Framework. Primarily this removes the clk_get_rate() call from each transfer. This call causes problems for slave drivers that themselves have internal clock components that are controlled by an I2C interface. When the slave's internal clock component is prepared, the prepare lock is obtained, and it makes calls to the I2C subsystem to command the hardware to activate the clock. In order to perform the I2C transfer, this driver sets the divider, which requires it to get the parent clock rate, which it does with clk_get_rate(). Unfortunately, this function will try to take the clock prepare lock, which is already held by the slave's internal clock calls creating a deadlock. Modeling the divider in the CCF natively removes this dependency and the divider value is only set upon changing the bus clock frequency or changes in the parent clock that cascade down to this divisor. This obviates the need to set the divider with every transfer and avoids the deadlock described above. It also should provide better clock debugging and save a few cycles on each transfer due to not having to recalcuate the divider value. Signed-off-by: Annaliese McDermond --- Changes in v2: - Remove empty whitespace between declarations - Preserve comment regarding register rounding - Consolodate declarations and initial assignments - Return proper error pointer from bcm2835_i2c_register_div() - Properly handle return value from bcm2835_i2c_register_div() in bcm2835_i2c_probe() - Refactor divider calulation code into separate function - Use clk_set_rate_exclusive() to ensure the clock is not modified by other consumers - Deregister clock in bcm2835_i2c_remove() to properly clean up after ourselves Changes in v3: - Changed name of clock to include the i2c device name to make for easier debugging when using /sys/kernel/debug/clk/clk_summary drivers/i2c/busses/i2c-bcm2835.c | 146 ++++++++++++++++++++++++------- 1 file changed, 115 insertions(+), 31 deletions(-) diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c index d2fbb4bb4a43..fc6ae024d0b3 100644 --- a/drivers/i2c/busses/i2c-bcm2835.c +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -4,6 +4,8 @@ */ #include +#include +#include #include #include #include @@ -51,9 +53,7 @@ struct bcm2835_i2c_dev { struct device *dev; void __iomem *regs; - struct clk *clk; int irq; - u32 bus_clk_rate; struct i2c_adapter adapter; struct completion completion; struct i2c_msg *curr_msg; @@ -74,12 +74,17 @@ static inline u32 bcm2835_i2c_readl(struct bcm2835_i2c_dev *i2c_dev, u32 reg) return readl(i2c_dev->regs + reg); } -static int bcm2835_i2c_set_divider(struct bcm2835_i2c_dev *i2c_dev) +#define to_clk_bcm2835_i2c(_hw) container_of(_hw, struct clk_bcm2835_i2c, hw) +struct clk_bcm2835_i2c { + struct clk_hw hw; + struct bcm2835_i2c_dev *i2c_dev; +}; + +static int clk_bcm2835_i2c_calc_divider(unsigned long rate, + unsigned long parent_rate) { - u32 divider, redl, fedl; + u32 divider = DIV_ROUND_UP(parent_rate, rate); - divider = DIV_ROUND_UP(clk_get_rate(i2c_dev->clk), - i2c_dev->bus_clk_rate); /* * Per the datasheet, the register is always interpreted as an even * number, by rounding down. In other words, the LSB is ignored. So, @@ -88,12 +93,23 @@ static int bcm2835_i2c_set_divider(struct bcm2835_i2c_dev *i2c_dev) if (divider & 1) divider++; if ((divider < BCM2835_I2C_CDIV_MIN) || - (divider > BCM2835_I2C_CDIV_MAX)) { - dev_err_ratelimited(i2c_dev->dev, "Invalid clock-frequency\n"); + (divider > BCM2835_I2C_CDIV_MAX)) return -EINVAL; - } - bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider); + return divider; +} + +static int clk_bcm2835_i2c_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_bcm2835_i2c *div = to_clk_bcm2835_i2c(hw); + u32 redl, fedl; + u32 divider = clk_bcm2835_i2c_calc_divider(rate, parent_rate); + + if (divider == -EINVAL) + return -EINVAL; + + bcm2835_i2c_writel(div->i2c_dev, BCM2835_I2C_DIV, divider); /* * Number of core clocks to wait after falling edge before @@ -108,12 +124,62 @@ static int bcm2835_i2c_set_divider(struct bcm2835_i2c_dev *i2c_dev) */ redl = max(divider / 4, 1u); - bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DEL, + bcm2835_i2c_writel(div->i2c_dev, BCM2835_I2C_DEL, (fedl << BCM2835_I2C_FEDL_SHIFT) | (redl << BCM2835_I2C_REDL_SHIFT)); return 0; } +static long clk_bcm2835_i2c_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u32 divider = clk_bcm2835_i2c_calc_divider(rate, *parent_rate); + + return DIV_ROUND_UP(*parent_rate, divider); +} + +static unsigned long clk_bcm2835_i2c_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_bcm2835_i2c *div = to_clk_bcm2835_i2c(hw); + u32 divider = bcm2835_i2c_readl(div->i2c_dev, BCM2835_I2C_DIV); + + return DIV_ROUND_UP(parent_rate, divider); +} + +static const struct clk_ops clk_bcm2835_i2c_ops = { + .set_rate = clk_bcm2835_i2c_set_rate, + .round_rate = clk_bcm2835_i2c_round_rate, + .recalc_rate = clk_bcm2835_i2c_recalc_rate, +}; + +static struct clk *bcm2835_i2c_register_div(struct device *dev, + const char *mclk_name, + struct bcm2835_i2c_dev *i2c_dev) +{ + struct clk_init_data init; + struct clk_bcm2835_i2c *priv; + char name[32]; + + snprintf(name, sizeof(name), "%s_div", dev_name(dev)); + + init.ops = &clk_bcm2835_i2c_ops; + init.name = name; + init.parent_names = (const char* []) { mclk_name }; + init.num_parents = 1; + init.flags = 0; + + priv = devm_kzalloc(dev, sizeof(struct clk_bcm2835_i2c), GFP_KERNEL); + if (priv == NULL) + return ERR_PTR(-ENOMEM); + + priv->hw.init = &init; + priv->i2c_dev = i2c_dev; + + clk_hw_register_clkdev(&priv->hw, "div", dev_name(dev)); + return devm_clk_register(dev, &priv->hw); +} + static void bcm2835_fill_txfifo(struct bcm2835_i2c_dev *i2c_dev) { u32 val; @@ -271,7 +337,7 @@ static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], { struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap); unsigned long time_left; - int i, ret; + int i; for (i = 0; i < (num - 1); i++) if (msgs[i].flags & I2C_M_RD) { @@ -280,10 +346,6 @@ static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], return -EOPNOTSUPP; } - ret = bcm2835_i2c_set_divider(i2c_dev); - if (ret) - return ret; - i2c_dev->curr_msg = msgs; i2c_dev->num_msgs = num; reinit_completion(&i2c_dev->completion); @@ -338,6 +400,9 @@ static int bcm2835_i2c_probe(struct platform_device *pdev) struct resource *mem, *irq; int ret; struct i2c_adapter *adap; + const char *mclk_name; + struct clk *bus_clk; + u32 bus_clk_rate; i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); if (!i2c_dev) @@ -351,21 +416,6 @@ static int bcm2835_i2c_probe(struct platform_device *pdev) if (IS_ERR(i2c_dev->regs)) return PTR_ERR(i2c_dev->regs); - i2c_dev->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(i2c_dev->clk)) { - if (PTR_ERR(i2c_dev->clk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get clock\n"); - return PTR_ERR(i2c_dev->clk); - } - - ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", - &i2c_dev->bus_clk_rate); - if (ret < 0) { - dev_warn(&pdev->dev, - "Could not read clock-frequency property\n"); - i2c_dev->bus_clk_rate = 100000; - } - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq) { dev_err(&pdev->dev, "No IRQ resource\n"); @@ -380,6 +430,35 @@ static int bcm2835_i2c_probe(struct platform_device *pdev) return -ENODEV; } + mclk_name = of_clk_get_parent_name(pdev->dev.of_node, 0); + + bus_clk = bcm2835_i2c_register_div(&pdev->dev, mclk_name, i2c_dev); + + if (IS_ERR(bus_clk)) { + dev_err(&pdev->dev, "Could not register clock\n"); + return PTR_ERR(bus_clk); + } + + ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &bus_clk_rate); + if (ret < 0) { + dev_warn(&pdev->dev, + "Could not read clock-frequency property\n"); + bus_clk_rate = 100000; + } + + ret = clk_set_rate_exclusive(bus_clk, bus_clk_rate); + if (ret < 0) { + dev_err(&pdev->dev, "Could not set clock frequency\n"); + return ret; + } + + ret = clk_prepare_enable(bus_clk); + if (ret) { + dev_err(&pdev->dev, "Couldn't prepare clock"); + return ret; + } + adap = &i2c_dev->adapter; i2c_set_adapdata(adap, i2c_dev); adap->owner = THIS_MODULE; @@ -402,6 +481,11 @@ static int bcm2835_i2c_probe(struct platform_device *pdev) static int bcm2835_i2c_remove(struct platform_device *pdev) { struct bcm2835_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + struct clk *bus_clk = devm_clk_get(i2c_dev->dev, "div"); + + clk_rate_exclusive_put(bus_clk); + clk_disable_unprepare(bus_clk); + devm_clk_unregister(i2c_dev->dev, bus_clk); free_irq(i2c_dev->irq, i2c_dev); i2c_del_adapter(&i2c_dev->adapter); -- 2.19.1 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel