From patchwork Sat Jan 28 18:40:47 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Torokhov X-Patchwork-Id: 9543427 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 3CB5560415 for ; Sat, 28 Jan 2017 18:41:11 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2C9F727D76 for ; Sat, 28 Jan 2017 18:41:11 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2001C27F0B; Sat, 28 Jan 2017 18:41:11 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.3 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 646BA27D76 for ; Sat, 28 Jan 2017 18:41:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751978AbdA1SlI (ORCPT ); Sat, 28 Jan 2017 13:41:08 -0500 Received: from mail-pf0-f193.google.com ([209.85.192.193]:35303 "EHLO mail-pf0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751992AbdA1SlE (ORCPT ); Sat, 28 Jan 2017 13:41:04 -0500 Received: by mail-pf0-f193.google.com with SMTP id f144so20648062pfa.2; Sat, 28 Jan 2017 10:40:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :user-agent; bh=ymE8lKOr58Tn3S/sxkwKlH9sGFHSOpZXMIbOn/wrwSs=; b=r1BqtKkYk/RgJMq9SsU3VLMEhrsW8Ja+2kNBrYREBy9NXrkPrcRd/xtlKVxemu7HNa ef18PM9NXczBs/StrE0YiktRHZ8oHboGRei8Sm7t6gTFZZ1Ey0VA+J4K5RprvfVWLIeQ WA8NJVxwXB2ohf957Del7qSU678bP2BhPK3G1vaC6sPXz1XCMQABc549S2SvN4SMuKrN /c9TrhVaUb+SFcH8u1xg+4yObu+qYqABcmf9UJ24PbhwIbvf0wolL4Oo9q2eaeFAaZ3l xWr85qLqS8a2HYeZBORMD7oiV6z70+n5FVR7Cx5zj5KBzDPL1KVkVgJyj+co8BnwGnKw Ebpw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:user-agent; bh=ymE8lKOr58Tn3S/sxkwKlH9sGFHSOpZXMIbOn/wrwSs=; b=Jp2RxRCEFjBqTNFNvjgJzZiBTfDUz8ivrFdrLQNPIr6qvK4vkTZ40QyrvQBndJEfWI 1yi3gEMzuSyo9g6DDakX4Rd9pK2AJ0oDfxYv80tKssPY+b54HJAPr+yPyeHjNChC4WKa epGfUvIoW5GNuE31Cgyi20g2FOcXm6NINqNq7+noui7YGWcBtpgu1w9KAc4ghGcc0793 Y+q1OQybLlZiOI8TZgS95Hl/gUIwm8MQ+pyN2Ajur1gesl5z8cbnPVg1bJrgJ99aKanv rDr4cjuW/dTiqlfyXrAPXWVTNY3/eKq1aQ+IbKKe6mJouSRXEjeNtz8dLjY9yAk0d3WX E1ow== X-Gm-Message-State: AIkVDXK5NBp/SsjqvJLGQtikiRk0/RCKahh5FuVJldeZKcvVinOOtYDv3e9JHOvzAZlv5A== X-Received: by 10.99.181.7 with SMTP id y7mr15486524pge.57.1485628850615; Sat, 28 Jan 2017 10:40:50 -0800 (PST) Received: from dtor-ws ([2620:0:1000:1311:90eb:8a67:9b0e:4ecf]) by smtp.gmail.com with ESMTPSA id j192sm20558147pfc.60.2017.01.28.10.40.49 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Sat, 28 Jan 2017 10:40:50 -0800 (PST) Date: Sat, 28 Jan 2017 10:40:47 -0800 From: Dmitry Torokhov To: Michael Turquette , Stephen Boyd Cc: Russell King , Viresh Kumar , Guenter Roeck , Andy Shevchenko , linux-clk@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH] clk: add more managed APIs Message-ID: <20170128184047.GA24957@dtor-ws> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) Sender: linux-clk-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-clk@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP When converting a driver to managed resources it is desirable to be able to manage all resources in the same fashion. This change allows managing clocks in the same way we manage many other resources. This adds the following managed APIs: - devm_clk_prepare()/devm_clk_unprepare(); - devm_clk_enable()/devm_clk_disable(); - devm_clk_prepare_enable()/devm_clk_disable_unprepare(). Signed-off-by: Dmitry Torokhov --- This is a resend from about 5 years ago ;) At that time I also tried uninlining clk_prepare_enable() and clk_disable_unprepare(), but got lost in the maze of COMMON_CLK/HAVE_CLK/HAVE_CLK_PREPARE. This time around I left these 2 fucntions alone (inline) and simply adding devm* equivalents. Thanks! drivers/clk/clk-devres.c | 99 +++++++++++++++++++++++++++++++--------------- include/linux/clk.h | 101 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+), 32 deletions(-) diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c index 3a218c3a06ae..ef59eb4f3bc8 100644 --- a/drivers/clk/clk-devres.c +++ b/drivers/clk/clk-devres.c @@ -9,30 +9,20 @@ #include #include -static void devm_clk_release(struct device *dev, void *res) +static int devm_clk_create_devres(struct device *dev, struct clk *clk, + void (*release)(struct device *, void *)) { - clk_put(*(struct clk **)res); -} + struct clk **ptr; -struct clk *devm_clk_get(struct device *dev, const char *id) -{ - struct clk **ptr, *clk; - - ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL); + ptr = devres_alloc(release, sizeof(*ptr), GFP_KERNEL); if (!ptr) - return ERR_PTR(-ENOMEM); + return -ENOMEM; - clk = clk_get(dev, id); - if (!IS_ERR(clk)) { - *ptr = clk; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } + *ptr = clk; + devres_add(dev, ptr); - return clk; + return 0; } -EXPORT_SYMBOL(devm_clk_get); static int devm_clk_match(struct device *dev, void *res, void *data) { @@ -44,31 +34,76 @@ static int devm_clk_match(struct device *dev, void *res, void *data) return *c == data; } -void devm_clk_put(struct device *dev, struct clk *clk) +#define DEFINE_DEVM_CLK_DESTROY_OP(destroy_op) \ +static void devm_##destroy_op##_release(struct device *dev, void *res) \ +{ \ + destroy_op(*(struct clk **)res); \ +} \ + \ +void devm_##destroy_op(struct device *dev, struct clk *clk) \ +{ \ + WARN_ON(devres_release(dev, devm_##destroy_op##_release, \ + devm_clk_match, clk)); \ +} \ +EXPORT_SYMBOL(devm_##destroy_op) + +#define DEFINE_DEVM_CLK_OP(create_op, destroy_op) \ +DEFINE_DEVM_CLK_DESTROY_OP(destroy_op); \ +int devm_##create_op(struct device *dev, struct clk *clk) \ +{ \ + int error; \ + \ + error = create_op(clk); \ + if (error) \ + return error; \ + \ + error = devm_clk_create_devres(dev, clk, \ + devm_##destroy_op##_release); \ + if (error) { \ + destroy_op(clk); \ + return error; \ + } \ + \ + return 0; \ +} \ +EXPORT_SYMBOL(devm_##create_op) + +DEFINE_DEVM_CLK_DESTROY_OP(clk_put); +DEFINE_DEVM_CLK_OP(clk_prepare, clk_unprepare); +DEFINE_DEVM_CLK_OP(clk_enable, clk_disable); +DEFINE_DEVM_CLK_OP(clk_prepare_enable, clk_disable_unprepare); + +struct clk *devm_clk_get(struct device *dev, const char *id) { - int ret; + struct clk *clk; + int error; - ret = devres_release(dev, devm_clk_release, devm_clk_match, clk); + clk = clk_get(dev, id); + if (!IS_ERR(clk)) { + error = devm_clk_create_devres(dev, clk, devm_clk_put_release); + if (error) { + clk_put(clk); + return ERR_PTR(error); + } + } - WARN_ON(ret); + return clk; } -EXPORT_SYMBOL(devm_clk_put); +EXPORT_SYMBOL(devm_clk_get); struct clk *devm_get_clk_from_child(struct device *dev, struct device_node *np, const char *con_id) { - struct clk **ptr, *clk; - - ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct clk *clk; + int error; clk = of_clk_get_by_name(np, con_id); if (!IS_ERR(clk)) { - *ptr = clk; - devres_add(dev, ptr); - } else { - devres_free(ptr); + error = devm_clk_create_devres(dev, clk, devm_clk_put_release); + if (error) { + clk_put(clk); + return ERR_PTR(error); + } } return clk; diff --git a/include/linux/clk.h b/include/linux/clk.h index e9d36b3e49de..03c21c5fee4c 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -267,6 +267,29 @@ struct clk *devm_get_clk_from_child(struct device *dev, struct device_node *np, const char *con_id); /** + * devm_clk_prepare - prepare clock source as a managed resource + * @dev: device owning the resource + * @clk: clock source + * + * This prepares the clock source for use. + * + * Must not be called from within atomic context. + */ +int devm_clk_prepare(struct device *dev, struct clk *clk); + +/** + * devm_clk_unprepare - undo preparation of a managed clock source + * @dev: device used to prepare the clock + * @clk: clock source + * + * This undoes preparation of a clock, previously prepared with a call + * to devm_clk_pepare(). + * + * Must not be called from within atomic context. + */ +void devm_clk_unprepare(struct device *dev, struct clk *clk); + +/** * clk_enable - inform the system when the clock source should be running. * @clk: clock source * @@ -279,6 +302,19 @@ struct clk *devm_get_clk_from_child(struct device *dev, int clk_enable(struct clk *clk); /** + * devm_clk_enable - enable clock source as a managed resource + * @dev: device owning the resource + * @clk: clock source + * + * If the clock can not be enabled/disabled, this should return success. + * + * May be not called from atomic contexts. + * + * Returns success (0) or negative errno. + */ +int devm_clk_enable(struct device *dev, struct clk *clk); + +/** * clk_disable - inform the system when the clock source is no longer required. * @clk: clock source * @@ -295,6 +331,40 @@ int clk_enable(struct clk *clk); void clk_disable(struct clk *clk); /** + * devm_clk_disable - disable managed clock source resource + * @dev: device owning the clock source + * @clk: clock source + * + * Inform the system that a clock source is no longer required by + * a driver and may be shut down. + * + * Must not be called from atomic contexts. + */ +void devm_clk_disable(struct device *dev, struct clk *clk); + +/** + * devm_clk_prepare_enable - prepare and enable a managed clock source + * @dev: device owning the clock source + * @clk: clock source + * + * This prepares the clock source for use and enables it. + * + * Must not be called from within atomic context. + */ +int devm_clk_prepare_enable(struct device *dev, struct clk *clk); + +/** + * devm_clk_disable_unprepare - disable and undo preparation of a managed clock source + * @dev: device used to prepare and enable the clock + * @clk: clock source + * + * This disables and undoes a previously prepared clock. + * + * Must not be called from within atomic context. + */ +void devm_clk_disable_unprepare(struct device *dev, struct clk *clk); + +/** * clk_get_rate - obtain the current clock rate (in Hz) for a clock source. * This is only valid once the clock source has been enabled. * @clk: clock source @@ -460,6 +530,17 @@ static inline void clk_put(struct clk *clk) {} static inline void devm_clk_put(struct device *dev, struct clk *clk) {} +static inline int devm_clk_prepare(struct device *dev, struct clk *clk) +{ + might_sleep(); + return 0; +} + +static inline void devm_clk_unprepare(struct device *dev, struct clk *clk) +{ + might_sleep(); +} + static inline int clk_enable(struct clk *clk) { return 0; @@ -467,6 +548,26 @@ static inline int clk_enable(struct clk *clk) static inline void clk_disable(struct clk *clk) {} +static inline int devm_clk_enable(struct device *dev, struct clk *clk) +{ + might_sleep(); + return 0; +} + +static inline void devm_clk_disable(struct device *dev, struct clk *clk) {} + +static inline int devm_clk_prepare_enable(struct device *dev, struct clk *clk) +{ + might_sleep(); + return 0; +} + +static inline void devm_clk_disable_unprepare(struct device *dev, + struct clk *clk) +{ + might_sleep(); +} + static inline unsigned long clk_get_rate(struct clk *clk) { return 0;