Commit | Line | Data |
---|---|---|
726b6324 MS |
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/slab.h> | |
13 | #include <linux/device.h> | |
14 | #include <linux/err.h> | |
15 | #include <linux/gpio/consumer.h> | |
16 | #include <linux/reboot.h> | |
17 | ||
18 | #include <linux/mmc/host.h> | |
19 | ||
20 | #include "pwrseq.h" | |
21 | ||
22 | struct mmc_pwrseq_emmc { | |
23 | struct mmc_pwrseq pwrseq; | |
24 | struct notifier_block reset_nb; | |
25 | struct gpio_desc *reset_gpio; | |
26 | }; | |
27 | ||
28 | static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq) | |
29 | { | |
30 | gpiod_set_value(pwrseq->reset_gpio, 1); | |
31 | udelay(1); | |
32 | gpiod_set_value(pwrseq->reset_gpio, 0); | |
33 | udelay(200); | |
34 | } | |
35 | ||
36 | static void mmc_pwrseq_emmc_reset(struct mmc_host *host) | |
37 | { | |
38 | struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq, | |
39 | struct mmc_pwrseq_emmc, pwrseq); | |
40 | ||
41 | __mmc_pwrseq_emmc_reset(pwrseq); | |
42 | } | |
43 | ||
44 | static void mmc_pwrseq_emmc_free(struct mmc_host *host) | |
45 | { | |
46 | struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq, | |
47 | struct mmc_pwrseq_emmc, pwrseq); | |
48 | ||
49 | unregister_restart_handler(&pwrseq->reset_nb); | |
50 | gpiod_put(pwrseq->reset_gpio); | |
51 | kfree(pwrseq); | |
726b6324 MS |
52 | } |
53 | ||
54 | static struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = { | |
55 | .post_power_on = mmc_pwrseq_emmc_reset, | |
56 | .free = mmc_pwrseq_emmc_free, | |
57 | }; | |
58 | ||
59 | static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this, | |
60 | unsigned long mode, void *cmd) | |
61 | { | |
62 | struct mmc_pwrseq_emmc *pwrseq = container_of(this, | |
63 | struct mmc_pwrseq_emmc, reset_nb); | |
64 | ||
65 | __mmc_pwrseq_emmc_reset(pwrseq); | |
66 | return NOTIFY_DONE; | |
67 | } | |
68 | ||
0f12a0ce AC |
69 | struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host, |
70 | struct device *dev) | |
726b6324 MS |
71 | { |
72 | struct mmc_pwrseq_emmc *pwrseq; | |
73 | int ret = 0; | |
74 | ||
75 | pwrseq = kzalloc(sizeof(struct mmc_pwrseq_emmc), GFP_KERNEL); | |
76 | if (!pwrseq) | |
0f12a0ce | 77 | return ERR_PTR(-ENOMEM); |
726b6324 MS |
78 | |
79 | pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_LOW); | |
80 | if (IS_ERR(pwrseq->reset_gpio)) { | |
81 | ret = PTR_ERR(pwrseq->reset_gpio); | |
82 | goto free; | |
83 | } | |
84 | ||
85 | /* | |
86 | * register reset handler to ensure emmc reset also from | |
87 | * emergency_reboot(), priority 129 schedules it just before | |
88 | * system reboot | |
89 | */ | |
90 | pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb; | |
91 | pwrseq->reset_nb.priority = 129; | |
92 | register_restart_handler(&pwrseq->reset_nb); | |
93 | ||
94 | pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops; | |
726b6324 | 95 | |
0f12a0ce | 96 | return &pwrseq->pwrseq; |
726b6324 MS |
97 | free: |
98 | kfree(pwrseq); | |
0f12a0ce | 99 | return ERR_PTR(ret); |
726b6324 | 100 | } |