From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933284AbcFIJtk (ORCPT ); Thu, 9 Jun 2016 05:49:40 -0400 Received: from mailout1.w1.samsung.com ([210.118.77.11]:58853 "EHLO mailout1.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933538AbcFIJpD (ORCPT ); Thu, 9 Jun 2016 05:45:03 -0400 X-AuditID: cbfec7f4-f796c6d000001486-96-57593a962310 From: Krzysztof Kozlowski To: Ulf Hansson , Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , Krzysztof Kozlowski , Sebastian Reichel , Dmitry Eremin-Solenikov , David Woodhouse , Liam Girdwood , Mark Brown , Greg Kroah-Hartman , Hans de Goede , Jean-Christophe Plagniol-Villard , Tomi Valkeinen , Heiko Stuebner , linux-mmc@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org, linux-pm@vger.kernel.org, linux-usb@vger.kernel.org, linux-fbdev@vger.kernel.org, hzpeterchen@gmail.com Cc: Bartlomiej Zolnierkiewicz Subject: [RFC v4 08/14] power: pwrseq: simple: Add support for regulators and generic property Date: Thu, 09 Jun 2016 11:44:25 +0200 Message-id: <1465465471-28740-9-git-send-email-k.kozlowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1465465471-28740-1-git-send-email-k.kozlowski@samsung.com> References: <1465465471-28740-1-git-send-email-k.kozlowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAAzWRXUiTYRiGe7+/zeXwY5p+TBAaSCVmzmW9SCwPAl86MShaBdGWfsyhU9mc aCcpMrDB1Jw6kdT8z7/W5kRzTUuHqwRdmsvEDHUH/sEKRQwta5PO7vu5L7gOHi4u6CSEXFVu AavJVeSIKB4x9cc9d74u5a4scaY2CVrrLSSsXfFRsNrnx2Gza5qET7tNOKzcbiFhWZuFgttu MwaPVrdIaJi3UXB6sxvArXUx3PtcjkHbmpeE7yt+kHBu5BkFXUeVAO4YXQDWz4xisLVTj8OO L58wWGXuJ+DLlXUM6p0uDpxyFEBL8yIF3f2y1GjU19QH0FyFEUOvG75xUHfXLoUGXsShTc8S gWw9Tyi05H1DobHGPg4aaH+M/KPzFKqw9wA05G3CkXthCEM7tpgbYfd4VzLZHFUhq7kglfOy rJ3VZL5HWrRh+UmUAL3EAEK4DH2RMe7oyeMcyXiWLZQB8LgCugMwjo5yznEpxZiaDQMeoCha wgx0tQepCHqWwyzMDmGBAachMzjYywnkcFrO2Fv8RCATdCwz0joWVPDpNMa3aATHuhjmw6Qp eA+hEbO6vBYUCP4xzjYTVQX4z8GJHnCK1WXkax8q1eIErUKt1eUqEzLy1DZw/L3dYdA2mTIO aC4QhfLPZd6RCUhFobZYPQ4YLi6K4O9fvisT8DMVxY9YTd4DjS6H1Y6DaC4hiuI3jvhvCWil ooDNZtl8VvN/xbghwhJgjpIaw/eydR7Vx/RlrzJSungpTWIF/qupC3XpoZLvZ+o0E0JWvp9d /ovzm9XZI2TJZx2RebC4zDmTluwoTJRbpcPXb0q/FvWq+KWJaid2Okpmi1Wa4mneq5q3hfFI MCueOHhnPyw5vCZX8cwNYULf6NqU8mTcwf3bRUkiQpulEMfhGq3iL4Wqq865AgAA Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Some devices need real hard-reset by cutting the power. During power sequence turn off and on all of provided regulators. Additionally add support for instantiating the pwrseq-simple device on a generic property 'power-sequence'. The device will attach itself to the node containing the property and parse the node's properties like reset-gpios, supplies etc. Signed-off-by: Krzysztof Kozlowski --- .../bindings/power/pwrseq/pwrseq-simple.txt | 30 +++++- drivers/power/pwrseq/pwrseq_simple.c | 113 ++++++++++++++++++++- 2 files changed, 136 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt index ce0e76749671..7e5a414a67fc 100644 --- a/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt +++ b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt @@ -1,11 +1,18 @@ -* The simple MMC power sequence provider +* The simple power sequence provider -The purpose of the simple MMC power sequence provider is to supports a set of +The purpose of the simple power sequence provider is to supports a set of common properties between various SOC designs. It thus enables us to use the same provider for several SOC designs. -Required properties: -- compatible : contains "mmc-pwrseq-simple". +The driver supports two types of bindings: +1. Property for any node + Required properties: + - power-sequence + +2. Separate node (not recommended for new users) + Required properties: + - compatible : contains "mmc-pwrseq-simple". + Optional properties: - reset-gpios : contains a list of GPIO specifiers. The reset GPIOs are asserted @@ -16,6 +23,8 @@ Optional properties: See ../clocks/clock-bindings.txt for details. - clock-names : Must include the following entry: "ext_clock" (External clock provided to the card). +- *-supply : If supplies are provided, the driver will enable and disable + them during power sequence. Example: @@ -25,3 +34,16 @@ Example: clocks = <&clk_32768_ck>; clock-names = "ext_clock"; } + + usb3503@08 { + compatible = "smsc,usb3503"; + reg = <0x08>; + + intn-gpios = <&gpx3 0 GPIO_ACTIVE_HIGH>; + connect-gpios = <&gpx3 4 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpx3 5 GPIO_ACTIVE_HIGH>; + initial-mode = <1>; + + power-sequence; + vdd-supply = <&buck8_reg>; + }; diff --git a/drivers/power/pwrseq/pwrseq_simple.c b/drivers/power/pwrseq/pwrseq_simple.c index 93807a6ef162..f70e207f994b 100644 --- a/drivers/power/pwrseq/pwrseq_simple.c +++ b/drivers/power/pwrseq/pwrseq_simple.c @@ -1,12 +1,15 @@ /* - * Copyright (C) 2014 Linaro Ltd + * Copyright (C) 2014 Linaro Ltd + * Copyright (C) 2016 Samsung Electronics * * Author: Ulf Hansson + * Krzysztof Kozlowski * * License terms: GNU General Public License (GPL) version 2 * * Simple MMC power sequence management */ +#include #include #include #include @@ -16,13 +19,25 @@ #include #include #include +#include +#include #include +#include + +static LIST_HEAD(mmc_pwrseq_devs); + +struct mmc_pwrseq_dev { + struct platform_device *pdev; + struct list_head entry; +}; struct mmc_pwrseq_simple { struct pwrseq pwrseq; bool clk_enabled; struct clk *ext_clk; struct gpio_descs *reset_gpios; + unsigned int regulator_count; + struct regulator_bulk_data *regulators; }; #define to_pwrseq_simple(p) container_of(p, struct mmc_pwrseq_simple, pwrseq) @@ -60,6 +75,14 @@ static void mmc_pwrseq_simple_post_power_on(struct pwrseq *_pwrseq) { struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(_pwrseq); + if (pwrseq->regulators) { + int err; + + err = regulator_bulk_enable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } + mmc_pwrseq_simple_set_gpios_value(pwrseq, 0); } @@ -73,6 +96,14 @@ static void mmc_pwrseq_simple_power_off(struct pwrseq *_pwrseq) clk_disable_unprepare(pwrseq->ext_clk); pwrseq->clk_enabled = false; } + + if (pwrseq->regulators) { + int err; + + err = regulator_bulk_disable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } } static const struct pwrseq_ops mmc_pwrseq_simple_ops = { @@ -91,6 +122,7 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev) { struct mmc_pwrseq_simple *pwrseq; struct device *dev = &pdev->dev; + int err; pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); if (!pwrseq) @@ -100,12 +132,39 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev) if (IS_ERR(pwrseq->ext_clk) && PTR_ERR(pwrseq->ext_clk) != -ENOENT) return PTR_ERR(pwrseq->ext_clk); + err = devm_of_regulator_all_get(dev, &pwrseq->regulator_count, + &pwrseq->regulators); + if (err) + return err; + + if (pwrseq->regulators) { + /* + * Be sure that regulator is off, before the driver will start + * power sequence. It is likely that regulator is on by default + * and it without toggling it here, it would be disabled much + * later by the core. + */ + err = regulator_bulk_enable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + + err = regulator_bulk_disable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } + pwrseq->reset_gpios = devm_gpiod_get_array(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(pwrseq->reset_gpios) && PTR_ERR(pwrseq->reset_gpios) != -ENOENT && PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) { - return PTR_ERR(pwrseq->reset_gpios); + /* + * Don't care about errors. If this pwrseq device was added + * to node with existing reset-gpios, then the GPIO reset will + * be handled by other device. + */ + dev_warn(dev, "Cannot get reset gpio: %ld\n", + PTR_ERR(pwrseq->reset_gpios)); } pwrseq->pwrseq.dev = dev; @@ -122,6 +181,14 @@ static int mmc_pwrseq_simple_remove(struct platform_device *pdev) pwrseq_unregister(&pwrseq->pwrseq); + if (pwrseq->regulators) { + int err; + + err = regulator_bulk_disable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } + return 0; } @@ -134,5 +201,45 @@ static struct platform_driver mmc_pwrseq_simple_driver = { }, }; -module_platform_driver(mmc_pwrseq_simple_driver); +static int __init mmc_pwrseq_simple_driver_init(void) +{ + struct mmc_pwrseq_dev *pwrseq_dev; + struct platform_device *pdev; + struct device_node *np; + + for_each_node_with_property(np, "power-sequence") { + pdev = platform_device_register_simple("pwrseq_simple", + PLATFORM_DEVID_AUTO, + NULL, 0); + if (IS_ERR(pdev)) + continue; + + pwrseq_dev = kzalloc(sizeof(*pwrseq_dev), GFP_KERNEL); + if (!pwrseq_dev) + continue; + + of_node_get(np); + pdev->dev.of_node = np; + pwrseq_dev->pdev = pdev; + list_add(&pwrseq_dev->entry, &mmc_pwrseq_devs); + } + + return platform_driver_register(&mmc_pwrseq_simple_driver); +} +module_init(mmc_pwrseq_simple_driver_init); + +static void __exit mmc_pwrseq_simple_driver_exit(void) +{ + struct mmc_pwrseq_dev *pwrseq_dev, *tmp; + + list_for_each_entry_safe(pwrseq_dev, tmp, &mmc_pwrseq_devs, entry) { + list_del(&pwrseq_dev->entry); + of_node_put(pwrseq_dev->pdev->dev.of_node); + platform_device_unregister(pwrseq_dev->pdev); + kfree(pwrseq_dev); + } + + platform_driver_unregister(&mmc_pwrseq_simple_driver); +} +module_exit(mmc_pwrseq_simple_driver_exit); MODULE_LICENSE("GPL v2"); -- 1.9.1 From mboxrd@z Thu Jan 1 00:00:00 1970 From: Krzysztof Kozlowski Subject: [RFC v4 08/14] power: pwrseq: simple: Add support for regulators and generic property Date: Thu, 09 Jun 2016 11:44:25 +0200 Message-ID: <1465465471-28740-9-git-send-email-k.kozlowski@samsung.com> References: <1465465471-28740-1-git-send-email-k.kozlowski@samsung.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: In-reply-to: <1465465471-28740-1-git-send-email-k.kozlowski@samsung.com> 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: Ulf Hansson , Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , Krzysztof Kozlowski , Sebastian Reichel , Dmitry Eremin-Solenikov , David Woodhouse , Liam Girdwood , Mark Brown , Greg Kroah-Hartman , Hans de Goede , Jean-Christophe Plagniol-Villard , Tomi Valkeinen , Heiko Stuebner , linux-mmc@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org, linux-pm@vger.kernel.org, linux-usb@vger. Cc: Bartlomiej Zolnierkiewicz List-Id: devicetree@vger.kernel.org Some devices need real hard-reset by cutting the power. During power sequence turn off and on all of provided regulators. Additionally add support for instantiating the pwrseq-simple device on a generic property 'power-sequence'. The device will attach itself to the node containing the property and parse the node's properties like reset-gpios, supplies etc. Signed-off-by: Krzysztof Kozlowski --- .../bindings/power/pwrseq/pwrseq-simple.txt | 30 +++++- drivers/power/pwrseq/pwrseq_simple.c | 113 ++++++++++++++++++++- 2 files changed, 136 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt index ce0e76749671..7e5a414a67fc 100644 --- a/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt +++ b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt @@ -1,11 +1,18 @@ -* The simple MMC power sequence provider +* The simple power sequence provider -The purpose of the simple MMC power sequence provider is to supports a set of +The purpose of the simple power sequence provider is to supports a set of common properties between various SOC designs. It thus enables us to use the same provider for several SOC designs. -Required properties: -- compatible : contains "mmc-pwrseq-simple". +The driver supports two types of bindings: +1. Property for any node + Required properties: + - power-sequence + +2. Separate node (not recommended for new users) + Required properties: + - compatible : contains "mmc-pwrseq-simple". + Optional properties: - reset-gpios : contains a list of GPIO specifiers. The reset GPIOs are asserted @@ -16,6 +23,8 @@ Optional properties: See ../clocks/clock-bindings.txt for details. - clock-names : Must include the following entry: "ext_clock" (External clock provided to the card). +- *-supply : If supplies are provided, the driver will enable and disable + them during power sequence. Example: @@ -25,3 +34,16 @@ Example: clocks = <&clk_32768_ck>; clock-names = "ext_clock"; } + + usb3503@08 { + compatible = "smsc,usb3503"; + reg = <0x08>; + + intn-gpios = <&gpx3 0 GPIO_ACTIVE_HIGH>; + connect-gpios = <&gpx3 4 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpx3 5 GPIO_ACTIVE_HIGH>; + initial-mode = <1>; + + power-sequence; + vdd-supply = <&buck8_reg>; + }; diff --git a/drivers/power/pwrseq/pwrseq_simple.c b/drivers/power/pwrseq/pwrseq_simple.c index 93807a6ef162..f70e207f994b 100644 --- a/drivers/power/pwrseq/pwrseq_simple.c +++ b/drivers/power/pwrseq/pwrseq_simple.c @@ -1,12 +1,15 @@ /* - * Copyright (C) 2014 Linaro Ltd + * Copyright (C) 2014 Linaro Ltd + * Copyright (C) 2016 Samsung Electronics * * Author: Ulf Hansson + * Krzysztof Kozlowski * * License terms: GNU General Public License (GPL) version 2 * * Simple MMC power sequence management */ +#include #include #include #include @@ -16,13 +19,25 @@ #include #include #include +#include +#include #include +#include + +static LIST_HEAD(mmc_pwrseq_devs); + +struct mmc_pwrseq_dev { + struct platform_device *pdev; + struct list_head entry; +}; struct mmc_pwrseq_simple { struct pwrseq pwrseq; bool clk_enabled; struct clk *ext_clk; struct gpio_descs *reset_gpios; + unsigned int regulator_count; + struct regulator_bulk_data *regulators; }; #define to_pwrseq_simple(p) container_of(p, struct mmc_pwrseq_simple, pwrseq) @@ -60,6 +75,14 @@ static void mmc_pwrseq_simple_post_power_on(struct pwrseq *_pwrseq) { struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(_pwrseq); + if (pwrseq->regulators) { + int err; + + err = regulator_bulk_enable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } + mmc_pwrseq_simple_set_gpios_value(pwrseq, 0); } @@ -73,6 +96,14 @@ static void mmc_pwrseq_simple_power_off(struct pwrseq *_pwrseq) clk_disable_unprepare(pwrseq->ext_clk); pwrseq->clk_enabled = false; } + + if (pwrseq->regulators) { + int err; + + err = regulator_bulk_disable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } } static const struct pwrseq_ops mmc_pwrseq_simple_ops = { @@ -91,6 +122,7 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev) { struct mmc_pwrseq_simple *pwrseq; struct device *dev = &pdev->dev; + int err; pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); if (!pwrseq) @@ -100,12 +132,39 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev) if (IS_ERR(pwrseq->ext_clk) && PTR_ERR(pwrseq->ext_clk) != -ENOENT) return PTR_ERR(pwrseq->ext_clk); + err = devm_of_regulator_all_get(dev, &pwrseq->regulator_count, + &pwrseq->regulators); + if (err) + return err; + + if (pwrseq->regulators) { + /* + * Be sure that regulator is off, before the driver will start + * power sequence. It is likely that regulator is on by default + * and it without toggling it here, it would be disabled much + * later by the core. + */ + err = regulator_bulk_enable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + + err = regulator_bulk_disable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } + pwrseq->reset_gpios = devm_gpiod_get_array(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(pwrseq->reset_gpios) && PTR_ERR(pwrseq->reset_gpios) != -ENOENT && PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) { - return PTR_ERR(pwrseq->reset_gpios); + /* + * Don't care about errors. If this pwrseq device was added + * to node with existing reset-gpios, then the GPIO reset will + * be handled by other device. + */ + dev_warn(dev, "Cannot get reset gpio: %ld\n", + PTR_ERR(pwrseq->reset_gpios)); } pwrseq->pwrseq.dev = dev; @@ -122,6 +181,14 @@ static int mmc_pwrseq_simple_remove(struct platform_device *pdev) pwrseq_unregister(&pwrseq->pwrseq); + if (pwrseq->regulators) { + int err; + + err = regulator_bulk_disable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } + return 0; } @@ -134,5 +201,45 @@ static struct platform_driver mmc_pwrseq_simple_driver = { }, }; -module_platform_driver(mmc_pwrseq_simple_driver); +static int __init mmc_pwrseq_simple_driver_init(void) +{ + struct mmc_pwrseq_dev *pwrseq_dev; + struct platform_device *pdev; + struct device_node *np; + + for_each_node_with_property(np, "power-sequence") { + pdev = platform_device_register_simple("pwrseq_simple", + PLATFORM_DEVID_AUTO, + NULL, 0); + if (IS_ERR(pdev)) + continue; + + pwrseq_dev = kzalloc(sizeof(*pwrseq_dev), GFP_KERNEL); + if (!pwrseq_dev) + continue; + + of_node_get(np); + pdev->dev.of_node = np; + pwrseq_dev->pdev = pdev; + list_add(&pwrseq_dev->entry, &mmc_pwrseq_devs); + } + + return platform_driver_register(&mmc_pwrseq_simple_driver); +} +module_init(mmc_pwrseq_simple_driver_init); + +static void __exit mmc_pwrseq_simple_driver_exit(void) +{ + struct mmc_pwrseq_dev *pwrseq_dev, *tmp; + + list_for_each_entry_safe(pwrseq_dev, tmp, &mmc_pwrseq_devs, entry) { + list_del(&pwrseq_dev->entry); + of_node_put(pwrseq_dev->pdev->dev.of_node); + platform_device_unregister(pwrseq_dev->pdev); + kfree(pwrseq_dev); + } + + platform_driver_unregister(&mmc_pwrseq_simple_driver); +} +module_exit(mmc_pwrseq_simple_driver_exit); MODULE_LICENSE("GPL v2"); -- 1.9.1 From mboxrd@z Thu Jan 1 00:00:00 1970 From: Krzysztof Kozlowski Date: Thu, 09 Jun 2016 09:44:25 +0000 Subject: [RFC v4 08/14] power: pwrseq: simple: Add support for regulators and generic property Message-Id: <1465465471-28740-9-git-send-email-k.kozlowski@samsung.com> List-Id: References: <1465465471-28740-1-git-send-email-k.kozlowski@samsung.com> In-Reply-To: <1465465471-28740-1-git-send-email-k.kozlowski@samsung.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: linux-arm-kernel@lists.infradead.org Some devices need real hard-reset by cutting the power. During power sequence turn off and on all of provided regulators. Additionally add support for instantiating the pwrseq-simple device on a generic property 'power-sequence'. The device will attach itself to the node containing the property and parse the node's properties like reset-gpios, supplies etc. Signed-off-by: Krzysztof Kozlowski --- .../bindings/power/pwrseq/pwrseq-simple.txt | 30 +++++- drivers/power/pwrseq/pwrseq_simple.c | 113 ++++++++++++++++++++- 2 files changed, 136 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt index ce0e76749671..7e5a414a67fc 100644 --- a/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt +++ b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt @@ -1,11 +1,18 @@ -* The simple MMC power sequence provider +* The simple power sequence provider -The purpose of the simple MMC power sequence provider is to supports a set of +The purpose of the simple power sequence provider is to supports a set of common properties between various SOC designs. It thus enables us to use the same provider for several SOC designs. -Required properties: -- compatible : contains "mmc-pwrseq-simple". +The driver supports two types of bindings: +1. Property for any node + Required properties: + - power-sequence + +2. Separate node (not recommended for new users) + Required properties: + - compatible : contains "mmc-pwrseq-simple". + Optional properties: - reset-gpios : contains a list of GPIO specifiers. The reset GPIOs are asserted @@ -16,6 +23,8 @@ Optional properties: See ../clocks/clock-bindings.txt for details. - clock-names : Must include the following entry: "ext_clock" (External clock provided to the card). +- *-supply : If supplies are provided, the driver will enable and disable + them during power sequence. Example: @@ -25,3 +34,16 @@ Example: clocks = <&clk_32768_ck>; clock-names = "ext_clock"; } + + usb3503@08 { + compatible = "smsc,usb3503"; + reg = <0x08>; + + intn-gpios = <&gpx3 0 GPIO_ACTIVE_HIGH>; + connect-gpios = <&gpx3 4 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpx3 5 GPIO_ACTIVE_HIGH>; + initial-mode = <1>; + + power-sequence; + vdd-supply = <&buck8_reg>; + }; diff --git a/drivers/power/pwrseq/pwrseq_simple.c b/drivers/power/pwrseq/pwrseq_simple.c index 93807a6ef162..f70e207f994b 100644 --- a/drivers/power/pwrseq/pwrseq_simple.c +++ b/drivers/power/pwrseq/pwrseq_simple.c @@ -1,12 +1,15 @@ /* - * Copyright (C) 2014 Linaro Ltd + * Copyright (C) 2014 Linaro Ltd + * Copyright (C) 2016 Samsung Electronics * * Author: Ulf Hansson + * Krzysztof Kozlowski * * License terms: GNU General Public License (GPL) version 2 * * Simple MMC power sequence management */ +#include #include #include #include @@ -16,13 +19,25 @@ #include #include #include +#include +#include #include +#include + +static LIST_HEAD(mmc_pwrseq_devs); + +struct mmc_pwrseq_dev { + struct platform_device *pdev; + struct list_head entry; +}; struct mmc_pwrseq_simple { struct pwrseq pwrseq; bool clk_enabled; struct clk *ext_clk; struct gpio_descs *reset_gpios; + unsigned int regulator_count; + struct regulator_bulk_data *regulators; }; #define to_pwrseq_simple(p) container_of(p, struct mmc_pwrseq_simple, pwrseq) @@ -60,6 +75,14 @@ static void mmc_pwrseq_simple_post_power_on(struct pwrseq *_pwrseq) { struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(_pwrseq); + if (pwrseq->regulators) { + int err; + + err = regulator_bulk_enable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } + mmc_pwrseq_simple_set_gpios_value(pwrseq, 0); } @@ -73,6 +96,14 @@ static void mmc_pwrseq_simple_power_off(struct pwrseq *_pwrseq) clk_disable_unprepare(pwrseq->ext_clk); pwrseq->clk_enabled = false; } + + if (pwrseq->regulators) { + int err; + + err = regulator_bulk_disable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } } static const struct pwrseq_ops mmc_pwrseq_simple_ops = { @@ -91,6 +122,7 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev) { struct mmc_pwrseq_simple *pwrseq; struct device *dev = &pdev->dev; + int err; pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); if (!pwrseq) @@ -100,12 +132,39 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev) if (IS_ERR(pwrseq->ext_clk) && PTR_ERR(pwrseq->ext_clk) != -ENOENT) return PTR_ERR(pwrseq->ext_clk); + err = devm_of_regulator_all_get(dev, &pwrseq->regulator_count, + &pwrseq->regulators); + if (err) + return err; + + if (pwrseq->regulators) { + /* + * Be sure that regulator is off, before the driver will start + * power sequence. It is likely that regulator is on by default + * and it without toggling it here, it would be disabled much + * later by the core. + */ + err = regulator_bulk_enable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + + err = regulator_bulk_disable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } + pwrseq->reset_gpios = devm_gpiod_get_array(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(pwrseq->reset_gpios) && PTR_ERR(pwrseq->reset_gpios) != -ENOENT && PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) { - return PTR_ERR(pwrseq->reset_gpios); + /* + * Don't care about errors. If this pwrseq device was added + * to node with existing reset-gpios, then the GPIO reset will + * be handled by other device. + */ + dev_warn(dev, "Cannot get reset gpio: %ld\n", + PTR_ERR(pwrseq->reset_gpios)); } pwrseq->pwrseq.dev = dev; @@ -122,6 +181,14 @@ static int mmc_pwrseq_simple_remove(struct platform_device *pdev) pwrseq_unregister(&pwrseq->pwrseq); + if (pwrseq->regulators) { + int err; + + err = regulator_bulk_disable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } + return 0; } @@ -134,5 +201,45 @@ static struct platform_driver mmc_pwrseq_simple_driver = { }, }; -module_platform_driver(mmc_pwrseq_simple_driver); +static int __init mmc_pwrseq_simple_driver_init(void) +{ + struct mmc_pwrseq_dev *pwrseq_dev; + struct platform_device *pdev; + struct device_node *np; + + for_each_node_with_property(np, "power-sequence") { + pdev = platform_device_register_simple("pwrseq_simple", + PLATFORM_DEVID_AUTO, + NULL, 0); + if (IS_ERR(pdev)) + continue; + + pwrseq_dev = kzalloc(sizeof(*pwrseq_dev), GFP_KERNEL); + if (!pwrseq_dev) + continue; + + of_node_get(np); + pdev->dev.of_node = np; + pwrseq_dev->pdev = pdev; + list_add(&pwrseq_dev->entry, &mmc_pwrseq_devs); + } + + return platform_driver_register(&mmc_pwrseq_simple_driver); +} +module_init(mmc_pwrseq_simple_driver_init); + +static void __exit mmc_pwrseq_simple_driver_exit(void) +{ + struct mmc_pwrseq_dev *pwrseq_dev, *tmp; + + list_for_each_entry_safe(pwrseq_dev, tmp, &mmc_pwrseq_devs, entry) { + list_del(&pwrseq_dev->entry); + of_node_put(pwrseq_dev->pdev->dev.of_node); + platform_device_unregister(pwrseq_dev->pdev); + kfree(pwrseq_dev); + } + + platform_driver_unregister(&mmc_pwrseq_simple_driver); +} +module_exit(mmc_pwrseq_simple_driver_exit); MODULE_LICENSE("GPL v2"); -- 1.9.1 From mboxrd@z Thu Jan 1 00:00:00 1970 From: k.kozlowski@samsung.com (Krzysztof Kozlowski) Date: Thu, 09 Jun 2016 11:44:25 +0200 Subject: [RFC v4 08/14] power: pwrseq: simple: Add support for regulators and generic property In-Reply-To: <1465465471-28740-1-git-send-email-k.kozlowski@samsung.com> References: <1465465471-28740-1-git-send-email-k.kozlowski@samsung.com> Message-ID: <1465465471-28740-9-git-send-email-k.kozlowski@samsung.com> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org Some devices need real hard-reset by cutting the power. During power sequence turn off and on all of provided regulators. Additionally add support for instantiating the pwrseq-simple device on a generic property 'power-sequence'. The device will attach itself to the node containing the property and parse the node's properties like reset-gpios, supplies etc. Signed-off-by: Krzysztof Kozlowski --- .../bindings/power/pwrseq/pwrseq-simple.txt | 30 +++++- drivers/power/pwrseq/pwrseq_simple.c | 113 ++++++++++++++++++++- 2 files changed, 136 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt index ce0e76749671..7e5a414a67fc 100644 --- a/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt +++ b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt @@ -1,11 +1,18 @@ -* The simple MMC power sequence provider +* The simple power sequence provider -The purpose of the simple MMC power sequence provider is to supports a set of +The purpose of the simple power sequence provider is to supports a set of common properties between various SOC designs. It thus enables us to use the same provider for several SOC designs. -Required properties: -- compatible : contains "mmc-pwrseq-simple". +The driver supports two types of bindings: +1. Property for any node + Required properties: + - power-sequence + +2. Separate node (not recommended for new users) + Required properties: + - compatible : contains "mmc-pwrseq-simple". + Optional properties: - reset-gpios : contains a list of GPIO specifiers. The reset GPIOs are asserted @@ -16,6 +23,8 @@ Optional properties: See ../clocks/clock-bindings.txt for details. - clock-names : Must include the following entry: "ext_clock" (External clock provided to the card). +- *-supply : If supplies are provided, the driver will enable and disable + them during power sequence. Example: @@ -25,3 +34,16 @@ Example: clocks = <&clk_32768_ck>; clock-names = "ext_clock"; } + + usb3503 at 08 { + compatible = "smsc,usb3503"; + reg = <0x08>; + + intn-gpios = <&gpx3 0 GPIO_ACTIVE_HIGH>; + connect-gpios = <&gpx3 4 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpx3 5 GPIO_ACTIVE_HIGH>; + initial-mode = <1>; + + power-sequence; + vdd-supply = <&buck8_reg>; + }; diff --git a/drivers/power/pwrseq/pwrseq_simple.c b/drivers/power/pwrseq/pwrseq_simple.c index 93807a6ef162..f70e207f994b 100644 --- a/drivers/power/pwrseq/pwrseq_simple.c +++ b/drivers/power/pwrseq/pwrseq_simple.c @@ -1,12 +1,15 @@ /* - * Copyright (C) 2014 Linaro Ltd + * Copyright (C) 2014 Linaro Ltd + * Copyright (C) 2016 Samsung Electronics * * Author: Ulf Hansson + * Krzysztof Kozlowski * * License terms: GNU General Public License (GPL) version 2 * * Simple MMC power sequence management */ +#include #include #include #include @@ -16,13 +19,25 @@ #include #include #include +#include +#include #include +#include + +static LIST_HEAD(mmc_pwrseq_devs); + +struct mmc_pwrseq_dev { + struct platform_device *pdev; + struct list_head entry; +}; struct mmc_pwrseq_simple { struct pwrseq pwrseq; bool clk_enabled; struct clk *ext_clk; struct gpio_descs *reset_gpios; + unsigned int regulator_count; + struct regulator_bulk_data *regulators; }; #define to_pwrseq_simple(p) container_of(p, struct mmc_pwrseq_simple, pwrseq) @@ -60,6 +75,14 @@ static void mmc_pwrseq_simple_post_power_on(struct pwrseq *_pwrseq) { struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(_pwrseq); + if (pwrseq->regulators) { + int err; + + err = regulator_bulk_enable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } + mmc_pwrseq_simple_set_gpios_value(pwrseq, 0); } @@ -73,6 +96,14 @@ static void mmc_pwrseq_simple_power_off(struct pwrseq *_pwrseq) clk_disable_unprepare(pwrseq->ext_clk); pwrseq->clk_enabled = false; } + + if (pwrseq->regulators) { + int err; + + err = regulator_bulk_disable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } } static const struct pwrseq_ops mmc_pwrseq_simple_ops = { @@ -91,6 +122,7 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev) { struct mmc_pwrseq_simple *pwrseq; struct device *dev = &pdev->dev; + int err; pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); if (!pwrseq) @@ -100,12 +132,39 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev) if (IS_ERR(pwrseq->ext_clk) && PTR_ERR(pwrseq->ext_clk) != -ENOENT) return PTR_ERR(pwrseq->ext_clk); + err = devm_of_regulator_all_get(dev, &pwrseq->regulator_count, + &pwrseq->regulators); + if (err) + return err; + + if (pwrseq->regulators) { + /* + * Be sure that regulator is off, before the driver will start + * power sequence. It is likely that regulator is on by default + * and it without toggling it here, it would be disabled much + * later by the core. + */ + err = regulator_bulk_enable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + + err = regulator_bulk_disable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } + pwrseq->reset_gpios = devm_gpiod_get_array(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(pwrseq->reset_gpios) && PTR_ERR(pwrseq->reset_gpios) != -ENOENT && PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) { - return PTR_ERR(pwrseq->reset_gpios); + /* + * Don't care about errors. If this pwrseq device was added + * to node with existing reset-gpios, then the GPIO reset will + * be handled by other device. + */ + dev_warn(dev, "Cannot get reset gpio: %ld\n", + PTR_ERR(pwrseq->reset_gpios)); } pwrseq->pwrseq.dev = dev; @@ -122,6 +181,14 @@ static int mmc_pwrseq_simple_remove(struct platform_device *pdev) pwrseq_unregister(&pwrseq->pwrseq); + if (pwrseq->regulators) { + int err; + + err = regulator_bulk_disable(pwrseq->regulator_count, + pwrseq->regulators); + WARN_ON_ONCE(err); + } + return 0; } @@ -134,5 +201,45 @@ static struct platform_driver mmc_pwrseq_simple_driver = { }, }; -module_platform_driver(mmc_pwrseq_simple_driver); +static int __init mmc_pwrseq_simple_driver_init(void) +{ + struct mmc_pwrseq_dev *pwrseq_dev; + struct platform_device *pdev; + struct device_node *np; + + for_each_node_with_property(np, "power-sequence") { + pdev = platform_device_register_simple("pwrseq_simple", + PLATFORM_DEVID_AUTO, + NULL, 0); + if (IS_ERR(pdev)) + continue; + + pwrseq_dev = kzalloc(sizeof(*pwrseq_dev), GFP_KERNEL); + if (!pwrseq_dev) + continue; + + of_node_get(np); + pdev->dev.of_node = np; + pwrseq_dev->pdev = pdev; + list_add(&pwrseq_dev->entry, &mmc_pwrseq_devs); + } + + return platform_driver_register(&mmc_pwrseq_simple_driver); +} +module_init(mmc_pwrseq_simple_driver_init); + +static void __exit mmc_pwrseq_simple_driver_exit(void) +{ + struct mmc_pwrseq_dev *pwrseq_dev, *tmp; + + list_for_each_entry_safe(pwrseq_dev, tmp, &mmc_pwrseq_devs, entry) { + list_del(&pwrseq_dev->entry); + of_node_put(pwrseq_dev->pdev->dev.of_node); + platform_device_unregister(pwrseq_dev->pdev); + kfree(pwrseq_dev); + } + + platform_driver_unregister(&mmc_pwrseq_simple_driver); +} +module_exit(mmc_pwrseq_simple_driver_exit); MODULE_LICENSE("GPL v2"); -- 1.9.1