pwrseq_emmc.c (3155B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2015, Samsung Electronics Co., Ltd. 4 * 5 * Author: Marek Szyprowski <m.szyprowski@samsung.com> 6 * 7 * Simple eMMC hardware reset provider 8 */ 9#include <linux/delay.h> 10#include <linux/kernel.h> 11#include <linux/init.h> 12#include <linux/platform_device.h> 13#include <linux/module.h> 14#include <linux/slab.h> 15#include <linux/device.h> 16#include <linux/err.h> 17#include <linux/gpio/consumer.h> 18#include <linux/reboot.h> 19 20#include <linux/mmc/host.h> 21 22#include "pwrseq.h" 23 24struct mmc_pwrseq_emmc { 25 struct mmc_pwrseq pwrseq; 26 struct notifier_block reset_nb; 27 struct gpio_desc *reset_gpio; 28}; 29 30#define to_pwrseq_emmc(p) container_of(p, struct mmc_pwrseq_emmc, pwrseq) 31 32static void mmc_pwrseq_emmc_reset(struct mmc_host *host) 33{ 34 struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(host->pwrseq); 35 36 gpiod_set_value_cansleep(pwrseq->reset_gpio, 1); 37 udelay(1); 38 gpiod_set_value_cansleep(pwrseq->reset_gpio, 0); 39 udelay(200); 40} 41 42static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this, 43 unsigned long mode, void *cmd) 44{ 45 struct mmc_pwrseq_emmc *pwrseq = container_of(this, 46 struct mmc_pwrseq_emmc, reset_nb); 47 gpiod_set_value(pwrseq->reset_gpio, 1); 48 udelay(1); 49 gpiod_set_value(pwrseq->reset_gpio, 0); 50 udelay(200); 51 52 return NOTIFY_DONE; 53} 54 55static const struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = { 56 .reset = mmc_pwrseq_emmc_reset, 57}; 58 59static int mmc_pwrseq_emmc_probe(struct platform_device *pdev) 60{ 61 struct mmc_pwrseq_emmc *pwrseq; 62 struct device *dev = &pdev->dev; 63 64 pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); 65 if (!pwrseq) 66 return -ENOMEM; 67 68 pwrseq->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); 69 if (IS_ERR(pwrseq->reset_gpio)) 70 return PTR_ERR(pwrseq->reset_gpio); 71 72 if (!gpiod_cansleep(pwrseq->reset_gpio)) { 73 /* 74 * register reset handler to ensure emmc reset also from 75 * emergency_reboot(), priority 255 is the highest priority 76 * so it will be executed before any system reboot handler. 77 */ 78 pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb; 79 pwrseq->reset_nb.priority = 255; 80 register_restart_handler(&pwrseq->reset_nb); 81 } else { 82 dev_notice(dev, "EMMC reset pin tied to a sleepy GPIO driver; reset on emergency-reboot disabled\n"); 83 } 84 85 pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops; 86 pwrseq->pwrseq.dev = dev; 87 pwrseq->pwrseq.owner = THIS_MODULE; 88 platform_set_drvdata(pdev, pwrseq); 89 90 return mmc_pwrseq_register(&pwrseq->pwrseq); 91} 92 93static int mmc_pwrseq_emmc_remove(struct platform_device *pdev) 94{ 95 struct mmc_pwrseq_emmc *pwrseq = platform_get_drvdata(pdev); 96 97 unregister_restart_handler(&pwrseq->reset_nb); 98 mmc_pwrseq_unregister(&pwrseq->pwrseq); 99 100 return 0; 101} 102 103static const struct of_device_id mmc_pwrseq_emmc_of_match[] = { 104 { .compatible = "mmc-pwrseq-emmc",}, 105 {/* sentinel */}, 106}; 107 108MODULE_DEVICE_TABLE(of, mmc_pwrseq_emmc_of_match); 109 110static struct platform_driver mmc_pwrseq_emmc_driver = { 111 .probe = mmc_pwrseq_emmc_probe, 112 .remove = mmc_pwrseq_emmc_remove, 113 .driver = { 114 .name = "pwrseq_emmc", 115 .of_match_table = mmc_pwrseq_emmc_of_match, 116 }, 117}; 118 119module_platform_driver(mmc_pwrseq_emmc_driver); 120MODULE_LICENSE("GPL v2");