]>
Commit | Line | Data |
---|---|---|
8c96f89c UH |
1 | /* |
2 | * Copyright (C) 2014 Linaro Ltd | |
3 | * | |
4 | * Author: Ulf Hansson <ulf.hansson@linaro.org> | |
5 | * | |
6 | * License terms: GNU General Public License (GPL) version 2 | |
7 | * | |
8 | * Simple MMC power sequence management | |
9 | */ | |
c13045b1 | 10 | #include <linux/clk.h> |
d97a1e5d | 11 | #include <linux/init.h> |
8c96f89c | 12 | #include <linux/kernel.h> |
d97a1e5d SK |
13 | #include <linux/platform_device.h> |
14 | #include <linux/module.h> | |
8c96f89c UH |
15 | #include <linux/slab.h> |
16 | #include <linux/device.h> | |
17 | #include <linux/err.h> | |
862b5dcf | 18 | #include <linux/gpio/consumer.h> |
8c96f89c UH |
19 | |
20 | #include <linux/mmc/host.h> | |
21 | ||
22 | #include "pwrseq.h" | |
23 | ||
24 | struct mmc_pwrseq_simple { | |
25 | struct mmc_pwrseq pwrseq; | |
c13045b1 JMC |
26 | bool clk_enabled; |
27 | struct clk *ext_clk; | |
ce037275 | 28 | struct gpio_descs *reset_gpios; |
8c96f89c UH |
29 | }; |
30 | ||
5b96fea7 SK |
31 | #define to_pwrseq_simple(p) container_of(p, struct mmc_pwrseq_simple, pwrseq) |
32 | ||
934f1f48 JMC |
33 | static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq, |
34 | int value) | |
35 | { | |
ce037275 | 36 | struct gpio_descs *reset_gpios = pwrseq->reset_gpios; |
934f1f48 | 37 | |
64a67d47 MF |
38 | if (!IS_ERR(reset_gpios)) { |
39 | int i; | |
40 | int values[reset_gpios->ndescs]; | |
ce037275 | 41 | |
64a67d47 MF |
42 | for (i = 0; i < reset_gpios->ndescs; i++) |
43 | values[i] = value; | |
44 | ||
45 | gpiod_set_array_value_cansleep( | |
46 | reset_gpios->ndescs, reset_gpios->desc, values); | |
47 | } | |
934f1f48 JMC |
48 | } |
49 | ||
862b5dcf UH |
50 | static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host) |
51 | { | |
5b96fea7 | 52 | struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); |
862b5dcf | 53 | |
c13045b1 JMC |
54 | if (!IS_ERR(pwrseq->ext_clk) && !pwrseq->clk_enabled) { |
55 | clk_prepare_enable(pwrseq->ext_clk); | |
56 | pwrseq->clk_enabled = true; | |
57 | } | |
58 | ||
934f1f48 | 59 | mmc_pwrseq_simple_set_gpios_value(pwrseq, 1); |
862b5dcf UH |
60 | } |
61 | ||
62 | static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host) | |
63 | { | |
5b96fea7 | 64 | struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); |
862b5dcf | 65 | |
934f1f48 | 66 | mmc_pwrseq_simple_set_gpios_value(pwrseq, 0); |
862b5dcf UH |
67 | } |
68 | ||
c13045b1 JMC |
69 | static void mmc_pwrseq_simple_power_off(struct mmc_host *host) |
70 | { | |
5b96fea7 | 71 | struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); |
c13045b1 JMC |
72 | |
73 | mmc_pwrseq_simple_set_gpios_value(pwrseq, 1); | |
74 | ||
75 | if (!IS_ERR(pwrseq->ext_clk) && pwrseq->clk_enabled) { | |
76 | clk_disable_unprepare(pwrseq->ext_clk); | |
77 | pwrseq->clk_enabled = false; | |
78 | } | |
79 | } | |
80 | ||
ffedbd22 | 81 | static const struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = { |
862b5dcf UH |
82 | .pre_power_on = mmc_pwrseq_simple_pre_power_on, |
83 | .post_power_on = mmc_pwrseq_simple_post_power_on, | |
c13045b1 | 84 | .power_off = mmc_pwrseq_simple_power_off, |
8c96f89c UH |
85 | }; |
86 | ||
d97a1e5d SK |
87 | static const struct of_device_id mmc_pwrseq_simple_of_match[] = { |
88 | { .compatible = "mmc-pwrseq-simple",}, | |
89 | {/* sentinel */}, | |
90 | }; | |
91 | MODULE_DEVICE_TABLE(of, mmc_pwrseq_simple_of_match); | |
92 | ||
93 | static int mmc_pwrseq_simple_probe(struct platform_device *pdev) | |
8c96f89c UH |
94 | { |
95 | struct mmc_pwrseq_simple *pwrseq; | |
d97a1e5d | 96 | struct device *dev = &pdev->dev; |
934f1f48 | 97 | |
d97a1e5d | 98 | pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); |
8c96f89c | 99 | if (!pwrseq) |
d97a1e5d | 100 | return -ENOMEM; |
8c96f89c | 101 | |
d97a1e5d SK |
102 | pwrseq->ext_clk = devm_clk_get(dev, "ext_clock"); |
103 | if (IS_ERR(pwrseq->ext_clk) && PTR_ERR(pwrseq->ext_clk) != -ENOENT) | |
104 | return PTR_ERR(pwrseq->ext_clk); | |
c13045b1 | 105 | |
d97a1e5d SK |
106 | pwrseq->reset_gpios = devm_gpiod_get_array(dev, "reset", |
107 | GPIOD_OUT_HIGH); | |
64a67d47 MF |
108 | if (IS_ERR(pwrseq->reset_gpios) && |
109 | PTR_ERR(pwrseq->reset_gpios) != -ENOENT && | |
110 | PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) { | |
d97a1e5d | 111 | return PTR_ERR(pwrseq->reset_gpios); |
862b5dcf UH |
112 | } |
113 | ||
d97a1e5d | 114 | pwrseq->pwrseq.dev = dev; |
8c96f89c | 115 | pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops; |
d97a1e5d SK |
116 | pwrseq->pwrseq.owner = THIS_MODULE; |
117 | platform_set_drvdata(pdev, pwrseq); | |
8c96f89c | 118 | |
d97a1e5d | 119 | return mmc_pwrseq_register(&pwrseq->pwrseq); |
8c96f89c | 120 | } |
d97a1e5d SK |
121 | |
122 | static int mmc_pwrseq_simple_remove(struct platform_device *pdev) | |
123 | { | |
124 | struct mmc_pwrseq_simple *pwrseq = platform_get_drvdata(pdev); | |
125 | ||
126 | mmc_pwrseq_unregister(&pwrseq->pwrseq); | |
127 | ||
128 | return 0; | |
129 | } | |
130 | ||
131 | static struct platform_driver mmc_pwrseq_simple_driver = { | |
132 | .probe = mmc_pwrseq_simple_probe, | |
133 | .remove = mmc_pwrseq_simple_remove, | |
134 | .driver = { | |
135 | .name = "pwrseq_simple", | |
136 | .of_match_table = mmc_pwrseq_simple_of_match, | |
137 | }, | |
138 | }; | |
139 | ||
140 | module_platform_driver(mmc_pwrseq_simple_driver); | |
141 | MODULE_LICENSE("GPL v2"); |