summaryrefslogtreecommitdiff
path: root/include/wait_bit.h
blob: e8acfa577666005301efba3b383fddc61c2c4c2d (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
135
136
137
138
/*
 * Wait for bit with timeout and ctrlc
 *
 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#ifndef __WAIT_BIT_H
#define __WAIT_BIT_H

#include <common.h>
#include <console.h>
#include <watchdog.h>
#include <linux/errno.h>
#include <asm/io.h>

/**
 * wait_for_bit()	waits for bit set/cleared in register
 *
 * Function polls register waiting for specific bit(s) change
 * (either 0->1 or 1->0). It can fail under two conditions:
 * - Timeout
 * - User interaction (CTRL-C)
 * Function succeeds only if all bits of masked register are set/cleared
 * (depending on set option).
 *
 * @param prefix	Prefix added to timeout messagge (message visible only
 *			with debug enabled)
 * @param reg		Register that will be read (using readl())
 * @param mask		Bit(s) of register that must be active
 * @param set		Selects wait condition (bit set or clear)
 * @param timeout_ms	Timeout (in miliseconds)
 * @param breakable	Enables CTRL-C interruption
 * @return		0 on success, -ETIMEDOUT or -EINTR on failure
 */
static inline int wait_for_bit(const char *prefix, const u32 *reg,
			       const u32 mask, const bool set,
			       const unsigned int timeout_ms,
			       const bool breakable)
{
	u32 val;
	unsigned long start = get_timer(0);

	while (1) {
		val = readl(reg);

		if (!set)
			val = ~val;

		if ((val & mask) == mask)
			return 0;

		if (get_timer(start) > timeout_ms)
			break;

		if (breakable && ctrlc()) {
			puts("Abort\n");
			return -EINTR;
		}

		udelay(1);
		WATCHDOG_RESET();
	}

	debug("%s: Timeout (reg=%p mask=%08x wait_set=%i)\n", prefix, reg, mask,
	      set);

	return -ETIMEDOUT;
}

/**
 * wait_for_bit_x()	waits for bit set/cleared in register
 *
 * Function polls register waiting for specific bit(s) change
 * (either 0->1 or 1->0). It can fail under two conditions:
 * - Timeout
 * - User interaction (CTRL-C)
 * Function succeeds only if all bits of masked register are set/cleared
 * (depending on set option).
 *
 * @param reg		Register that will be read (using read_x())
 * @param mask		Bit(s) of register that must be active
 * @param set		Selects wait condition (bit set or clear)
 * @param timeout_ms	Timeout (in milliseconds)
 * @param breakable	Enables CTRL-C interruption
 * @return		0 on success, -ETIMEDOUT or -EINTR on failure
 */

#define BUILD_WAIT_FOR_BIT(sfx, type, read)				\
									\
static inline int wait_for_bit_##sfx(const void *reg,			\
				     const type mask,			\
				     const bool set,			\
				     const unsigned int timeout_ms,	\
				     const bool breakable)		\
{									\
	type val;							\
	unsigned long start = get_timer(0);				\
									\
	while (1) {							\
		val = read(reg);					\
									\
		if (!set)						\
			val = ~val;					\
									\
		if ((val & mask) == mask)				\
			return 0;					\
									\
		if (get_timer(start) > timeout_ms)			\
			break;						\
									\
		if (breakable && ctrlc()) {				\
			puts("Abort\n");				\
			return -EINTR;					\
		}							\
									\
		udelay(1);						\
		WATCHDOG_RESET();					\
	}								\
									\
	debug("%s: Timeout (reg=%p mask=%x wait_set=%i)\n", __func__,	\
	      reg, mask, set);						\
									\
	return -ETIMEDOUT;						\
}

BUILD_WAIT_FOR_BIT(8, u8, readb)
BUILD_WAIT_FOR_BIT(le16, u16, readw)
#ifdef readw_be
BUILD_WAIT_FOR_BIT(be16, u16, readw_be)
#endif
BUILD_WAIT_FOR_BIT(le32, u32, readl)
#ifdef readl_be
BUILD_WAIT_FOR_BIT(be32, u32, readl_be)
#endif

#endif