summaryrefslogtreecommitdiff
path: root/drivers/mmc/core/pwrseq_sd8787.c
blob: f24bbd68e25130bfb61de4da93dbe3873ee33cce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * pwrseq_sd8787.c - power sequence support for Marvell SD8787 BT + Wifi chip
 *
 * Copyright (C) 2016 Matt Ranostay <matt@ranostay.consulting>
 *
 * Based on the original work pwrseq_simple.c
 *  Copyright (C) 2014 Linaro Ltd
 *  Author: Ulf Hansson <ulf.hansson@linaro.org>
 */

#include <linux/delay.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>

#include <linux/mmc/host.h>

#include "pwrseq.h"

struct mmc_pwrseq_sd8787 {
	struct mmc_pwrseq pwrseq;
	struct gpio_desc *reset_gpio;
	struct gpio_desc *pwrdn_gpio;
};

#define to_pwrseq_sd8787(p) container_of(p, struct mmc_pwrseq_sd8787, pwrseq)

static void mmc_pwrseq_sd8787_pre_power_on(struct mmc_host *host)
{
	struct mmc_pwrseq_sd8787 *pwrseq = to_pwrseq_sd8787(host->pwrseq);

	gpiod_set_value_cansleep(pwrseq->reset_gpio, 1);

	msleep(300);
	gpiod_set_value_cansleep(pwrseq->pwrdn_gpio, 1);
}

static void mmc_pwrseq_sd8787_power_off(struct mmc_host *host)
{
	struct mmc_pwrseq_sd8787 *pwrseq = to_pwrseq_sd8787(host->pwrseq);

	gpiod_set_value_cansleep(pwrseq->pwrdn_gpio, 0);
	gpiod_set_value_cansleep(pwrseq->reset_gpio, 0);
}

static void mmc_pwrseq_wilc1000_pre_power_on(struct mmc_host *host)
{
	struct mmc_pwrseq_sd8787 *pwrseq = to_pwrseq_sd8787(host->pwrseq);

	/* The pwrdn_gpio is really CHIP_EN, reset_gpio is RESETN */
	gpiod_set_value_cansleep(pwrseq->pwrdn_gpio, 1);
	msleep(5);
	gpiod_set_value_cansleep(pwrseq->reset_gpio, 1);
}

static void mmc_pwrseq_wilc1000_power_off(struct mmc_host *host)
{
	struct mmc_pwrseq_sd8787 *pwrseq = to_pwrseq_sd8787(host->pwrseq);

	gpiod_set_value_cansleep(pwrseq->reset_gpio, 0);
	gpiod_set_value_cansleep(pwrseq->pwrdn_gpio, 0);
}

static const struct mmc_pwrseq_ops mmc_pwrseq_sd8787_ops = {
	.pre_power_on = mmc_pwrseq_sd8787_pre_power_on,
	.power_off = mmc_pwrseq_sd8787_power_off,
};

static const struct mmc_pwrseq_ops mmc_pwrseq_wilc1000_ops = {
	.pre_power_on = mmc_pwrseq_wilc1000_pre_power_on,
	.power_off = mmc_pwrseq_wilc1000_power_off,
};

static const struct of_device_id mmc_pwrseq_sd8787_of_match[] = {
	{ .compatible = "mmc-pwrseq-sd8787", .data = &mmc_pwrseq_sd8787_ops },
	{ .compatible = "mmc-pwrseq-wilc1000", .data = &mmc_pwrseq_wilc1000_ops },
	{/* sentinel */},
};
MODULE_DEVICE_TABLE(of, mmc_pwrseq_sd8787_of_match);

static int mmc_pwrseq_sd8787_probe(struct platform_device *pdev)
{
	struct mmc_pwrseq_sd8787 *pwrseq;
	struct device *dev = &pdev->dev;
	const struct of_device_id *match;

	pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL);
	if (!pwrseq)
		return -ENOMEM;

	match = of_match_node(mmc_pwrseq_sd8787_of_match, pdev->dev.of_node);

	pwrseq->pwrdn_gpio = devm_gpiod_get(dev, "powerdown", GPIOD_OUT_LOW);
	if (IS_ERR(pwrseq->pwrdn_gpio))
		return PTR_ERR(pwrseq->pwrdn_gpio);

	pwrseq->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
	if (IS_ERR(pwrseq->reset_gpio))
		return PTR_ERR(pwrseq->reset_gpio);

	pwrseq->pwrseq.dev = dev;
	pwrseq->pwrseq.ops = match->data;
	pwrseq->pwrseq.owner = THIS_MODULE;
	platform_set_drvdata(pdev, pwrseq);

	return mmc_pwrseq_register(&pwrseq->pwrseq);
}

static void mmc_pwrseq_sd8787_remove(struct platform_device *pdev)
{
	struct mmc_pwrseq_sd8787 *pwrseq = platform_get_drvdata(pdev);

	mmc_pwrseq_unregister(&pwrseq->pwrseq);
}

static struct platform_driver mmc_pwrseq_sd8787_driver = {
	.probe = mmc_pwrseq_sd8787_probe,
	.remove_new = mmc_pwrseq_sd8787_remove,
	.driver = {
		.name = "pwrseq_sd8787",
		.of_match_table = mmc_pwrseq_sd8787_of_match,
	},
};

module_platform_driver(mmc_pwrseq_sd8787_driver);
MODULE_DESCRIPTION("Power sequence support for Marvell SD8787 BT + Wifi chip");
MODULE_LICENSE("GPL v2");