summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/prm2xxx.c
blob: af0f15278fc2afc2d7480da370cba6cafac9d2f6 (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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
/*
 * OMAP2xxx PRM module functions
 *
 * Copyright (C) 2010-2012 Texas Instruments, Inc.
 * Copyright (C) 2010 Nokia Corporation
 * Benoît Cousson
 * Paul Walmsley
 * Rajendra Nayak <rnayak@ti.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/irq.h>

#include "powerdomain.h"
#include "clockdomain.h"
#include "prm2xxx.h"
#include "cm2xxx_3xxx.h"
#include "prm-regbits-24xx.h"

/*
 * OMAP24xx PM_PWSTCTRL_*.POWERSTATE and PM_PWSTST_*.LASTSTATEENTERED bits -
 * these are reversed from the bits used on OMAP3+
 */
#define OMAP24XX_PWRDM_POWER_ON			0x0
#define OMAP24XX_PWRDM_POWER_RET		0x1
#define OMAP24XX_PWRDM_POWER_OFF		0x3

/*
 * omap2xxx_prm_reset_src_map - map from bits in the PRM_RSTST_WKUP
 *   hardware register (which are specific to the OMAP2xxx SoCs) to
 *   reset source ID bit shifts (which is an OMAP SoC-independent
 *   enumeration)
 */
static struct prm_reset_src_map omap2xxx_prm_reset_src_map[] = {
	{ OMAP_GLOBALCOLD_RST_SHIFT, OMAP_GLOBAL_COLD_RST_SRC_ID_SHIFT },
	{ OMAP_GLOBALWARM_RST_SHIFT, OMAP_GLOBAL_WARM_RST_SRC_ID_SHIFT },
	{ OMAP24XX_SECU_VIOL_RST_SHIFT, OMAP_SECU_VIOL_RST_SRC_ID_SHIFT },
	{ OMAP24XX_MPU_WD_RST_SHIFT, OMAP_MPU_WD_RST_SRC_ID_SHIFT },
	{ OMAP24XX_SECU_WD_RST_SHIFT, OMAP_SECU_WD_RST_SRC_ID_SHIFT },
	{ OMAP24XX_EXTWMPU_RST_SHIFT, OMAP_EXTWARM_RST_SRC_ID_SHIFT },
	{ -1, -1 },
};

/**
 * omap2xxx_prm_read_reset_sources - return the last SoC reset source
 *
 * Return a u32 representing the last reset sources of the SoC.  The
 * returned reset source bits are standardized across OMAP SoCs.
 */
static u32 omap2xxx_prm_read_reset_sources(void)
{
	struct prm_reset_src_map *p;
	u32 r = 0;
	u32 v;

	v = omap2_prm_read_mod_reg(WKUP_MOD, OMAP2_RM_RSTST);

	p = omap2xxx_prm_reset_src_map;
	while (p->reg_shift >= 0 && p->std_shift >= 0) {
		if (v & (1 << p->reg_shift))
			r |= 1 << p->std_shift;
		p++;
	}

	return r;
}

/**
 * omap2xxx_pwrst_to_common_pwrst - convert OMAP2xxx pwrst to common pwrst
 * @omap2xxx_pwrst: OMAP2xxx hardware power state to convert
 *
 * Return the common power state bits corresponding to the OMAP2xxx
 * hardware power state bits @omap2xxx_pwrst, or -EINVAL upon error.
 */
static int omap2xxx_pwrst_to_common_pwrst(u8 omap2xxx_pwrst)
{
	u8 pwrst;

	switch (omap2xxx_pwrst) {
	case OMAP24XX_PWRDM_POWER_OFF:
		pwrst = PWRDM_POWER_OFF;
		break;
	case OMAP24XX_PWRDM_POWER_RET:
		pwrst = PWRDM_POWER_RET;
		break;
	case OMAP24XX_PWRDM_POWER_ON:
		pwrst = PWRDM_POWER_ON;
		break;
	default:
		return -EINVAL;
	}

	return pwrst;
}

/**
 * omap2xxx_prm_dpll_reset - use DPLL reset to reboot the OMAP SoC
 *
 * Set the DPLL reset bit, which should reboot the SoC.  This is the
 * recommended way to restart the SoC.  No return value.
 */
static void omap2xxx_prm_dpll_reset(void)
{
	omap2_prm_set_mod_reg_bits(OMAP_RST_DPLL3_MASK, WKUP_MOD,
				   OMAP2_RM_RSTCTRL);
	/* OCP barrier */
	omap2_prm_read_mod_reg(WKUP_MOD, OMAP2_RM_RSTCTRL);
}

/**
 * omap2xxx_prm_clear_mod_irqs - clear wakeup status bits for a module
 * @module: PRM module to clear wakeups from
 * @regs: register offset to clear
 * @wkst_mask: wakeup status mask to clear
 *
 * Clears wakeup status bits for a given module, so that the device can
 * re-enter idle.
 */
void omap2xxx_prm_clear_mod_irqs(s16 module, u8 regs, u32 wkst_mask)
{
	u32 wkst;

	wkst = omap2_prm_read_mod_reg(module, regs);
	wkst &= wkst_mask;
	omap2_prm_write_mod_reg(wkst, module, regs);
}

int omap2xxx_clkdm_sleep(struct clockdomain *clkdm)
{
	omap2_prm_set_mod_reg_bits(OMAP24XX_FORCESTATE_MASK,
				   clkdm->pwrdm.ptr->prcm_offs,
				   OMAP2_PM_PWSTCTRL);
	return 0;
}

int omap2xxx_clkdm_wakeup(struct clockdomain *clkdm)
{
	omap2_prm_clear_mod_reg_bits(OMAP24XX_FORCESTATE_MASK,
				     clkdm->pwrdm.ptr->prcm_offs,
				     OMAP2_PM_PWSTCTRL);
	return 0;
}

static int omap2xxx_pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst)
{
	u8 omap24xx_pwrst;

	switch (pwrst) {
	case PWRDM_POWER_OFF:
		omap24xx_pwrst = OMAP24XX_PWRDM_POWER_OFF;
		break;
	case PWRDM_POWER_RET:
		omap24xx_pwrst = OMAP24XX_PWRDM_POWER_RET;
		break;
	case PWRDM_POWER_ON:
		omap24xx_pwrst = OMAP24XX_PWRDM_POWER_ON;
		break;
	default:
		return -EINVAL;
	}

	omap2_prm_rmw_mod_reg_bits(OMAP_POWERSTATE_MASK,
				   (omap24xx_pwrst << OMAP_POWERSTATE_SHIFT),
				   pwrdm->prcm_offs, OMAP2_PM_PWSTCTRL);
	return 0;
}

static int omap2xxx_pwrdm_read_next_pwrst(struct powerdomain *pwrdm)
{
	u8 omap2xxx_pwrst;

	omap2xxx_pwrst = omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs,
						       OMAP2_PM_PWSTCTRL,
						       OMAP_POWERSTATE_MASK);

	return omap2xxx_pwrst_to_common_pwrst(omap2xxx_pwrst);
}

static int omap2xxx_pwrdm_read_pwrst(struct powerdomain *pwrdm)
{
	u8 omap2xxx_pwrst;

	omap2xxx_pwrst = omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs,
						       OMAP2_PM_PWSTST,
						       OMAP_POWERSTATEST_MASK);

	return omap2xxx_pwrst_to_common_pwrst(omap2xxx_pwrst);
}

struct pwrdm_ops omap2_pwrdm_operations = {
	.pwrdm_set_next_pwrst	= omap2xxx_pwrdm_set_next_pwrst,
	.pwrdm_read_next_pwrst	= omap2xxx_pwrdm_read_next_pwrst,
	.pwrdm_read_pwrst	= omap2xxx_pwrdm_read_pwrst,
	.pwrdm_set_logic_retst	= omap2_pwrdm_set_logic_retst,
	.pwrdm_set_mem_onst	= omap2_pwrdm_set_mem_onst,
	.pwrdm_set_mem_retst	= omap2_pwrdm_set_mem_retst,
	.pwrdm_read_mem_pwrst	= omap2_pwrdm_read_mem_pwrst,
	.pwrdm_read_mem_retst	= omap2_pwrdm_read_mem_retst,
	.pwrdm_wait_transition	= omap2_pwrdm_wait_transition,
};

/*
 *
 */

static struct prm_ll_data omap2xxx_prm_ll_data = {
	.read_reset_sources = &omap2xxx_prm_read_reset_sources,
	.assert_hardreset = &omap2_prm_assert_hardreset,
	.deassert_hardreset = &omap2_prm_deassert_hardreset,
	.is_hardreset_asserted = &omap2_prm_is_hardreset_asserted,
	.reset_system = &omap2xxx_prm_dpll_reset,
};

int __init omap2xxx_prm_init(void)
{
	return prm_register(&omap2xxx_prm_ll_data);
}

static void __exit omap2xxx_prm_exit(void)
{
	prm_unregister(&omap2xxx_prm_ll_data);
}
__exitcall(omap2xxx_prm_exit);