1/*	$OpenBSD: qcpon.c,v 1.4 2023/04/24 14:34:13 patrick Exp $	*/
2/*
3 * Copyright (c) 2022 Patrick Wildt <patrick@blueri.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/param.h>
19#include <sys/malloc.h>
20#include <sys/systm.h>
21#include <sys/task.h>
22#include <sys/proc.h>
23#include <sys/signalvar.h>
24
25#include <machine/bus.h>
26#include <machine/fdt.h>
27
28#include <dev/fdt/spmivar.h>
29
30#include <dev/ofw/openfirm.h>
31#include <dev/ofw/ofw_clock.h>
32#include <dev/ofw/ofw_power.h>
33#include <dev/ofw/fdt.h>
34
35struct qcpon_softc {
36	struct device		sc_dev;
37	int			sc_node;
38
39	spmi_tag_t		sc_tag;
40	int8_t			sc_sid;
41
42	void			*sc_pwrkey_ih;
43	int			sc_pwrkey_debounce;
44	struct task		sc_powerdown_task;
45};
46
47int	qcpon_match(struct device *, void *, void *);
48void	qcpon_attach(struct device *, struct device *, void *);
49
50int	qcpon_pwrkey_intr(void *);
51void	qcpon_powerdown_task(void *);
52
53const struct cfattach qcpon_ca = {
54	sizeof(struct qcpon_softc), qcpon_match, qcpon_attach
55};
56
57struct cfdriver qcpon_cd = {
58	NULL, "qcpon", DV_DULL
59};
60
61int
62qcpon_match(struct device *parent, void *match, void *aux)
63{
64	struct spmi_attach_args *saa = aux;
65
66	return (OF_is_compatible(saa->sa_node, "qcom,pm8998-pon") ||
67	    OF_is_compatible(saa->sa_node, "qcom,pmk8350-pon"));
68}
69
70void
71qcpon_attach(struct device *parent, struct device *self, void *aux)
72{
73	struct spmi_attach_args *saa = aux;
74	struct qcpon_softc *sc = (struct qcpon_softc *)self;
75	int node;
76
77	sc->sc_node = saa->sa_node;
78	sc->sc_tag = saa->sa_tag;
79	sc->sc_sid = saa->sa_sid;
80
81	task_set(&sc->sc_powerdown_task, qcpon_powerdown_task, sc);
82
83	printf("\n");
84
85	for (node = OF_child(saa->sa_node); node; node = OF_peer(node)) {
86		if (OF_is_compatible(node, "qcom,pmk8350-pwrkey")) {
87			sc->sc_pwrkey_ih = fdt_intr_establish(node,
88			    IPL_BIO | IPL_WAKEUP, qcpon_pwrkey_intr, sc,
89			    sc->sc_dev.dv_xname);
90			if (sc->sc_pwrkey_ih == NULL) {
91				printf("%s: can't establish interrupt\n",
92				    sc->sc_dev.dv_xname);
93				continue;
94			}
95#ifdef SUSPEND
96			device_register_wakeup(&sc->sc_dev);
97#endif
98		}
99	}
100}
101
102int
103qcpon_pwrkey_intr(void *arg)
104{
105	struct qcpon_softc *sc = arg;
106#ifdef SUSPEND
107	extern int cpu_suspended;
108#endif
109
110	/* Ignore presses, handle releases. */
111	sc->sc_pwrkey_debounce = (sc->sc_pwrkey_debounce + 1) % 2;
112	if (sc->sc_pwrkey_debounce == 1)
113		return 1;
114
115#ifdef SUSPEND
116	if (cpu_suspended)
117		cpu_suspended = 0;
118	else
119#endif
120		task_add(systq, &sc->sc_powerdown_task);
121
122	return 1;
123}
124
125void
126qcpon_powerdown_task(void *arg)
127{
128	extern int allowpowerdown;
129
130	if (allowpowerdown == 1) {
131		allowpowerdown = 0;
132		prsignal(initprocess, SIGUSR2);
133	}
134}
135