Branch data Line data Source code
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 : : 24 : : struct 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 : : 32 : 0 : static void mmc_pwrseq_emmc_reset(struct mmc_host *host) 33 : : { 34 : 0 : struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(host->pwrseq); 35 : : 36 : 0 : gpiod_set_value_cansleep(pwrseq->reset_gpio, 1); 37 : 0 : udelay(1); 38 : 0 : gpiod_set_value_cansleep(pwrseq->reset_gpio, 0); 39 : 0 : udelay(200); 40 : 0 : } 41 : : 42 : 0 : static 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 : 0 : gpiod_set_value(pwrseq->reset_gpio, 1); 48 : 0 : udelay(1); 49 : 0 : gpiod_set_value(pwrseq->reset_gpio, 0); 50 : 0 : udelay(200); 51 : : 52 : 0 : return NOTIFY_DONE; 53 : : } 54 : : 55 : : static const struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = { 56 : : .reset = mmc_pwrseq_emmc_reset, 57 : : }; 58 : : 59 : 0 : static int mmc_pwrseq_emmc_probe(struct platform_device *pdev) 60 : : { 61 : : struct mmc_pwrseq_emmc *pwrseq; 62 : 0 : struct device *dev = &pdev->dev; 63 : : 64 : : pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); 65 [ # # ]: 0 : if (!pwrseq) 66 : : return -ENOMEM; 67 : : 68 : 0 : pwrseq->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); 69 [ # # ]: 0 : if (IS_ERR(pwrseq->reset_gpio)) 70 : 0 : return PTR_ERR(pwrseq->reset_gpio); 71 : : 72 [ # # ]: 0 : 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 : 0 : pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb; 79 : 0 : pwrseq->reset_nb.priority = 255; 80 : 0 : register_restart_handler(&pwrseq->reset_nb); 81 : : } else { 82 : 0 : dev_notice(dev, "EMMC reset pin tied to a sleepy GPIO driver; reset on emergency-reboot disabled\n"); 83 : : } 84 : : 85 : 0 : pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops; 86 : 0 : pwrseq->pwrseq.dev = dev; 87 : 0 : pwrseq->pwrseq.owner = THIS_MODULE; 88 : : platform_set_drvdata(pdev, pwrseq); 89 : : 90 : 0 : return mmc_pwrseq_register(&pwrseq->pwrseq); 91 : : } 92 : : 93 : 0 : static int mmc_pwrseq_emmc_remove(struct platform_device *pdev) 94 : : { 95 : : struct mmc_pwrseq_emmc *pwrseq = platform_get_drvdata(pdev); 96 : : 97 : 0 : unregister_restart_handler(&pwrseq->reset_nb); 98 : 0 : mmc_pwrseq_unregister(&pwrseq->pwrseq); 99 : : 100 : 0 : return 0; 101 : : } 102 : : 103 : : static const struct of_device_id mmc_pwrseq_emmc_of_match[] = { 104 : : { .compatible = "mmc-pwrseq-emmc",}, 105 : : {/* sentinel */}, 106 : : }; 107 : : 108 : : MODULE_DEVICE_TABLE(of, mmc_pwrseq_emmc_of_match); 109 : : 110 : : static 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 : : 119 : 404 : module_platform_driver(mmc_pwrseq_emmc_driver); 120 : : MODULE_LICENSE("GPL v2");