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 int nr_gpios; 27 struct gpio_desc *reset_gpios[0]; 28 }; 29 30 static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq, 31 int value) 32 { 33 int i; 34 35 for (i = 0; i < pwrseq->nr_gpios; i++) 36 if (!IS_ERR(pwrseq->reset_gpios[i])) 37 gpiod_set_value_cansleep(pwrseq->reset_gpios[i], value); 38 } 39 40 static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host) 41 { 42 struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, 43 struct mmc_pwrseq_simple, pwrseq); 44 45 if (!IS_ERR(pwrseq->ext_clk) && !pwrseq->clk_enabled) { 46 clk_prepare_enable(pwrseq->ext_clk); 47 pwrseq->clk_enabled = true; 48 } 49 50 mmc_pwrseq_simple_set_gpios_value(pwrseq, 1); 51 } 52 53 static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host) 54 { 55 struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, 56 struct mmc_pwrseq_simple, pwrseq); 57 58 mmc_pwrseq_simple_set_gpios_value(pwrseq, 0); 59 } 60 61 static void mmc_pwrseq_simple_power_off(struct mmc_host *host) 62 { 63 struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, 64 struct mmc_pwrseq_simple, pwrseq); 65 66 mmc_pwrseq_simple_set_gpios_value(pwrseq, 1); 67 68 if (!IS_ERR(pwrseq->ext_clk) && pwrseq->clk_enabled) { 69 clk_disable_unprepare(pwrseq->ext_clk); 70 pwrseq->clk_enabled = false; 71 } 72 } 73 74 static void mmc_pwrseq_simple_free(struct mmc_host *host) 75 { 76 struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, 77 struct mmc_pwrseq_simple, pwrseq); 78 int i; 79 80 for (i = 0; i < pwrseq->nr_gpios; i++) 81 if (!IS_ERR(pwrseq->reset_gpios[i])) 82 gpiod_put(pwrseq->reset_gpios[i]); 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 i, nr_gpios, ret = 0; 102 103 nr_gpios = of_gpio_named_count(dev->of_node, "reset-gpios"); 104 if (nr_gpios < 0) 105 nr_gpios = 0; 106 107 pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple) + nr_gpios * 108 sizeof(struct gpio_desc *), GFP_KERNEL); 109 if (!pwrseq) 110 return ERR_PTR(-ENOMEM); 111 112 pwrseq->ext_clk = clk_get(dev, "ext_clock"); 113 if (IS_ERR(pwrseq->ext_clk) && 114 PTR_ERR(pwrseq->ext_clk) != -ENOENT) { 115 ret = PTR_ERR(pwrseq->ext_clk); 116 goto free; 117 } 118 119 for (i = 0; i < nr_gpios; i++) { 120 pwrseq->reset_gpios[i] = gpiod_get_index(dev, "reset", i, 121 GPIOD_OUT_HIGH); 122 if (IS_ERR(pwrseq->reset_gpios[i]) && 123 PTR_ERR(pwrseq->reset_gpios[i]) != -ENOENT && 124 PTR_ERR(pwrseq->reset_gpios[i]) != -ENOSYS) { 125 ret = PTR_ERR(pwrseq->reset_gpios[i]); 126 127 while (i--) 128 gpiod_put(pwrseq->reset_gpios[i]); 129 130 goto clk_put; 131 } 132 } 133 134 pwrseq->nr_gpios = nr_gpios; 135 pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops; 136 137 return &pwrseq->pwrseq; 138 clk_put: 139 if (!IS_ERR(pwrseq->ext_clk)) 140 clk_put(pwrseq->ext_clk); 141 free: 142 kfree(pwrseq); 143 return ERR_PTR(ret); 144 } 145