From patchwork Mon Jan 20 03:56:53 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Olof Johansson X-Patchwork-Id: 3510601 Return-Path: X-Original-To: patchwork-linux-arm@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 2815BC02DC for ; Mon, 20 Jan 2014 03:57:23 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 150A2200E9 for ; Mon, 20 Jan 2014 03:57:22 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id D6F61200E5 for ; Mon, 20 Jan 2014 03:57:20 +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 1W55yf-00040s-Dr; Mon, 20 Jan 2014 03:56:45 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1W55yW-0002Pp-GL; Mon, 20 Jan 2014 03:56:36 +0000 Received: from mail-pb0-f46.google.com ([209.85.160.46]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1W55yI-0002NT-VT for linux-arm-kernel@lists.infradead.org; Mon, 20 Jan 2014 03:56:23 +0000 Received: by mail-pb0-f46.google.com with SMTP id um1so1450470pbc.33 for ; Sun, 19 Jan 2014 19:55:58 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=VnzPpSb71ikmb2IEHA8k8Sh+MA0GfNJ6e/Lf1u/WMBY=; b=azYqGL9DGwBkbPumPqs2Geg4K6CEuV0HX4dlDzR5jHxhwX4YxlIjqdCI+yugEon4jR 7fi8wUHLR9caRjGfPPHKx7VDh3Cnamcv/+Voz+eXv7cl0w9X6SWWD0Hr6l/Hi8IgMOv+ D0Iv3/DPmWJNcei+vYuI3/1DEtgtc+qCKdqBx7+9Oi9GiWF7rew73njADVkRBHz/jOEq ZXLJO4nYYQQDQi81FfEN65awDOTaz11CrupJyBp2V1Dib9Z+Sa7EUvW+t4/M5RwiD4vI B/Zy63Ti0ncUcAfxCZ1kZEMEocCpYlcpK8oFiaVTbm6dQkyS9E1w37XndHey/2xWIQlD y2Cw== X-Gm-Message-State: ALoCoQmCsmLOATUz1iNSqAkluOE1eKBpsfn+YNJjzbhNcIkPERsawBpOUdT4OJhCdjrL6Iyhl+i4 X-Received: by 10.66.155.7 with SMTP id vs7mr16207142pab.42.1390190158762; Sun, 19 Jan 2014 19:55:58 -0800 (PST) Received: from brutus.lixom.net (173-13-129-225-sfba.hfc.comcastbusiness.net. [173.13.129.225]) by mx.google.com with ESMTPSA id vf7sm40622917pbc.5.2014.01.19.19.55.57 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 19 Jan 2014 19:55:57 -0800 (PST) From: Olof Johansson To: linux-mmc@vger.kernel.org Subject: [PATCH 1/3] mmc: add support for power-on sequencing through DT Date: Sun, 19 Jan 2014 19:56:53 -0800 Message-Id: <1390190215-22700-2-git-send-email-olof@lixom.net> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1390190215-22700-1-git-send-email-olof@lixom.net> References: <1390190215-22700-1-git-send-email-olof@lixom.net> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140119_225623_165622_CB4682C1 X-CRM114-Status: GOOD ( 21.70 ) X-Spam-Score: -2.6 (--) Cc: mark.rutland@arm.com, devicetree@vger.kernel.org, linux@arm.linux.org.uk, pawel.moll@arm.com, chris@printf.net, robh+dt@kernel.org, ijc+devicetree@hellion.org.ok, galak@codeaurora.org, Olof Johansson , linux-arm-kernel@lists.infradead.org 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 This patch enables support for power-on sequencing of SDIO peripherals through DT. In general, it's quite common that wifi modules and other similar peripherals have several signals in addition to the SDIO interface that needs wiggling before the module will power on. It's common to have a reference clock, one or several power rails and one or several lines for reset/enable type functions. The binding as written today introduces a number of reset gpios, a regulator and a clock specifier. The code will handle up to 2 gpio reset lines, but it's trivial to increase to more than that if needed at some point. Implementation-wise, the MMC core has been changed to handle this during host power up, before the host interface is powered on. I have not yet implemented the power-down side, I wanted people to have a chance for reporting back w.r.t. issues (or comments on the bindings) first. I have not tested the regulator portion, since the system and module I'm working on doesn't need one (Samsung Chromebook with Marvell 8797-based wifi). Testing of those portions (and reporting back) would be appreciated. Signed-off-by: Olof Johansson --- Documentation/devicetree/bindings/mmc/mmc.txt | 11 +++++++ drivers/mmc/core/core.c | 42 +++++++++++++++++++++++++ drivers/mmc/core/host.c | 30 +++++++++++++++++- include/linux/mmc/host.h | 5 +++ 4 files changed, 87 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index 458b57f..962e0ee 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -5,6 +5,8 @@ these definitions. Interpreted by the OF core: - reg: Registers location and length. - interrupts: Interrupts used by the MMC controller. +- clocks: Clocks needed for the host controller, if any. +- clock-names: Goes with clocks above. Card detection: If no property below is supplied, host native card detect is used. @@ -30,6 +32,15 @@ Optional properties: - cap-sdio-irq: enable SDIO IRQ signalling on this interface - full-pwr-cycle: full power cycle of the card is supported +Card power and reset control: +The following properties can be specified for cases where the MMC +peripheral needs additional reset, regulator and clock lines. It is for +example common for WiFi/BT adapters to have these separate from the main +MMC bus: + - card-reset-gpios: Specify GPIOs for card reset (reset active low) + - card-external-vcc-supply: Regulator to drive (independent) card VCC + - clock with name "card_ext_clock": External clock provided to the card + *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line polarity properties, we have to fix the meaning of the "normal" and "inverted" line levels. We choose to follow the SDHCI standard, which specifies both those diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 098374b..c43e6c8 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -13,11 +13,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -1519,6 +1521,43 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type) mmc_host_clk_release(host); } +static void mmc_card_power_up(struct mmc_host *host) +{ + int i; + struct gpio_desc **gds = host->card_reset_gpios; + + for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) { + if (gds[i]) { + dev_dbg(host->parent, "Asserting reset line %d", i); + gpiod_set_value(gds[i], 1); + } + } + + if (host->card_regulator) { + dev_dbg(host->parent, "Enabling external regulator"); + if (regulator_enable(host->card_regulator)) + dev_err(host->parent, "Failed to enable external regulator"); + } + + if (host->card_clk) { + dev_dbg(host->parent, "Enabling external clock"); + clk_prepare_enable(host->card_clk); + } + + /* 2ms delay to let clocks and power settle */ + mmc_delay(20); + + for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) { + if (gds[i]) { + dev_dbg(host->parent, "Deasserting reset line %d", i); + gpiod_set_value(gds[i], 0); + } + } + + /* 2ms delay to after reset release */ + mmc_delay(20); +} + /* * Apply power to the MMC stack. This is a two-stage process. * First, we enable power to the card without the clock running. @@ -1535,6 +1574,9 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) if (host->ios.power_mode == MMC_POWER_ON) return; + /* Power up the card/module first, if needed */ + mmc_card_power_up(host); + mmc_host_clk_hold(host); host->ios.vdd = fls(ocr) - 1; diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 49bc403..e6b850b 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -12,14 +12,18 @@ * MMC host class device management */ +#include +#include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -312,7 +316,7 @@ int mmc_of_parse(struct mmc_host *host) u32 bus_width; bool explicit_inv_wp, gpio_inv_wp = false; enum of_gpio_flags flags; - int len, ret, gpio; + int i, len, ret, gpio; if (!host->parent || !host->parent->of_node) return 0; @@ -415,6 +419,30 @@ int mmc_of_parse(struct mmc_host *host) if (explicit_inv_wp ^ gpio_inv_wp) host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; + /* Parse card power/reset/clock control */ + if (of_find_property(np, "card-reset-gpios", NULL)) { + struct gpio_desc *gpd; + for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) { + gpd = devm_gpiod_get_index(host->parent, "card-reset", i); + if (IS_ERR(gpd)) + break; + gpiod_direction_output(gpd, 0); + host->card_reset_gpios[i] = gpd; + } + + gpd = devm_gpiod_get_index(host->parent, "card-reset", ARRAY_SIZE(host->card_reset_gpios)); + if (!IS_ERR(gpd)) { + dev_warn(host->parent, "More reset gpios than we can handle"); + gpiod_put(gpd); + } + } + + host->card_clk = of_clk_get_by_name(np, "card_ext_clock"); + if (IS_ERR(host->card_clk)) + host->card_clk = NULL; + + host->card_regulator = regulator_get(host->parent, "card-external-vcc"); + if (of_find_property(np, "cap-sd-highspeed", &len)) host->caps |= MMC_CAP_SD_HIGHSPEED; if (of_find_property(np, "cap-mmc-highspeed", &len)) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 99f5709..6781887 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -297,6 +297,11 @@ struct mmc_host { unsigned long clkgate_delay; #endif + /* card specific properties to deal with power and reset */ + struct regulator *card_regulator; /* External VCC needed by the card */ + struct gpio_desc *card_reset_gpios[2]; /* External resets, active low */ + struct clk *card_clk; /* External clock needed by the card */ + /* host specific block data */ unsigned int max_seg_size; /* see blk_queue_max_segment_size */ unsigned short max_segs; /* see blk_queue_max_segments */