1/*	$NetBSD: tx39power.c,v 1.20 2012/02/12 16:34:08 matt Exp $ */
2
3/*-
4 * Copyright (c) 1999, 2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by UCHIYAMA Yasushi.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: tx39power.c,v 1.20 2012/02/12 16:34:08 matt Exp $");
34
35#include "opt_tx39power_debug.h"
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/device.h>
40
41#include <machine/bus.h>
42#include <machine/intr.h>
43#include <machine/config_hook.h>
44
45#include <hpcmips/tx/tx39var.h>
46#include <hpcmips/tx/tx39icureg.h>
47#include <hpcmips/tx/tx39powerreg.h>
48
49#ifdef	TX39POWER_DEBUG
50#define DPRINTF_ENABLE
51#define DPRINTF_DEBUG	tx39power_debug
52#endif
53#include <machine/debug.h>
54
55#ifdef TX39POWER_DEBUG
56#define DUMP_REGS(x)		__tx39power_dump(x)
57#else
58#define DUMP_REGS(x)		((void)0)
59#endif
60
61#define ISSETPRINT(r, m)	dbg_bitmask_print(r, TX39_POWERCTRL_##m, #m)
62
63int	tx39power_match(device_t, cfdata_t, void *);
64void	tx39power_attach(device_t, device_t, void *);
65
66struct tx39power_softc {
67	device_t sc_dev;
68	tx_chipset_tag_t sc_tc;
69
70	/* save interrupt status for resume */
71	txreg_t sc_icu_state[TX39_INTRSET_MAX + 1];
72};
73
74CFATTACH_DECL_NEW(tx39power, sizeof(struct tx39power_softc),
75    tx39power_match, tx39power_attach, NULL, NULL);
76
77void tx39power_suspend_cpu(void); /* automatic hardware resume */
78
79static int tx39power_intr_p(void *);
80static int tx39power_intr_n(void *);
81static int tx39power_ok_intr_p(void *);
82static int tx39power_ok_intr_n(void *);
83static int tx39power_button_intr_p(void *);
84static int tx39power_button_intr_n(void *);
85#ifdef TX39POWER_DEBUG
86static void __tx39power_dump(struct tx39power_softc *);
87#endif
88
89int
90tx39power_match(device_t parent, cfdata_t cf, void *aux)
91{
92	return (ATTACH_FIRST);
93}
94
95void
96tx39power_attach(device_t parent, device_t self, void *aux)
97{
98	struct txsim_attach_args *ta = aux;
99	struct tx39power_softc *sc = device_private(self);
100	tx_chipset_tag_t tc;
101	txreg_t reg;
102
103	sc->sc_dev = self;
104	tc = sc->sc_tc = ta->ta_tc;
105	tx_conf_register_power(tc, self);
106
107	printf("\n");
108	DUMP_REGS(sc);
109
110	/* power button setting */
111	reg = tx_conf_read(tc, TX39_POWERCTRL_REG);
112	reg |= TX39_POWERCTRL_DBNCONBUTN;
113	tx_conf_write(tc, TX39_POWERCTRL_REG, reg);
114
115	/* enable stop timer */
116	reg = tx_conf_read(tc, TX39_POWERCTRL_REG);
117	reg &= ~(TX39_POWERCTRL_STPTIMERVAL_MASK <<
118	    TX39_POWERCTRL_STPTIMERVAL_SHIFT);
119	reg = TX39_POWERCTRL_STPTIMERVAL_SET(reg,
120	    TX39_POWERCTRL_STPTIMERVAL_MAX);
121	reg |= TX39_POWERCTRL_ENSTPTIMER;
122	tx_conf_write(tc, TX39_POWERCTRL_REG, reg);
123
124	/* install power event handler */
125	/* low priority */
126	tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_POSPWRINT),
127	    IST_EDGE, IPL_CLOCK,
128	    tx39power_intr_p, sc);
129	tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_NEGPWRINT),
130	    IST_EDGE, IPL_CLOCK,
131	    tx39power_intr_n, sc);
132	/* high priority */
133	tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_POSPWROKINT),
134	    IST_EDGE, IPL_CLOCK,
135	    tx39power_ok_intr_p, sc);
136	tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_NEGPWROKINT),
137	    IST_EDGE, IPL_CLOCK,
138	    tx39power_ok_intr_n, sc);
139	/* user driven event */
140	tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_POSONBUTNINT),
141	    IST_EDGE, IPL_CLOCK,
142	    tx39power_button_intr_p, sc);
143	tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_NEGONBUTNINT),
144	    IST_EDGE, IPL_CLOCK,
145	    tx39power_button_intr_n, sc);
146}
147
148void
149tx39power_suspend_cpu(void) /* I assume already splhigh */
150{
151	tx_chipset_tag_t tc = tx_conf_get_tag();
152	struct tx39power_softc *sc = tc->tc_powert;
153	txreg_t reg, *iregs = sc->sc_icu_state;
154
155	printf ("%s: CPU sleep\n", device_xname(sc->sc_dev));
156	__asm volatile(".set noreorder");
157	reg = tx_conf_read(tc, TX39_POWERCTRL_REG);
158	reg |= TX39_POWERCTRL_STOPCPU;
159	/* save interrupt state */
160	iregs[0] = tx_conf_read(tc, TX39_INTRENABLE6_REG);
161	iregs[1] = tx_conf_read(tc, TX39_INTRENABLE1_REG);
162	iregs[2] = tx_conf_read(tc, TX39_INTRENABLE2_REG);
163	iregs[3] = tx_conf_read(tc, TX39_INTRENABLE3_REG);
164	iregs[4] = tx_conf_read(tc, TX39_INTRENABLE4_REG);
165	iregs[5] = tx_conf_read(tc, TX39_INTRENABLE5_REG);
166#ifdef TX392X
167	iregs[7] = tx_conf_read(tc, TX39_INTRENABLE7_REG);
168	iregs[8] = tx_conf_read(tc, TX39_INTRENABLE8_REG);
169#endif
170	/* disable all interrupt (don't disable GLOBALEN) */
171	tx_conf_write(tc, TX39_INTRENABLE6_REG, TX39_INTRENABLE6_GLOBALEN);
172	tx_conf_write(tc, TX39_INTRENABLE1_REG, 0);
173	tx_conf_write(tc, TX39_INTRENABLE2_REG, 0);
174	tx_conf_write(tc, TX39_INTRENABLE3_REG, 0);
175	tx_conf_write(tc, TX39_INTRENABLE4_REG, 0);
176	tx_conf_write(tc, TX39_INTRENABLE5_REG, 0);
177#ifdef TX392X
178	tx_conf_write(tc, TX39_INTRENABLE7_REG, 0);
179	tx_conf_write(tc, TX39_INTRENABLE8_REG, 0);
180#endif
181	/* enable power button interrupt only */
182	tx_conf_write(tc, TX39_INTRCLEAR5_REG, TX39_INTRSTATUS5_NEGONBUTNINT);
183	tx_conf_write(tc, TX39_INTRENABLE5_REG, TX39_INTRSTATUS5_NEGONBUTNINT);
184	__asm volatile(".set push; .set mips2; sync; .set pop");
185
186	/* stop CPU clock */
187	tx_conf_write(tc, TX39_POWERCTRL_REG, reg);
188	__asm volatile(".set push; .set mips2; sync; .set pop");
189	/* wait until power button pressed */
190	/* clear interrupt */
191	tx_conf_write(tc, TX39_INTRCLEAR5_REG, TX39_INTRSTATUS5_NEGONBUTNINT);
192#ifdef TX392X
193	/* Clear WARMSTART bit to reset vector(0xbfc00000) work correctly */
194	reg = tx_conf_read(tc, TX39_POWERCTRL_REG);
195	reg &= ~TX39_POWERCTRL_WARMSTART;
196	tx_conf_write(tc, TX39_POWERCTRL_REG, reg);
197#endif
198
199	/* restore interrupt state */
200	tx_conf_write(tc, TX39_INTRENABLE6_REG, iregs[0]);
201	tx_conf_write(tc, TX39_INTRENABLE1_REG, iregs[1]);
202	tx_conf_write(tc, TX39_INTRENABLE2_REG, iregs[2]);
203	tx_conf_write(tc, TX39_INTRENABLE3_REG, iregs[3]);
204	tx_conf_write(tc, TX39_INTRENABLE4_REG, iregs[4]);
205	tx_conf_write(tc, TX39_INTRENABLE5_REG, iregs[5]);
206#ifdef TX392X
207	tx_conf_write(tc, TX39_INTRENABLE7_REG, iregs[7]);
208	tx_conf_write(tc, TX39_INTRENABLE8_REG, iregs[8]);
209#endif
210	__asm volatile(".set reorder");
211
212	printf ("%s: CPU wakeup\n", device_xname(sc->sc_dev));
213}
214
215static int
216tx39power_button_intr_p(void *arg)
217{
218	config_hook_call(CONFIG_HOOK_BUTTONEVENT,
219	    CONFIG_HOOK_BUTTONEVENT_POWER,
220	    (void *)1 /* on */);
221
222	return (0);
223}
224
225static int
226tx39power_button_intr_n(void *arg)
227{
228	config_hook_call(CONFIG_HOOK_BUTTONEVENT,
229	    CONFIG_HOOK_BUTTONEVENT_POWER,
230	    (void *)0 /* off */);
231	DUMP_REGS(arg);
232
233	return (0);
234}
235
236int
237tx39power_intr_p(void *arg)
238{
239	/* low priority event */
240	printf("power_p\n");
241	DUMP_REGS(arg);
242
243	return (0);
244}
245
246static int
247tx39power_intr_n(void *arg)
248{
249	/* low priority event */
250	printf("power_n\n");
251	DUMP_REGS(arg);
252
253	return (0);
254}
255
256static int
257tx39power_ok_intr_p(void *arg)
258{
259	/* high priority event */
260	printf("power NG\n");
261	DUMP_REGS(arg);
262	config_hook_call(CONFIG_HOOK_PMEVENT,
263	    CONFIG_HOOK_PMEVENT_SUSPENDREQ, NULL);
264
265	return (0);
266}
267
268static int
269tx39power_ok_intr_n(void *arg)
270{
271	/* high priority event */
272	printf("power OK\n");
273	DUMP_REGS(arg);
274
275	return (0);
276}
277
278#ifdef TX39POWER_DEBUG
279static void
280__tx39power_dump (struct tx39power_softc *sc)
281{
282	tx_chipset_tag_t tc = sc->sc_tc;
283	txreg_t reg;
284
285	reg = tx_conf_read(tc, TX39_POWERCTRL_REG);
286	ISSETPRINT(reg, ONBUTN);
287	ISSETPRINT(reg, PWRINT);
288	ISSETPRINT(reg, PWROK);
289#ifdef TX392X
290	ISSETPRINT(reg, PWROKNMI);
291#endif /* TX392X */
292	ISSETPRINT(reg, SLOWBUS);
293#ifdef TX391X
294	ISSETPRINT(reg, DIVMOD);
295#endif /* TX391X */
296	ISSETPRINT(reg, ENSTPTIMER);
297	ISSETPRINT(reg, ENFORCESHUTDWN);
298	ISSETPRINT(reg, FORCESHUTDWN);
299	ISSETPRINT(reg, FORCESHUTDWNOCC);
300	ISSETPRINT(reg, SELC2MS);
301#ifdef TX392X
302	ISSETPRINT(reg, WARMSTART);
303#endif /* TX392X */
304	ISSETPRINT(reg, BPDBVCC3);
305	ISSETPRINT(reg, STOPCPU);
306	ISSETPRINT(reg, DBNCONBUTN);
307	ISSETPRINT(reg, COLDSTART);
308	ISSETPRINT(reg, PWRCS);
309	ISSETPRINT(reg, VCCON);
310#ifdef TX391X
311	printf("VIDRF=%d ", TX39_POWERCTRL_VIDRF(reg));
312#endif /* TX391X */
313	printf("STPTIMERVAL=%d ", TX39_POWERCTRL_STPTIMERVAL(reg));
314	printf("\n");
315}
316#endif /* TX39POWER_DEBUG */
317