| 1 | |
| 2 | |
| 3 | * Copyright (C) 2014 Linaro Ltd
|
| 4 | *
|
| 5 | * Author: Ulf Hansson <[email protected]>
|
| 6 | *
|
| 7 | * MMC power sequence management
|
| 8 |
|
| 9 | #include <linux/kernel.h>
|
| 10 | #include <linux/err.h>
|
| 11 | #include <linux/module.h>
|
| 12 | #include <linux/of.h>
|
| 13 |
|
| 14 | #include <linux/mmc/host.h>
|
| 15 |
|
| 16 | #include "pwrseq.h"
|
| 17 |
|
| 18 | static DEFINE_MUTEX(pwrseq_list_mutex);
|
| 19 | static LIST_HEAD(pwrseq_list);
|
| 20 |
|
| 21 | int mmc_pwrseq_alloc(struct mmc_host *host)
|
| 22 | {
|
| 23 | struct device_node *np;
|
| 24 | struct mmc_pwrseq *p;
|
| 25 |
|
| 26 | np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0);
|
| 27 | if (!np)
|
| 28 | return 0;
|
| 29 |
|
| 30 | mutex_lock(&pwrseq_list_mutex);
|
| 31 | list_for_each_entry(p, &pwrseq_list, pwrseq_node) {
|
| 32 | if (device_match_of_node(p->dev, np)) {
|
| 33 | if (!try_module_get(p->owner))
|
| 34 | dev_err(host->parent,
|
| 35 | "increasing module refcount failed\n");
|
| 36 | else
|
| 37 | host->pwrseq = p;
|
| 38 |
|
| 39 | break;
|
| 40 | }
|
| 41 | }
|
| 42 |
|
| 43 | of_node_put(np);
|
| 44 | mutex_unlock(&pwrseq_list_mutex);
|
| 45 |
|
| 46 | if (!host->pwrseq)
|
| 47 | return -EPROBE_DEFER;
|
| 48 |
|
| 49 | dev_info(host->parent, "allocated mmc-pwrseq\n");
|
| 50 |
|
| 51 | return 0;
|
| 52 | }
|
| 53 |
|
| 54 | void mmc_pwrseq_pre_power_on(struct mmc_host *host)
|
| 55 | {
|
| 56 | struct mmc_pwrseq *pwrseq = host->pwrseq;
|
| 57 |
|
| 58 | if (pwrseq && pwrseq->ops->pre_power_on)
|
| 59 | pwrseq->ops->pre_power_on(host);
|
| 60 | }
|
| 61 |
|
| 62 | void mmc_pwrseq_post_power_on(struct mmc_host *host)
|
| 63 | {
|
| 64 | struct mmc_pwrseq *pwrseq = host->pwrseq;
|
| 65 |
|
| 66 | if (pwrseq && pwrseq->ops->post_power_on)
|
| 67 | pwrseq->ops->post_power_on(host);
|
| 68 | }
|
| 69 |
|
| 70 | void mmc_pwrseq_power_off(struct mmc_host *host)
|
| 71 | {
|
| 72 | struct mmc_pwrseq *pwrseq = host->pwrseq;
|
| 73 |
|
| 74 | if (pwrseq && pwrseq->ops->power_off)
|
| 75 | pwrseq->ops->power_off(host);
|
| 76 | }
|
| 77 |
|
| 78 | void mmc_pwrseq_reset(struct mmc_host *host)
|
| 79 | {
|
| 80 | struct mmc_pwrseq *pwrseq = host->pwrseq;
|
| 81 |
|
| 82 | if (pwrseq && pwrseq->ops->reset)
|
| 83 | pwrseq->ops->reset(host);
|
| 84 | }
|
| 85 |
|
| 86 | void mmc_pwrseq_free(struct mmc_host *host)
|
| 87 | {
|
| 88 | struct mmc_pwrseq *pwrseq = host->pwrseq;
|
| 89 |
|
| 90 | if (pwrseq) {
|
| 91 | module_put(pwrseq->owner);
|
| 92 | host->pwrseq = NULL;
|
| 93 | }
|
| 94 | }
|
| 95 |
|
| 96 | int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq)
|
| 97 | {
|
| 98 | if (!pwrseq || !pwrseq->ops || !pwrseq->dev)
|
| 99 | return -EINVAL;
|
| 100 |
|
| 101 | mutex_lock(&pwrseq_list_mutex);
|
| 102 | list_add(&pwrseq->pwrseq_node, &pwrseq_list);
|
| 103 | mutex_unlock(&pwrseq_list_mutex);
|
| 104 |
|
| 105 | return 0;
|
| 106 | }
|
| 107 | EXPORT_SYMBOL_GPL(mmc_pwrseq_register);
|
| 108 |
|
| 109 | void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq)
|
| 110 | {
|
| 111 | if (pwrseq) {
|
| 112 | mutex_lock(&pwrseq_list_mutex);
|
| 113 | list_del(&pwrseq->pwrseq_node);
|
| 114 | mutex_unlock(&pwrseq_list_mutex);
|
| 115 | }
|
| 116 | }
|
| 117 | EXPORT_SYMBOL_GPL(mmc_pwrseq_unregister);
|