#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_pinctrl.h>
#include <dev/ofw/ofw_regulator.h>
#include <dev/ofw/fdt.h>
#include <dev/i2c/i2cvar.h>
#define FAN53555_VSEL0 0x00
#define FAN53555_VSEL1 0x01
#define FAN53555_VSEL_NSEL_MASK 0x3f
#define FAN53555_CONTROL 0x02
#define FAN53555_CONTROL_SLEW_MASK (0x7 << 4)
#define FAN53555_CONTROL_SLEW_SHIFT 4
#define FAN53555_ID1 0x03
#define FAN53555_ID2 0x04
#define TCS4525_VSEL1 0x10
#define TCS4525_VSEL0 0x11
#define TCS4525_VSEL_NSEL_MASK 0x7f
#define TCS4525_TIME 0x13
#define TCS4525_TIME_SLEW_MASK (0x3 << 3)
#define TCS4525_TIME_SLEW_SHIFT 3
#define RK8602_VSEL0 0x06
#define RK8602_VSEL1 0x07
#define RK8602_VSEL_NSEL_MASK 0xff
enum fanpwr_id {
FANPWR_FAN53555,
FANPWR_RK8602,
FANPWR_SYR827,
FANPWR_SYR828,
FANPWR_TCS4525,
};
struct fanpwr_softc {
struct device sc_dev;
i2c_tag_t sc_tag;
i2c_addr_t sc_addr;
enum fanpwr_id sc_id;
uint8_t sc_vsel;
uint8_t sc_vsel_nsel_mask;
struct regulator_device sc_rd;
uint32_t sc_vbase;
uint32_t sc_vstep;
};
int fanpwr_match(struct device *, void *, void *);
void fanpwr_attach(struct device *, struct device *, void *);
const struct cfattach fanpwr_ca = {
sizeof(struct fanpwr_softc), fanpwr_match, fanpwr_attach
};
struct cfdriver fanpwr_cd = {
NULL, "fanpwr", DV_DULL
};
uint8_t fanpwr_read(struct fanpwr_softc *, int);
void fanpwr_write(struct fanpwr_softc *, int, uint8_t);
uint32_t fanpwr_get_voltage(void *);
int fanpwr_set_voltage(void *, uint32_t);
int
fanpwr_match(struct device *parent, void *match, void *aux)
{
struct i2c_attach_args *ia = aux;
return (strcmp(ia->ia_name, "fcs,fan53555") == 0 ||
strcmp(ia->ia_name, "rockchip,rk8602") == 0 ||
strcmp(ia->ia_name, "rockchip,rk8603") == 0 ||
strcmp(ia->ia_name, "silergy,syr827") == 0 ||
strcmp(ia->ia_name, "silergy,syr828") == 0 ||
strcmp(ia->ia_name, "tcs,tcs4525") == 0);
}
void
fanpwr_attach(struct device *parent, struct device *self, void *aux)
{
struct fanpwr_softc *sc = (struct fanpwr_softc *)self;
struct i2c_attach_args *ia = aux;
int node = *(int *)ia->ia_cookie;
uint32_t voltage, ramp_delay;
uint8_t id1, id2;
pinctrl_byname(node, "default");
sc->sc_tag = ia->ia_tag;
sc->sc_addr = ia->ia_addr;
if (OF_is_compatible(node, "rockchip,rk8602") ||
OF_is_compatible(node, "rockchip,rk8603")) {
printf(": RK8602");
sc->sc_id = FANPWR_RK8602;
} else if (OF_is_compatible(node, "silergy,syr827")) {
printf(": SYR827");
sc->sc_id = FANPWR_SYR827;
} else if (OF_is_compatible(node, "silergy,syr828")) {
printf(": SYR828");
sc->sc_id = FANPWR_SYR828;
} else if (OF_is_compatible(node, "tcs,tcs4525")) {
printf(": TCS4525");
sc->sc_id = FANPWR_TCS4525;
} else {
printf(": FAN53555");
sc->sc_id = FANPWR_FAN53555;
}
if (sc->sc_id == FANPWR_TCS4525) {
if (OF_getpropint(node, "fcs,suspend-voltage-selector", 0))
sc->sc_vsel = TCS4525_VSEL0;
else
sc->sc_vsel = TCS4525_VSEL1;
sc->sc_vsel_nsel_mask = TCS4525_VSEL_NSEL_MASK;
} else if (sc->sc_id == FANPWR_RK8602) {
if (OF_getpropint(node, "fcs,suspend-voltage-selector", 0))
sc->sc_vsel = RK8602_VSEL0;
else
sc->sc_vsel = RK8602_VSEL1;
sc->sc_vsel_nsel_mask = RK8602_VSEL_NSEL_MASK;
} else {
if (OF_getpropint(node, "fcs,suspend-voltage-selector", 0))
sc->sc_vsel = FAN53555_VSEL0;
else
sc->sc_vsel = FAN53555_VSEL1;
sc->sc_vsel_nsel_mask = FAN53555_VSEL_NSEL_MASK;
}
id1 = fanpwr_read(sc, FAN53555_ID1);
id2 = fanpwr_read(sc, FAN53555_ID2);
switch (sc->sc_id) {
case FANPWR_FAN53555:
switch (id1 << 8 | id2) {
case 0x8003:
case 0x8103:
case 0x8303:
case 0x8503:
case 0x8801:
case 0x880f:
case 0x8108:
sc->sc_vbase = 600000;
sc->sc_vstep = 10000;
break;
case 0x840f:
case 0x8c0f:
sc->sc_vbase = 603000;
sc->sc_vstep = 12826;
break;
case 0x800f:
sc->sc_vbase = 800000;
sc->sc_vstep = 10000;
break;
case 0x800c:
sc->sc_vbase = 600000;
sc->sc_vstep = 12500;
break;
case 0x8004:
sc->sc_vbase = 603000;
sc->sc_vstep = 12967;
break;
default:
printf(", unknown ID1 0x%02x ID2 0x%02x\n", id1, id2);
return;
}
break;
case FANPWR_RK8602:
sc->sc_vbase = 500000;
sc->sc_vstep = 6250;
break;
case FANPWR_SYR827:
case FANPWR_SYR828:
sc->sc_vbase = 712500;
sc->sc_vstep = 12500;
break;
case FANPWR_TCS4525:
sc->sc_vbase = 600000;
sc->sc_vstep = 6250;
break;
}
voltage = fanpwr_get_voltage(sc);
printf(", %d.%02d VDC", voltage / 1000000,
(voltage % 1000000) / 10000);
ramp_delay = OF_getpropint(node, "regulator-ramp-delay", 0);
if (ramp_delay > 0) {
if (sc->sc_id == FANPWR_TCS4525) {
uint8_t ctrl, slew;
if (ramp_delay >= 18700)
slew = 0;
else if (ramp_delay >= 9300)
slew = 1;
else if (ramp_delay >= 4600)
slew = 2;
else
slew = 3;
ctrl = fanpwr_read(sc, TCS4525_TIME);
ctrl &= ~TCS4525_TIME_SLEW_MASK;
ctrl |= slew << TCS4525_TIME_SLEW_SHIFT;
fanpwr_write(sc, TCS4525_TIME, ctrl);
} else {
uint8_t ctrl, slew;
for (slew = 7; slew > 0; slew--)
if ((64000 >> slew) >= ramp_delay)
break;
ctrl = fanpwr_read(sc, FAN53555_CONTROL);
ctrl &= ~FAN53555_CONTROL_SLEW_MASK;
ctrl |= slew << FAN53555_CONTROL_SLEW_SHIFT;
fanpwr_write(sc, FAN53555_CONTROL, ctrl);
}
}
sc->sc_rd.rd_node = node;
sc->sc_rd.rd_cookie = sc;
sc->sc_rd.rd_get_voltage = fanpwr_get_voltage;
sc->sc_rd.rd_set_voltage = fanpwr_set_voltage;
regulator_register(&sc->sc_rd);
printf("\n");
}
uint8_t
fanpwr_read(struct fanpwr_softc *sc, int reg)
{
uint8_t cmd = reg;
uint8_t val;
int error;
iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
&cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
iic_release_bus(sc->sc_tag, I2C_F_POLL);
if (error) {
printf("error %d\n", error);
printf("%s: can't read register 0x%02x\n",
sc->sc_dev.dv_xname, reg);
val = 0xff;
}
return val;
}
void
fanpwr_write(struct fanpwr_softc *sc, int reg, uint8_t val)
{
uint8_t cmd = reg;
int error;
iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
&cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
iic_release_bus(sc->sc_tag, I2C_F_POLL);
if (error) {
printf("%s: can't write register 0x%02x\n",
sc->sc_dev.dv_xname, reg);
}
}
uint32_t
fanpwr_get_voltage(void *cookie)
{
struct fanpwr_softc *sc = cookie;
uint8_t vsel;
vsel = fanpwr_read(sc, sc->sc_vsel);
return sc->sc_vbase + (vsel & sc->sc_vsel_nsel_mask) * sc->sc_vstep;
}
int
fanpwr_set_voltage(void *cookie, uint32_t voltage)
{
struct fanpwr_softc *sc = cookie;
uint32_t vmin = sc->sc_vbase;
uint32_t vmax = vmin + sc->sc_vsel_nsel_mask * sc->sc_vstep;
uint8_t vsel;
if (voltage < vmin || voltage > vmax)
return EINVAL;
vsel = fanpwr_read(sc, sc->sc_vsel);
vsel &= ~sc->sc_vsel_nsel_mask;
vsel |= (voltage - sc->sc_vbase) / sc->sc_vstep;
fanpwr_write(sc, sc->sc_vsel, vsel);
return 0;
}