aplpmgr.c revision 1.1
138136Sdfr/*	$OpenBSD: aplpmgr.c,v 1.1 2021/12/09 11:38:27 kettenis Exp $	*/
238136Sdfr/*
338136Sdfr * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
438136Sdfr *
538136Sdfr * Permission to use, copy, modify, and distribute this software for any
638136Sdfr * purpose with or without fee is hereby granted, provided that the above
738136Sdfr * copyright notice and this permission notice appear in all copies.
838136Sdfr *
938136Sdfr * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1038136Sdfr * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1138136Sdfr * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1238136Sdfr * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1338136Sdfr * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1438136Sdfr * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1538136Sdfr * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1638136Sdfr */
1738136Sdfr
1838136Sdfr#include <sys/param.h>
1938136Sdfr#include <sys/systm.h>
2038136Sdfr#include <sys/device.h>
2138136Sdfr#include <sys/malloc.h>
2238136Sdfr
2338136Sdfr#include <machine/bus.h>
2438136Sdfr#include <machine/fdt.h>
2538136Sdfr
2638136Sdfr#include <dev/ofw/openfirm.h>
2738136Sdfr#include <dev/ofw/ofw_misc.h>
2838136Sdfr#include <dev/ofw/ofw_power.h>
2938136Sdfr#include <dev/ofw/fdt.h>
3038136Sdfr
3138136Sdfr#define PMGR_PS_TARGET_MASK	0x0000000f
3238136Sdfr#define PMGR_PS_TARGET_SHIFT	0
3338136Sdfr#define PMGR_PS_ACTUAL_MASK	0x000000f0
3438136Sdfr#define PMGR_PS_ACTUAL_SHIFT	4
3538136Sdfr#define  PMGR_PS_ACTIVE		0xf
3638136Sdfr#define  PMGR_PS_PWRGATE	0x0
3742333Syokota
3838136Sdfr#define HREAD4(sc, reg)							\
3938136Sdfr	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
4038136Sdfr#define HWRITE4(sc, reg, val)						\
4138136Sdfr	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
4238136Sdfr
4338136Sdfrstruct aplpmgr_softc;
4438136Sdfr
4538136Sdfrstruct aplpmgr_pwrstate {
4638136Sdfr	struct aplpmgr_softc	*ps_sc;
4738136Sdfr	struct power_domain_device ps_pd;
4838136Sdfr	bus_size_t		ps_offset;
4938136Sdfr};
5038136Sdfr
5138136Sdfrstruct aplpmgr_softc {
5238136Sdfr	struct device		sc_dev;
5338136Sdfr	bus_space_tag_t		sc_iot;
5438136Sdfr	bus_space_handle_t	sc_ioh;
5538136Sdfr
5638136Sdfr	struct aplpmgr_pwrstate	*sc_pwrstate;
5738136Sdfr	int			sc_npwrstate;
5838136Sdfr};
5938136Sdfr
6038136Sdfrint	aplpmgr_match(struct device *, void *, void *);
6138136Sdfrvoid	aplpmgr_attach(struct device *, struct device *, void *);
6238136Sdfr
6338136Sdfrconst struct cfattach aplpmgr_ca = {
6438136Sdfr	sizeof (struct aplpmgr_softc), aplpmgr_match, aplpmgr_attach
6538136Sdfr};
6638136Sdfr
6738136Sdfrstruct cfdriver aplpmgr_cd = {
6838136Sdfr	NULL, "aplpmgr", DV_DULL
6938136Sdfr};
7038136Sdfr
7138136Sdfrvoid	aplpmgr_enable(void *, uint32_t *, int);
7238136Sdfr
7338136Sdfrint
7438136Sdfraplpmgr_match(struct device *parent, void *match, void *aux)
7538136Sdfr{
7638136Sdfr	struct fdt_attach_args *faa = aux;
7738136Sdfr
7838136Sdfr	if (OF_is_compatible(faa->fa_node, "apple,pmgr"))
7938136Sdfr		return 10;	/* Must beat syscon(4). */
8038136Sdfr
8138136Sdfr	return 0;
8238136Sdfr}
8338136Sdfr
8438136Sdfrvoid
8538136Sdfraplpmgr_attach(struct device *parent, struct device *self, void *aux)
8638136Sdfr{
8738136Sdfr	struct aplpmgr_softc *sc = (struct aplpmgr_softc *)self;
8838136Sdfr	struct fdt_attach_args *faa = aux;
8938136Sdfr	struct aplpmgr_pwrstate *ps;
9038136Sdfr	uint32_t reg[2];
9138136Sdfr	int node;
9238136Sdfr
9338136Sdfr	if (faa->fa_nreg < 1) {
9438136Sdfr		printf(": no registers\n");
9538136Sdfr		return;
9638136Sdfr	}
9738136Sdfr
9838136Sdfr	sc->sc_iot = faa->fa_iot;
9938136Sdfr	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
10038136Sdfr	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
10138136Sdfr		printf(": can't map registers\n");
10238136Sdfr		return;
10338136Sdfr	}
10438136Sdfr
10538136Sdfr	printf("\n");
10638136Sdfr
10738136Sdfr	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
10838136Sdfr		if (OF_is_compatible(node, "apple,pmgr-pwrstate"))
10938136Sdfr			sc->sc_npwrstate++;
11038136Sdfr	}
11138136Sdfr
11238136Sdfr	sc->sc_pwrstate = mallocarray(sc->sc_npwrstate,
11338136Sdfr	    sizeof(*sc->sc_pwrstate), M_DEVBUF, M_WAITOK | M_ZERO);
11438136Sdfr
11538136Sdfr	ps = sc->sc_pwrstate;
11638136Sdfr	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
11738136Sdfr		if (!OF_is_compatible(node, "apple,pmgr-pwrstate"))
11838136Sdfr			continue;
11938136Sdfr
12038136Sdfr		if (OF_getpropintarray(node, "reg", reg,
12138136Sdfr		    sizeof(reg)) != sizeof(reg)) {
12238136Sdfr			printf("%s: invalid reg property\n",
12338136Sdfr			    sc->sc_dev.dv_xname);
12438136Sdfr			continue;
12538136Sdfr		}
12638136Sdfr
12738136Sdfr		ps->ps_sc = sc;
12838136Sdfr		ps->ps_offset = reg[0];
12938136Sdfr		ps->ps_pd.pd_node = node;
13038136Sdfr		ps->ps_pd.pd_cookie = ps;
13138136Sdfr		ps->ps_pd.pd_enable = aplpmgr_enable;
13238136Sdfr		power_domain_register(&ps->ps_pd);
13338136Sdfr		ps++;
13438136Sdfr	}
13538136Sdfr}
13638136Sdfr
13738136Sdfrvoid
13838136Sdfraplpmgr_enable(void *cookie, uint32_t *cells, int on)
13938136Sdfr{
14038136Sdfr	struct aplpmgr_pwrstate *ps = cookie;
14138136Sdfr	struct aplpmgr_softc *sc = ps->ps_sc;
14238136Sdfr	uint32_t pstate = on ? PMGR_PS_ACTIVE : PMGR_PS_PWRGATE;
14338136Sdfr	uint32_t val;
14438136Sdfr	int timo;
14538136Sdfr
14638136Sdfr	power_domain_enable_all(ps->ps_pd.pd_node);
14738136Sdfr
14838136Sdfr	val = HREAD4(sc, ps->ps_offset);
14938136Sdfr	val &= ~PMGR_PS_TARGET_MASK;
15042333Syokota	val |= (pstate << PMGR_PS_TARGET_SHIFT);
15138136Sdfr	HWRITE4(sc, ps->ps_offset, val);
15238136Sdfr
15338136Sdfr	for (timo = 0; timo < 100; timo++) {
15438136Sdfr		val = HREAD4(sc, ps->ps_offset);
15538136Sdfr		val &= PMGR_PS_ACTUAL_MASK;
15638136Sdfr		if ((val >> PMGR_PS_ACTUAL_SHIFT) == pstate)
15738136Sdfr			break;
15838136Sdfr		delay(1);
15938136Sdfr	}
16038136Sdfr}
16142333Syokota