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
56 struct 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
66 int amlpwrc_match(struct device *, void *, void *);
67 void amlpwrc_attach(struct device *, struct device *, void *);
68
69 const struct cfattach amlpwrc_ca = {
70 sizeof (struct amlpwrc_softc), amlpwrc_match, amlpwrc_attach
71 };
72
73 struct cfdriver amlpwrc_cd = {
74 NULL, "amlpwrc", DV_DULL
75 };
76
77 void amlpwrc_g12a_enable(void *, uint32_t *, int);
78 void amlpwrc_sm1_enable(void *, uint32_t *, int);
79
80 int
amlpwrc_match(struct device * parent,void * match,void * aux)81 amlpwrc_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
89 void
amlpwrc_attach(struct device * parent,struct device * self,void * aux)90 amlpwrc_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
118 static inline void
amlpwrc_toggle(struct regmap * rm,bus_size_t reg,uint32_t mask,int on)119 amlpwrc_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
131 void
amlpwrc_g12a_enable(void * cookie,uint32_t * cells,int on)132 amlpwrc_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
151 void
amlpwrc_sm1_enable(void * cookie,uint32_t * cells,int on)152 amlpwrc_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