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