summaryrefslogtreecommitdiffstats
path: root/drivers/platform/tegra/tegra-wakeups.c
blob: 0309ce3272613e612313b794f462e83779cced98 (plain) (blame)
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
/*
 * Tegra Wakeups for NVIDIA SoCs Tegra
 *
 * Copyright (c) 2013-2020, NVIDIA CORPORATION.  All rights reserved.
 *
 * Author: Laxman Dewangan <ldewangan@nvidia.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/irqchip/tegra.h>
#include <linux/system-wakeup.h>

int *tegra_gpio_wake_table;
EXPORT_SYMBOL_GPL(tegra_gpio_wake_table);

int *tegra_irq_wake_table;
EXPORT_SYMBOL_GPL(tegra_irq_wake_table);

int tegra_wake_table_len;
EXPORT_SYMBOL_GPL(tegra_wake_table_len);

void tegra_pm_update_gpio_wakeup_table(int base, int *gpio_wakeup_list,
				       int nlist)
{
	int i;

	for (i = 0; i < nlist; ++i) {
		if (gpio_wakeup_list[i] == -ENOENT)
			continue;

		if (gpio_wakeup_list[i] < 0) {
			tegra_gpio_wake_table[i] = gpio_wakeup_list[i];
		} else {
			tegra_gpio_wake_table[i] = base + gpio_wakeup_list[i];
			tegra_irq_wake_table[i] = -EAGAIN;
		}
	}
}

void tegra_irq_to_wake(int irq, int *wak_list, int *wak_size)
{
	int i;

	*wak_size = 0;
	for (i = 0; i < tegra_wake_table_len; i++) {
		if (tegra_irq_wake_table[i] == irq) {
			pr_info("Wake%d for irq=%d\n", i, irq);
			wak_list[*wak_size] = i;
			*wak_size = *wak_size + 1;
		}
	}
}

int tegra_wake_to_gpio(int wake)
{
	if (wake < 0 || wake >= tegra_wake_table_len)
		return -EINVAL;

	return tegra_gpio_wake_table[wake];
}

int tegra_wake_to_irq(int wake)
{
	int ret;

	if (wake < 0)
		return -EINVAL;

	if (wake >= tegra_wake_table_len)
		return -EINVAL;

	ret = tegra_irq_wake_table[wake];
	if (ret == -EAGAIN) {
		ret = tegra_gpio_wake_table[wake];
		if (ret != -EINVAL)
			ret = gpio_to_irq(ret);
	}

	return ret;
}

int get_wakeup_reason_irq(void)
{
	int irq;
	struct irq_desc *desc;
	int i, j;
	u32 wake_status[3];
	int len;

	len = tegra_read_wake_status(wake_status);

	for (i = 0; i < len; i++) {
		unsigned long temp = wake_status[i];
		for_each_set_bit(j, &temp, 32) {
			irq = tegra_wake_to_irq(j + (i * 32));
			if (!irq)
				continue;
			desc = irq_to_desc(irq);
			if (!desc || !desc->action || !desc->action->name)
				continue;
			return irq;
		}
	}

	return -EINVAL;
}
EXPORT_SYMBOL_GPL(get_wakeup_reason_irq);