1/*	$OpenBSD: amlpwrc.c,v 1.4 2022/04/06 18:59:28 naddy Exp $	*/
2/*
3 * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
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/systm.h>
20#include <sys/device.h>
21
22#include <machine/intr.h>
23#include <machine/bus.h>
24#include <machine/fdt.h>
25
26#include <dev/ofw/openfirm.h>
27#include <dev/ofw/ofw_power.h>
28#include <dev/ofw/ofw_misc.h>
29#include <dev/ofw/fdt.h>
30
31/* Power domain IDs */
32#define PWRC_G12A_ETH_ID	1
33#define PWRC_SM1_USB_ID		2
34#define PWRC_SM1_PCIE_ID	3
35#define PWRC_SM1_ETH_ID		6
36
37/* Registers */
38#define AO_RTI_GEN_PWR_SLEEP0		0x3a
39#define AO_RTI_GEN_PWR_ISO0		0x3b
40#define  AO_RTI_GEN_PWR_PCIE_MASK	(1 << 18)
41#define  AO_RTI_GEN_PWR_USB_MASK	(1 << 17)
42#define HHI_MEM_PD_REG0			0x40
43#define  HHI_MEM_PD_USB_MASK		(0x3 << 30)
44#define  HHI_MEM_PD_PCIE_MASK		(0xf << 26)
45#define  HHI_MEM_PD_ETH_MASK		(0x3 << 2)
46
47#define HREAD4(sc, reg)							\
48	(regmap_read_4((sc)->sc_rm, (reg) << 2))
49#define HWRITE4(sc, reg, val)						\
50	regmap_write_4((sc)->sc_rm, (reg) << 2, (val))
51#define HSET4(sc, reg, bits)						\
52	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
53#define HCLR4(sc, reg, bits)						\
54	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
55
56struct amlpwrc_softc {
57	struct device		sc_dev;
58	struct regmap		*sc_rm_hhi;
59	struct regmap		*sc_rm_ao;
60	uint32_t		sc_ao;
61	int			sc_node;
62
63	struct power_domain_device sc_pd;
64};
65
66int amlpwrc_match(struct device *, void *, void *);
67void amlpwrc_attach(struct device *, struct device *, void *);
68
69const struct cfattach amlpwrc_ca = {
70	sizeof (struct amlpwrc_softc), amlpwrc_match, amlpwrc_attach
71};
72
73struct cfdriver amlpwrc_cd = {
74	NULL, "amlpwrc", DV_DULL
75};
76
77void	amlpwrc_g12a_enable(void *, uint32_t *, int);
78void	amlpwrc_sm1_enable(void *, uint32_t *, int);
79
80int
81amlpwrc_match(struct device *parent, void *match, void *aux)
82{
83	struct fdt_attach_args *faa = aux;
84
85	return (OF_is_compatible(faa->fa_node, "amlogic,meson-g12a-pwrc") ||
86	    OF_is_compatible(faa->fa_node, "amlogic,meson-sm1-pwrc"));
87}
88
89void
90amlpwrc_attach(struct device *parent, struct device *self, void *aux)
91{
92	struct amlpwrc_softc *sc = (struct amlpwrc_softc *)self;
93	struct fdt_attach_args *faa = aux;
94
95	/*
96	 * We can't lookup the AO regmap at this point since the
97	 * syscon(4) instance that provides it attaches after us.
98	 */
99	sc->sc_rm_hhi = regmap_bynode(OF_parent(faa->fa_node));
100	sc->sc_ao = OF_getpropint(faa->fa_node, "amlogic,ao-sysctrl", 0);
101	if (sc->sc_rm_hhi == NULL || sc->sc_ao == 0) {
102		printf(": no registers\n");
103		return;
104	}
105
106	sc->sc_node = faa->fa_node;
107	printf("\n");
108
109	sc->sc_pd.pd_node = faa->fa_node;
110	sc->sc_pd.pd_cookie = sc;
111	if (OF_is_compatible(faa->fa_node, "amlogic,meson-g12a-pwrc"))
112		sc->sc_pd.pd_enable = amlpwrc_g12a_enable;
113	else if (OF_is_compatible(faa->fa_node, "amlogic,meson-sm1-pwrc"))
114		sc->sc_pd.pd_enable = amlpwrc_sm1_enable;
115	power_domain_register(&sc->sc_pd);
116}
117
118static inline void
119amlpwrc_toggle(struct regmap *rm, bus_size_t reg, uint32_t mask, int on)
120{
121	uint32_t val;
122
123	val = regmap_read_4(rm, reg << 2);
124	if (on)
125		val &= ~mask;
126	else
127		val |= mask;
128	regmap_write_4(rm, reg << 2, val);
129}
130
131void
132amlpwrc_g12a_enable(void *cookie, uint32_t *cells, int on)
133{
134	struct amlpwrc_softc *sc = cookie;
135	uint32_t idx = cells[0];
136
137	sc->sc_rm_ao = regmap_byphandle(sc->sc_ao);
138	KASSERT(sc->sc_rm_ao);
139
140	switch (idx) {
141	case PWRC_G12A_ETH_ID:
142		amlpwrc_toggle(sc->sc_rm_hhi, HHI_MEM_PD_REG0,
143		    HHI_MEM_PD_ETH_MASK, on);
144		delay(20);
145		return;
146	}
147
148	printf("%s: 0x%08x\n", __func__, idx);
149}
150
151void
152amlpwrc_sm1_enable(void *cookie, uint32_t *cells, int on)
153{
154	struct amlpwrc_softc *sc = cookie;
155	uint32_t idx = cells[0];
156
157	sc->sc_rm_ao = regmap_byphandle(sc->sc_ao);
158	KASSERT(sc->sc_rm_ao);
159
160	switch (idx) {
161	case PWRC_SM1_USB_ID:
162		amlpwrc_toggle(sc->sc_rm_ao, AO_RTI_GEN_PWR_SLEEP0,
163		    AO_RTI_GEN_PWR_USB_MASK, on);
164		delay(20);
165		amlpwrc_toggle(sc->sc_rm_hhi, HHI_MEM_PD_REG0,
166		    HHI_MEM_PD_USB_MASK, on);
167		delay(20);
168		amlpwrc_toggle(sc->sc_rm_ao, AO_RTI_GEN_PWR_ISO0,
169		    AO_RTI_GEN_PWR_USB_MASK, on);
170		return;
171	case PWRC_SM1_PCIE_ID:
172		amlpwrc_toggle(sc->sc_rm_ao, AO_RTI_GEN_PWR_SLEEP0,
173		    AO_RTI_GEN_PWR_PCIE_MASK, on);
174		delay(20);
175		amlpwrc_toggle(sc->sc_rm_hhi, HHI_MEM_PD_REG0,
176		    HHI_MEM_PD_PCIE_MASK, on);
177		delay(20);
178		amlpwrc_toggle(sc->sc_rm_ao, AO_RTI_GEN_PWR_ISO0,
179		    AO_RTI_GEN_PWR_PCIE_MASK, on);
180		return;
181	case PWRC_SM1_ETH_ID:
182		amlpwrc_toggle(sc->sc_rm_hhi, HHI_MEM_PD_REG0,
183		    HHI_MEM_PD_ETH_MASK, on);
184		delay(20);
185		return;
186	}
187
188	printf("%s: 0x%08x\n", __func__, idx);
189}
190