#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mvsocpmu.c,v 1.1 2017/01/07 16:19:28 kiyohara Exp $");
#include "opt_mvsoc.h"
#include <sys/param.h>
#include <sys/device.h>
#include <sys/errno.h>
#include <dev/sysmon/sysmonvar.h>
#include <arm/marvell/mvsocreg.h>
#include <arm/marvell/mvsocvar.h>
#include <arm/marvell/mvsocpmuvar.h>
#include <dev/marvell/marvellreg.h>
#include <dev/marvell/marvellvar.h>
#define UC2UK(uc) ((uc) + 273150000)
#define UK2UC(uk) ((uk) - 273150000)
#define MVSOCPMU_TM_CSR 0x0
#define TM_CSR_TMDIS (1 << 0)
#define TM_CSR_THERMTEMPOUT(v) (((v) >> 1) & 0x1ff)
#define TM_CSR_THR(oh, c) ((((oh) & 0x1ff) << 19)|(((c) & 0x1ff) << 10))
#define TM_CSR_COOLTHR_MASK (0x1ff << 10)
#define TM_CSR_OVERHEATTHR_MASK (0x1ff << 19)
#define TM_CSR_COOLTHR(v) (((v) >> 10) & 0x1ff)
#define TM_CSR_OVERHEATTHR(v) (((v) >> 19) & 0x1ff)
#define MVSOCPMU_TM_CDR 0x4
#define MVSOCPMU_TM_ODR 0x8
#define MVSOCPMU_TM_READ(sc, r) \
bus_space_read_4((sc)->sc_iot, (sc)->sc_tmh, _TM_REG(r))
#define MVSOCPMU_TM_WRITE(sc, r, v) \
bus_space_write_4((sc)->sc_iot, (sc)->sc_tmh, _TM_REG(r), (v))
#define _TM_REG(r) MVSOCPMU_TM_ ## r
static void mvsocpmu_tm_init(struct mvsocpmu_softc *);
static void mvsocpmu_tm_refresh(struct sysmon_envsys *, envsys_data_t *);
static void mvsocpmu_tm_get_limits(struct sysmon_envsys *, envsys_data_t *,
sysmon_envsys_lim_t *, uint32_t *);
static void mvsocpmu_tm_set_limits(struct sysmon_envsys *, envsys_data_t *,
sysmon_envsys_lim_t *, uint32_t *);
int
mvsocpmu_match(device_t parent, struct cfdata *match, void *aux)
{
struct marvell_attach_args *mva = aux;
if (strcmp(mva->mva_name, match->cf_name) != 0)
return 0;
return 1;
}
void
mvsocpmu_attach(device_t parent, device_t self, void *aux)
{
struct mvsocpmu_softc *sc = device_private(self);
aprint_naive("\n");
aprint_normal(": Marvell SoC Power Management Unit\n");
sc->sc_dev = self;
if (sc->sc_val2uc != NULL && sc->sc_uc2val != NULL)
mvsocpmu_tm_init(sc);
}
static void
mvsocpmu_tm_init(struct mvsocpmu_softc *sc)
{
uint32_t csr;
csr = MVSOCPMU_TM_READ(sc, CSR);
sc->sc_deflims.sel_warnmin = UC2UK(sc->sc_val2uc(TM_CSR_COOLTHR(csr)));
sc->sc_deflims.sel_warnmax =
UC2UK(sc->sc_val2uc(TM_CSR_OVERHEATTHR(csr)));
sc->sc_defprops = PROP_WARNMIN | PROP_WARNMAX;
sc->sc_sme = sysmon_envsys_create();
sc->sc_sensor.units = ENVSYS_STEMP;
sc->sc_sensor.state = ENVSYS_SINVALID;
sc->sc_sensor.flags = ENVSYS_FMONLIMITS;
strlcpy(sc->sc_sensor.desc, device_xname(sc->sc_dev),
sizeof(sc->sc_sensor.desc));
if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) {
aprint_error_dev(sc->sc_dev, "Unable to attach sysmon\n");
sysmon_envsys_destroy(sc->sc_sme);
return;
}
sc->sc_sme->sme_name = device_xname(sc->sc_dev);
sc->sc_sme->sme_cookie = sc;
sc->sc_sme->sme_refresh = mvsocpmu_tm_refresh;
sc->sc_sme->sme_get_limits = mvsocpmu_tm_get_limits;
sc->sc_sme->sme_set_limits = mvsocpmu_tm_set_limits;
if (sysmon_envsys_register(sc->sc_sme)) {
aprint_error_dev(sc->sc_dev,
"Unable to register with sysmon\n");
sysmon_envsys_destroy(sc->sc_sme);
}
}
static void
mvsocpmu_tm_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
{
struct mvsocpmu_softc *sc = sme->sme_cookie;
uint32_t csr, uc, uk;
csr = MVSOCPMU_TM_READ(sc, CSR);
if (csr & TM_CSR_TMDIS) {
sc->sc_sensor.state = ENVSYS_SINVALID;
return;
}
uc = sc->sc_val2uc(TM_CSR_THERMTEMPOUT(csr));
uk = UC2UK(uc);
sc->sc_sensor.value_cur = uk;
sc->sc_sensor.state = ENVSYS_SVALID;
}
static void
mvsocpmu_tm_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
sysmon_envsys_lim_t *limits, uint32_t *props)
{
struct mvsocpmu_softc *sc = sme->sme_cookie;
uint32_t csr;
csr = MVSOCPMU_TM_READ(sc, CSR);
limits->sel_warnmin = UC2UK(sc->sc_val2uc(TM_CSR_COOLTHR(csr)));
limits->sel_warnmax = UC2UK(sc->sc_val2uc(TM_CSR_OVERHEATTHR(csr)));
*props = (PROP_WARNMIN | PROP_WARNMAX | PROP_DRIVER_LIMITS);
}
static void
mvsocpmu_tm_set_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
sysmon_envsys_lim_t *limits, uint32_t *props)
{
struct mvsocpmu_softc *sc = sme->sme_cookie;
uint32_t csr, mask;
int oh, c;
if (limits == NULL) {
limits = &sc->sc_deflims;
props = &sc->sc_defprops;
}
oh = c = 0;
mask = 0x0;
if (*props & PROP_WARNMIN) {
c = sc->sc_uc2val(UK2UC(limits->sel_warnmin));
mask |= TM_CSR_COOLTHR_MASK;
}
if (*props & PROP_WARNMAX) {
oh = sc->sc_uc2val(UK2UC(limits->sel_warnmax));
mask |= TM_CSR_OVERHEATTHR_MASK;
}
if (mask != 0) {
csr = MVSOCPMU_TM_READ(sc, CSR);
csr &= ~mask;
MVSOCPMU_TM_WRITE(sc, CSR, csr | TM_CSR_THR(oh, c));
}
if (*props & (PROP_WARNMIN | PROP_WARNMAX))
*props |= PROP_DRIVER_LIMITS;
else
*props &= ~PROP_DRIVER_LIMITS;
}