Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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