1/*	$NetBSD: tx39power.c,v 1.19 2008/04/28 20:23:21 martin 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.19 2008/04/28 20:23:21 martin 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(struct device *, struct cfdata *, void *);
64void	tx39power_attach(struct device *, struct device *, void *);
65
66struct tx39power_softc {
67	struct	device 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(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(struct device *parent, struct cfdata *cf, void *aux)
91{
92	return (ATTACH_FIRST);
93}
94
95void
96tx39power_attach(struct device *parent, struct device *self, void *aux)
97{
98	struct txsim_attach_args *ta = aux;
99	struct tx39power_softc *sc = (void*)self;
100	tx_chipset_tag_t tc;
101	txreg_t reg;
102
103	tc = sc->sc_tc = ta->ta_tc;
104	tx_conf_register_power(tc, self);
105
106	printf("\n");
107	DUMP_REGS(sc);
108
109	/* power button setting */
110	reg = tx_conf_read(tc, TX39_POWERCTRL_REG);
111	reg |= TX39_POWERCTRL_DBNCONBUTN;
112	tx_conf_write(tc, TX39_POWERCTRL_REG, reg);
113
114	/* enable stop timer */
115	reg = tx_conf_read(tc, TX39_POWERCTRL_REG);
116	reg &= ~(TX39_POWERCTRL_STPTIMERVAL_MASK <<
117	    TX39_POWERCTRL_STPTIMERVAL_SHIFT);
118	reg = TX39_POWERCTRL_STPTIMERVAL_SET(reg,
119	    TX39_POWERCTRL_STPTIMERVAL_MAX);
120	reg |= TX39_POWERCTRL_ENSTPTIMER;
121	tx_conf_write(tc, TX39_POWERCTRL_REG, reg);
122
123	/* install power event handler */
124	/* low priority */
125	tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_POSPWRINT),
126	    IST_EDGE, IPL_CLOCK,
127	    tx39power_intr_p, sc);
128	tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_NEGPWRINT),
129	    IST_EDGE, IPL_CLOCK,
130	    tx39power_intr_n, sc);
131	/* high priority */
132	tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_POSPWROKINT),
133	    IST_EDGE, IPL_CLOCK,
134	    tx39power_ok_intr_p, sc);
135	tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_NEGPWROKINT),
136	    IST_EDGE, IPL_CLOCK,
137	    tx39power_ok_intr_n, sc);
138	/* user driven event */
139	tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_POSONBUTNINT),
140	    IST_EDGE, IPL_CLOCK,
141	    tx39power_button_intr_p, sc);
142	tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_NEGONBUTNINT),
143	    IST_EDGE, IPL_CLOCK,
144	    tx39power_button_intr_n, sc);
145}
146
147void
148tx39power_suspend_cpu(void) /* I assume already splhigh */
149{
150	tx_chipset_tag_t tc = tx_conf_get_tag();
151	struct tx39power_softc *sc = tc->tc_powert;
152	txreg_t reg, *iregs = sc->sc_icu_state;
153
154	printf ("%s: CPU sleep\n", sc->sc_dev.dv_xname);
155	__asm volatile(".set noreorder");
156	reg = tx_conf_read(tc, TX39_POWERCTRL_REG);
157	reg |= TX39_POWERCTRL_STOPCPU;
158	/* save interrupt state */
159	iregs[0] = tx_conf_read(tc, TX39_INTRENABLE6_REG);
160	iregs[1] = tx_conf_read(tc, TX39_INTRENABLE1_REG);
161	iregs[2] = tx_conf_read(tc, TX39_INTRENABLE2_REG);
162	iregs[3] = tx_conf_read(tc, TX39_INTRENABLE3_REG);
163	iregs[4] = tx_conf_read(tc, TX39_INTRENABLE4_REG);
164	iregs[5] = tx_conf_read(tc, TX39_INTRENABLE5_REG);
165#ifdef TX392X
166	iregs[7] = tx_conf_read(tc, TX39_INTRENABLE7_REG);
167	iregs[8] = tx_conf_read(tc, TX39_INTRENABLE8_REG);
168#endif
169	/* disable all interrupt (don't disable GLOBALEN) */
170	tx_conf_write(tc, TX39_INTRENABLE6_REG, TX39_INTRENABLE6_GLOBALEN);
171	tx_conf_write(tc, TX39_INTRENABLE1_REG, 0);
172	tx_conf_write(tc, TX39_INTRENABLE2_REG, 0);
173	tx_conf_write(tc, TX39_INTRENABLE3_REG, 0);
174	tx_conf_write(tc, TX39_INTRENABLE4_REG, 0);
175	tx_conf_write(tc, TX39_INTRENABLE5_REG, 0);
176#ifdef TX392X
177	tx_conf_write(tc, TX39_INTRENABLE7_REG, 0);
178	tx_conf_write(tc, TX39_INTRENABLE8_REG, 0);
179#endif
180	/* enable power button interrupt only */
181	tx_conf_write(tc, TX39_INTRCLEAR5_REG, TX39_INTRSTATUS5_NEGONBUTNINT);
182	tx_conf_write(tc, TX39_INTRENABLE5_REG, TX39_INTRSTATUS5_NEGONBUTNINT);
183	__asm volatile(".set push; .set mips2; sync; .set pop");
184
185	/* stop CPU clock */
186	tx_conf_write(tc, TX39_POWERCTRL_REG, reg);
187	__asm volatile(".set push; .set mips2; sync; .set pop");
188	/* wait until power button pressed */
189	/* clear interrupt */
190	tx_conf_write(tc, TX39_INTRCLEAR5_REG, TX39_INTRSTATUS5_NEGONBUTNINT);
191#ifdef TX392X
192	/* Clear WARMSTART bit to reset vector(0xbfc00000) work correctly */
193	reg = tx_conf_read(tc, TX39_POWERCTRL_REG);
194	reg &= ~TX39_POWERCTRL_WARMSTART;
195	tx_conf_write(tc, TX39_POWERCTRL_REG, reg);
196#endif
197
198	/* restore interrupt state */
199	tx_conf_write(tc, TX39_INTRENABLE6_REG, iregs[0]);
200	tx_conf_write(tc, TX39_INTRENABLE1_REG, iregs[1]);
201	tx_conf_write(tc, TX39_INTRENABLE2_REG, iregs[2]);
202	tx_conf_write(tc, TX39_INTRENABLE3_REG, iregs[3]);
203	tx_conf_write(tc, TX39_INTRENABLE4_REG, iregs[4]);
204	tx_conf_write(tc, TX39_INTRENABLE5_REG, iregs[5]);
205#ifdef TX392X
206	tx_conf_write(tc, TX39_INTRENABLE7_REG, iregs[7]);
207	tx_conf_write(tc, TX39_INTRENABLE8_REG, iregs[8]);
208#endif
209	__asm volatile(".set reorder");
210
211	printf ("%s: CPU wakeup\n", sc->sc_dev.dv_xname);
212}
213
214static int
215tx39power_button_intr_p(void *arg)
216{
217	config_hook_call(CONFIG_HOOK_BUTTONEVENT,
218	    CONFIG_HOOK_BUTTONEVENT_POWER,
219	    (void *)1 /* on */);
220
221	return (0);
222}
223
224static int
225tx39power_button_intr_n(void *arg)
226{
227	config_hook_call(CONFIG_HOOK_BUTTONEVENT,
228	    CONFIG_HOOK_BUTTONEVENT_POWER,
229	    (void *)0 /* off */);
230	DUMP_REGS(arg);
231
232	return (0);
233}
234
235int
236tx39power_intr_p(void *arg)
237{
238	/* low priority event */
239	printf("power_p\n");
240	DUMP_REGS(arg);
241
242	return (0);
243}
244
245static int
246tx39power_intr_n(void *arg)
247{
248	/* low priority event */
249	printf("power_n\n");
250	DUMP_REGS(arg);
251
252	return (0);
253}
254
255static int
256tx39power_ok_intr_p(void *arg)
257{
258	/* high priority event */
259	printf("power NG\n");
260	DUMP_REGS(arg);
261	config_hook_call(CONFIG_HOOK_PMEVENT,
262	    CONFIG_HOOK_PMEVENT_SUSPENDREQ, NULL);
263
264	return (0);
265}
266
267static int
268tx39power_ok_intr_n(void *arg)
269{
270	/* high priority event */
271	printf("power OK\n");
272	DUMP_REGS(arg);
273
274	return (0);
275}
276
277#ifdef TX39POWER_DEBUG
278static void
279__tx39power_dump (struct tx39power_softc *sc)
280{
281	tx_chipset_tag_t tc = sc->sc_tc;
282	txreg_t reg;
283
284	reg = tx_conf_read(tc, TX39_POWERCTRL_REG);
285	ISSETPRINT(reg, ONBUTN);
286	ISSETPRINT(reg, PWRINT);
287	ISSETPRINT(reg, PWROK);
288#ifdef TX392X
289	ISSETPRINT(reg, PWROKNMI);
290#endif /* TX392X */
291	ISSETPRINT(reg, SLOWBUS);
292#ifdef TX391X
293	ISSETPRINT(reg, DIVMOD);
294#endif /* TX391X */
295	ISSETPRINT(reg, ENSTPTIMER);
296	ISSETPRINT(reg, ENFORCESHUTDWN);
297	ISSETPRINT(reg, FORCESHUTDWN);
298	ISSETPRINT(reg, FORCESHUTDWNOCC);
299	ISSETPRINT(reg, SELC2MS);
300#ifdef TX392X
301	ISSETPRINT(reg, WARMSTART);
302#endif /* TX392X */
303	ISSETPRINT(reg, BPDBVCC3);
304	ISSETPRINT(reg, STOPCPU);
305	ISSETPRINT(reg, DBNCONBUTN);
306	ISSETPRINT(reg, COLDSTART);
307	ISSETPRINT(reg, PWRCS);
308	ISSETPRINT(reg, VCCON);
309#ifdef TX391X
310	printf("VIDRF=%d ", TX39_POWERCTRL_VIDRF(reg));
311#endif /* TX391X */
312	printf("STPTIMERVAL=%d ", TX39_POWERCTRL_STPTIMERVAL(reg));
313	printf("\n");
314}
315#endif /* TX39POWER_DEBUG */
316