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