Commit | Line | Data |
---|---|---|
af873fce | 1 | // SPDX-License-Identifier: GPL-2.0-only |
726b6324 MS |
2 | /* |
3 | * Copyright (C) 2015, Samsung Electronics Co., Ltd. | |
4 | * | |
5 | * Author: Marek Szyprowski <m.szyprowski@samsung.com> | |
6 | * | |
726b6324 MS |
7 | * Simple eMMC hardware reset provider |
8 | */ | |
9 | #include <linux/delay.h> | |
10 | #include <linux/kernel.h> | |
d97a1e5d SK |
11 | #include <linux/init.h> |
12 | #include <linux/platform_device.h> | |
13 | #include <linux/module.h> | |
726b6324 MS |
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 | ||
f01b72d0 SK |
30 | #define to_pwrseq_emmc(p) container_of(p, struct mmc_pwrseq_emmc, pwrseq) |
31 | ||
726b6324 MS |
32 | static void mmc_pwrseq_emmc_reset(struct mmc_host *host) |
33 | { | |
f01b72d0 | 34 | struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(host->pwrseq); |
726b6324 | 35 | |
002ee28e AM |
36 | gpiod_set_value_cansleep(pwrseq->reset_gpio, 1); |
37 | udelay(1); | |
38 | gpiod_set_value_cansleep(pwrseq->reset_gpio, 0); | |
39 | udelay(200); | |
726b6324 MS |
40 | } |
41 | ||
726b6324 MS |
42 | 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); | |
002ee28e AM |
47 | gpiod_set_value(pwrseq->reset_gpio, 1); |
48 | udelay(1); | |
49 | gpiod_set_value(pwrseq->reset_gpio, 0); | |
50 | udelay(200); | |
726b6324 | 51 | |
726b6324 MS |
52 | return NOTIFY_DONE; |
53 | } | |
54 | ||
d97a1e5d | 55 | static const struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = { |
52c8212d | 56 | .reset = mmc_pwrseq_emmc_reset, |
d97a1e5d SK |
57 | }; |
58 | ||
59 | static int mmc_pwrseq_emmc_probe(struct platform_device *pdev) | |
726b6324 MS |
60 | { |
61 | struct mmc_pwrseq_emmc *pwrseq; | |
d97a1e5d | 62 | struct device *dev = &pdev->dev; |
726b6324 | 63 | |
d97a1e5d | 64 | pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); |
726b6324 | 65 | if (!pwrseq) |
d97a1e5d | 66 | return -ENOMEM; |
726b6324 | 67 | |
d97a1e5d SK |
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); | |
726b6324 | 71 | |
002ee28e AM |
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 | } | |
726b6324 MS |
84 | |
85 | pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops; | |
d97a1e5d SK |
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 | ||
93 | static 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); | |
726b6324 | 99 | |
d97a1e5d | 100 | return 0; |
726b6324 | 101 | } |
d97a1e5d SK |
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 | module_platform_driver(mmc_pwrseq_emmc_driver); | |
120 | MODULE_LICENSE("GPL v2"); |