diff options
Diffstat (limited to 'sys/dev/fdt/amlpwrc.c')
-rw-r--r-- | sys/dev/fdt/amlpwrc.c | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/sys/dev/fdt/amlpwrc.c b/sys/dev/fdt/amlpwrc.c new file mode 100644 index 00000000000..c331f9cb27b --- /dev/null +++ b/sys/dev/fdt/amlpwrc.c @@ -0,0 +1,176 @@ +/* $OpenBSD: amlpwrc.c,v 1.1 2020/05/18 10:39:33 kettenis Exp $ */ +/* + * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/intr.h> +#include <machine/bus.h> +#include <machine/fdt.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_power.h> +#include <dev/ofw/ofw_misc.h> +#include <dev/ofw/fdt.h> + +/* Power domain IDs */ +#define PWRC_G12A_ETH_ID 1 +#define PWRC_SM1_USB_ID 2 +#define PWRC_SM1_ETH_ID 6 + +/* Registers */ +#define AO_RTI_GEN_PWR_SLEEP0 0x3a +#define AO_RTI_GEN_PWR_ISO0 0x3b +#define AO_RTI_GEN_PWR_USB_MASK (1 << 17) +#define HHI_MEM_PD_REG0 0x40 +#define HHI_MEM_PD_USB_MASK (0x3 << 30) +#define HHI_MEM_PD_ETH_MASK (0x3 << 2) + +#define HREAD4(sc, reg) \ + (regmap_read_4((sc)->sc_rm, (reg) << 2)) +#define HWRITE4(sc, reg, val) \ + regmap_write_4((sc)->sc_rm, (reg) << 2, (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + +struct amlpwrc_softc { + struct device sc_dev; + struct regmap *sc_rm_hhi; + struct regmap *sc_rm_ao; + uint32_t sc_ao; + int sc_node; + + struct power_domain_device sc_pd; +}; + +int amlpwrc_match(struct device *, void *, void *); +void amlpwrc_attach(struct device *, struct device *, void *); + +struct cfattach amlpwrc_ca = { + sizeof (struct amlpwrc_softc), amlpwrc_match, amlpwrc_attach +}; + +struct cfdriver amlpwrc_cd = { + NULL, "amlpwrc", DV_DULL +}; + +void amlpwrc_g12a_enable(void *, uint32_t *, int); +void amlpwrc_sm1_enable(void *, uint32_t *, int); + +int +amlpwrc_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return (OF_is_compatible(faa->fa_node, "amlogic,meson-g12a-pwrc") || + OF_is_compatible(faa->fa_node, "amlogic,meson-sm1-pwrc")); +} + +void +amlpwrc_attach(struct device *parent, struct device *self, void *aux) +{ + struct amlpwrc_softc *sc = (struct amlpwrc_softc *)self; + struct fdt_attach_args *faa = aux; + + /* + * We can't lookup the AO regmap at this point since the + * syscon(4) instance that provides it attaches after us. + */ + sc->sc_rm_hhi = regmap_bynode(OF_parent(faa->fa_node)); + sc->sc_ao = OF_getpropint(faa->fa_node, "amlogic,ao-sysctrl", 0); + if (sc->sc_rm_hhi == NULL || sc->sc_ao == 0) { + printf(": no registers\n"); + return; + } + + sc->sc_node = faa->fa_node; + printf("\n"); + + sc->sc_pd.pd_node = faa->fa_node; + sc->sc_pd.pd_cookie = sc; + if (OF_is_compatible(faa->fa_node, "amlogic,meson-g12a-pwrc")) + sc->sc_pd.pd_enable = amlpwrc_g12a_enable; + else if (OF_is_compatible(faa->fa_node, "amlogic,meson-sm1-pwrc")) + sc->sc_pd.pd_enable = amlpwrc_sm1_enable; + power_domain_register(&sc->sc_pd); +} + +inline void +amlpwrc_toggle(struct regmap *rm, bus_size_t reg, uint32_t mask, int on) +{ + uint32_t val; + + val = regmap_read_4(rm, reg << 2); + if (on) + val &= ~mask; + else + val |= mask; + regmap_write_4(rm, reg << 2, val); +} + +void +amlpwrc_g12a_enable(void *cookie, uint32_t *cells, int on) +{ + struct amlpwrc_softc *sc = cookie; + uint32_t idx = cells[0]; + + sc->sc_rm_ao = regmap_byphandle(sc->sc_ao); + KASSERT(sc->sc_rm_ao); + + switch (idx) { + case PWRC_G12A_ETH_ID: + amlpwrc_toggle(sc->sc_rm_hhi, HHI_MEM_PD_REG0, + HHI_MEM_PD_ETH_MASK, on); + delay(20); + return; + } + + printf("%s: 0x%08x\n", __func__, idx); +} + +void +amlpwrc_sm1_enable(void *cookie, uint32_t *cells, int on) +{ + struct amlpwrc_softc *sc = cookie; + uint32_t idx = cells[0]; + + sc->sc_rm_ao = regmap_byphandle(sc->sc_ao); + KASSERT(sc->sc_rm_ao); + + switch (idx) { + case PWRC_SM1_USB_ID: + amlpwrc_toggle(sc->sc_rm_ao, AO_RTI_GEN_PWR_SLEEP0, + AO_RTI_GEN_PWR_USB_MASK, on); + delay(20); + amlpwrc_toggle(sc->sc_rm_hhi, HHI_MEM_PD_REG0, + HHI_MEM_PD_USB_MASK, on); + delay(20); + amlpwrc_toggle(sc->sc_rm_ao, AO_RTI_GEN_PWR_ISO0, + AO_RTI_GEN_PWR_USB_MASK, on); + return; + case PWRC_SM1_ETH_ID: + amlpwrc_toggle(sc->sc_rm_hhi, HHI_MEM_PD_REG0, + HHI_MEM_PD_ETH_MASK, on); + delay(20); + return; + } + + printf("%s: 0x%08x\n", __func__, idx); +} |