linux/drivers/mmc/core/pwrseq_simple.c
<<
>>
Prefs
   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
  22struct 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
  30static 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
  40static 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
  53static 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
  61static 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
  74static 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        host->pwrseq = NULL;
  89}
  90
  91static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
  92        .pre_power_on = mmc_pwrseq_simple_pre_power_on,
  93        .post_power_on = mmc_pwrseq_simple_post_power_on,
  94        .power_off = mmc_pwrseq_simple_power_off,
  95        .free = mmc_pwrseq_simple_free,
  96};
  97
  98int mmc_pwrseq_simple_alloc(struct mmc_host *host, 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 -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        host->pwrseq = &pwrseq->pwrseq;
 137
 138        return 0;
 139clk_put:
 140        if (!IS_ERR(pwrseq->ext_clk))
 141                clk_put(pwrseq->ext_clk);
 142free:
 143        kfree(pwrseq);
 144        return ret;
 145}
 146