forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
mmc: pwrseq: add driver for emmc hardware reset
This patch provides a simple mmc-pwrseq-emmc driver, which controls single gpio line. It perform standard eMMC hw reset procedure, as descibed by Jedec 4.4 specification. This procedure is performed just after MMC core enabled power to the given mmc host (to fix possible issues if bootloader has left eMMC card in initialized or unknown state), and before performing complete system reboot (also in case of emergency reboot call). The latter is needed on boards, which doesn't have hardware reset logic connected to emmc card and (limited or broken) ROM bootloaders are unable to read second stage from the emmc card if the card is left in unknown or already initialized state. Signed-off-by: Marek Szyprowski <[email protected]> Signed-off-by: Ulf Hansson <[email protected]>
- Loading branch information
Showing
5 changed files
with
131 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
* The simple eMMC hardware reset provider | ||
|
||
The purpose of this driver is to perform standard eMMC hw reset | ||
procedure, as descibed by Jedec 4.4 specification. This procedure is | ||
performed just after MMC core enabled power to the given mmc host (to | ||
fix possible issues if bootloader has left eMMC card in initialized or | ||
unknown state), and before performing complete system reboot (also in | ||
case of emergency reboot call). The latter is needed on boards, which | ||
doesn't have hardware reset logic connected to emmc card and (limited or | ||
broken) ROM bootloaders are unable to read second stage from the emmc | ||
card if the card is left in unknown or already initialized state. | ||
|
||
Required properties: | ||
- compatible : contains "mmc-pwrseq-emmc". | ||
- reset-gpios : contains a GPIO specifier. The reset GPIO is asserted | ||
and then deasserted to perform eMMC card reset. To perform | ||
reset procedure as described in Jedec 4.4 specification, the | ||
gpio line should be defined as GPIO_ACTIVE_LOW. | ||
|
||
Example: | ||
|
||
sdhci0_pwrseq { | ||
compatible = "mmc-pwrseq-emmc"; | ||
reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/* | ||
* Copyright (C) 2015, Samsung Electronics Co., Ltd. | ||
* | ||
* Author: Marek Szyprowski <[email protected]> | ||
* | ||
* License terms: GNU General Public License (GPL) version 2 | ||
* | ||
* Simple eMMC hardware reset provider | ||
*/ | ||
#include <linux/delay.h> | ||
#include <linux/kernel.h> | ||
#include <linux/slab.h> | ||
#include <linux/device.h> | ||
#include <linux/err.h> | ||
#include <linux/gpio/consumer.h> | ||
#include <linux/reboot.h> | ||
|
||
#include <linux/mmc/host.h> | ||
|
||
#include "pwrseq.h" | ||
|
||
struct mmc_pwrseq_emmc { | ||
struct mmc_pwrseq pwrseq; | ||
struct notifier_block reset_nb; | ||
struct gpio_desc *reset_gpio; | ||
}; | ||
|
||
static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq) | ||
{ | ||
gpiod_set_value(pwrseq->reset_gpio, 1); | ||
udelay(1); | ||
gpiod_set_value(pwrseq->reset_gpio, 0); | ||
udelay(200); | ||
} | ||
|
||
static void mmc_pwrseq_emmc_reset(struct mmc_host *host) | ||
{ | ||
struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq, | ||
struct mmc_pwrseq_emmc, pwrseq); | ||
|
||
__mmc_pwrseq_emmc_reset(pwrseq); | ||
} | ||
|
||
static void mmc_pwrseq_emmc_free(struct mmc_host *host) | ||
{ | ||
struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq, | ||
struct mmc_pwrseq_emmc, pwrseq); | ||
|
||
unregister_restart_handler(&pwrseq->reset_nb); | ||
gpiod_put(pwrseq->reset_gpio); | ||
kfree(pwrseq); | ||
host->pwrseq = NULL; | ||
} | ||
|
||
static struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = { | ||
.post_power_on = mmc_pwrseq_emmc_reset, | ||
.free = mmc_pwrseq_emmc_free, | ||
}; | ||
|
||
static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this, | ||
unsigned long mode, void *cmd) | ||
{ | ||
struct mmc_pwrseq_emmc *pwrseq = container_of(this, | ||
struct mmc_pwrseq_emmc, reset_nb); | ||
|
||
__mmc_pwrseq_emmc_reset(pwrseq); | ||
return NOTIFY_DONE; | ||
} | ||
|
||
int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev) | ||
{ | ||
struct mmc_pwrseq_emmc *pwrseq; | ||
int ret = 0; | ||
|
||
pwrseq = kzalloc(sizeof(struct mmc_pwrseq_emmc), GFP_KERNEL); | ||
if (!pwrseq) | ||
return -ENOMEM; | ||
|
||
pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_LOW); | ||
if (IS_ERR(pwrseq->reset_gpio)) { | ||
ret = PTR_ERR(pwrseq->reset_gpio); | ||
goto free; | ||
} | ||
|
||
/* | ||
* register reset handler to ensure emmc reset also from | ||
* emergency_reboot(), priority 129 schedules it just before | ||
* system reboot | ||
*/ | ||
pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb; | ||
pwrseq->reset_nb.priority = 129; | ||
register_restart_handler(&pwrseq->reset_nb); | ||
|
||
pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops; | ||
host->pwrseq = &pwrseq->pwrseq; | ||
|
||
return 0; | ||
free: | ||
kfree(pwrseq); | ||
return ret; | ||
} |