]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/mmc/core/pwrseq_simple.c
Merge remote-tracking branch 'dt-rh/for-next'
[karo-tx-linux.git] / drivers / mmc / core / pwrseq_simple.c
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  */
10 #include <linux/clk.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
17 #include <linux/mmc/host.h>
18
19 #include "pwrseq.h"
20
21 struct mmc_pwrseq_simple {
22         struct mmc_pwrseq pwrseq;
23         bool clk_enabled;
24         struct clk *ext_clk;
25         struct gpio_descs *reset_gpios;
26 };
27
28 static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
29                                               int value)
30 {
31         struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
32
33         if (!IS_ERR(reset_gpios)) {
34                 int i;
35                 int values[reset_gpios->ndescs];
36
37                 for (i = 0; i < reset_gpios->ndescs; i++)
38                         values[i] = value;
39
40                 gpiod_set_array_value_cansleep(
41                         reset_gpios->ndescs, reset_gpios->desc, values);
42         }
43 }
44
45 static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host)
46 {
47         struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
48                                         struct mmc_pwrseq_simple, pwrseq);
49
50         if (!IS_ERR(pwrseq->ext_clk) && !pwrseq->clk_enabled) {
51                 clk_prepare_enable(pwrseq->ext_clk);
52                 pwrseq->clk_enabled = true;
53         }
54
55         mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
56 }
57
58 static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host)
59 {
60         struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
61                                         struct mmc_pwrseq_simple, pwrseq);
62
63         mmc_pwrseq_simple_set_gpios_value(pwrseq, 0);
64 }
65
66 static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
67 {
68         struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
69                                         struct mmc_pwrseq_simple, pwrseq);
70
71         mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
72
73         if (!IS_ERR(pwrseq->ext_clk) && pwrseq->clk_enabled) {
74                 clk_disable_unprepare(pwrseq->ext_clk);
75                 pwrseq->clk_enabled = false;
76         }
77 }
78
79 static void mmc_pwrseq_simple_free(struct mmc_host *host)
80 {
81         struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
82                                         struct mmc_pwrseq_simple, pwrseq);
83
84         if (!IS_ERR(pwrseq->reset_gpios))
85                 gpiod_put_array(pwrseq->reset_gpios);
86
87         if (!IS_ERR(pwrseq->ext_clk))
88                 clk_put(pwrseq->ext_clk);
89
90         kfree(pwrseq);
91 }
92
93 static const struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
94         .pre_power_on = mmc_pwrseq_simple_pre_power_on,
95         .post_power_on = mmc_pwrseq_simple_post_power_on,
96         .power_off = mmc_pwrseq_simple_power_off,
97         .free = mmc_pwrseq_simple_free,
98 };
99
100 struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host,
101                                            struct device *dev)
102 {
103         struct mmc_pwrseq_simple *pwrseq;
104         int ret = 0;
105
106         pwrseq = kzalloc(sizeof(*pwrseq), GFP_KERNEL);
107         if (!pwrseq)
108                 return ERR_PTR(-ENOMEM);
109
110         pwrseq->ext_clk = clk_get(dev, "ext_clock");
111         if (IS_ERR(pwrseq->ext_clk) &&
112             PTR_ERR(pwrseq->ext_clk) != -ENOENT) {
113                 ret = PTR_ERR(pwrseq->ext_clk);
114                 goto free;
115         }
116
117         pwrseq->reset_gpios = gpiod_get_array(dev, "reset", GPIOD_OUT_HIGH);
118         if (IS_ERR(pwrseq->reset_gpios) &&
119             PTR_ERR(pwrseq->reset_gpios) != -ENOENT &&
120             PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) {
121                 ret = PTR_ERR(pwrseq->reset_gpios);
122                 goto clk_put;
123         }
124
125         pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops;
126
127         return &pwrseq->pwrseq;
128 clk_put:
129         if (!IS_ERR(pwrseq->ext_clk))
130                 clk_put(pwrseq->ext_clk);
131 free:
132         kfree(pwrseq);
133         return ERR_PTR(ret);
134 }