#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/task.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_regulator.h>
#include <dev/ofw/fdt.h>
#include <dev/i2c/i2cvar.h>
#include <dev/clock_subr.h>
#include <machine/fdt.h>
extern void (*cpuresetfn)(void);
extern void (*powerdownfn)(void);
#define FAULT_LOG 0x05
#define EVENT_A 0x06
#define EVENT_A_EVENTS_D (1 << 7)
#define EVENT_A_EVENTS_C (1 << 6)
#define EVENT_A_EVENTS_B (1 << 5)
#define EVENT_A_E_nONKEY (1 << 0)
#define EVENT_B 0x07
#define EVENT_C 0x08
#define EVENT_D 0x09
#define IRQ_MASK_A 0x0a
#define IRQ_MASK_A_M_RESERVED ((1 << 7) | (1 << 6) | (1 << 5))
#define IRQ_MASK_A_M_SEQ_RDY (1 << 4)
#define IRQ_MASK_A_M_ADC_RDY (1 << 3)
#define IRQ_MASK_A_M_TICK (1 << 2)
#define IRQ_MASK_A_M_ALARM (1 << 1)
#define IRQ_MASK_A_M_nONKEY (1 << 0)
#define IRQ_MASK_B 0x0b
#define IRQ_MASK_C 0x0c
#define IRQ_MASK_D 0x0d
#define CONTROL_F 0x13
#define CONTROL_F_WAKE_UP (1 << 2)
#define CONTROL_F_SHUTDOWN (1 << 1)
#define COUNT_S 0x40
#define COUNT_S_COUNT_SEC 0x3f
#define COUNT_MI 0x41
#define COUNT_MI_COUNT_MIN 0x3f
#define COUNT_H 0x42
#define COUNT_H_COUNT_HOUR 0x1f
#define COUNT_D 0x43
#define COUNT_D_COUNT_DAY 0x1f
#define COUNT_MO 0x44
#define COUNT_MO_COUNT_MONTH 0x0f
#define COUNT_Y 0x45
#define COUNT_Y_MONITOR (1 << 6)
#define COUNT_Y_COUNT_YEAR 0x3f
#define ALARM_MO 0x4a
#define ALARM_MO_TICK_WAKE (1 << 5)
#define ALARM_MO_TICK_TYPE (1 << 4)
#define ALARM_Y 0x4b
#define ALARM_Y_TICK_ON (1 << 7)
#ifdef DAPMIC_DEBUG
# define DPRINTF(args) do { printf args; } while (0)
#else
# define DPRINTF(args) do {} while (0)
#endif
struct dapmic_softc {
struct device sc_dev;
i2c_tag_t sc_tag;
i2c_addr_t sc_addr;
int (*sc_ih)(void *);
struct todr_chip_handle sc_todr;
};
int dapmic_match(struct device *, void *, void *);
void dapmic_attach(struct device *, struct device *, void *);
const struct cfattach dapmic_ca = {
sizeof(struct dapmic_softc), dapmic_match, dapmic_attach
};
struct cfdriver dapmic_cd = {
NULL, "dapmic", DV_DULL
};
uint8_t dapmic_reg_read(struct dapmic_softc *, int);
void dapmic_reg_write(struct dapmic_softc *, int, uint8_t);
int dapmic_clock_read(struct dapmic_softc *, struct clock_ymdhms *);
int dapmic_clock_write(struct dapmic_softc *, struct clock_ymdhms *);
int dapmic_gettime(struct todr_chip_handle *, struct timeval *);
int dapmic_settime(struct todr_chip_handle *, struct timeval *);
void dapmic_reset_irq_mask(struct dapmic_softc *);
void dapmic_reset(void);
void dapmic_powerdown(void);
int dapmic_intr(void *);
int
dapmic_match(struct device *parent, void *match, void *aux)
{
struct i2c_attach_args *ia = aux;
return (strcmp(ia->ia_name, "dlg,da9063") == 0);
}
void
dapmic_attach(struct device *parent, struct device *self, void *aux)
{
struct dapmic_softc *sc = (struct dapmic_softc *)self;
struct i2c_attach_args *ia = aux;
int node = *(int *)ia->ia_cookie;
sc->sc_tag = ia->ia_tag;
sc->sc_addr = ia->ia_addr;
sc->sc_todr.cookie = sc;
sc->sc_todr.todr_gettime = dapmic_gettime;
sc->sc_todr.todr_settime = dapmic_settime;
sc->sc_todr.todr_quality = 0;
todr_attach(&sc->sc_todr);
if (cpuresetfn == NULL)
cpuresetfn = dapmic_reset;
if (powerdownfn == NULL)
powerdownfn = dapmic_powerdown;
dapmic_reg_write(sc, IRQ_MASK_A,
0xff & ~(IRQ_MASK_A_M_RESERVED | IRQ_MASK_A_M_nONKEY));
dapmic_reg_write(sc, IRQ_MASK_B, 0xff);
dapmic_reg_write(sc, IRQ_MASK_C, 0xff);
dapmic_reg_write(sc, IRQ_MASK_D, 0xff);
dapmic_reg_write(sc, FAULT_LOG, dapmic_reg_read(sc, FAULT_LOG));
dapmic_reg_write(sc, EVENT_A, dapmic_reg_read(sc, EVENT_A));
dapmic_reg_write(sc, EVENT_B, dapmic_reg_read(sc, EVENT_B));
dapmic_reg_write(sc, EVENT_C, dapmic_reg_read(sc, EVENT_C));
dapmic_reg_write(sc, EVENT_D, dapmic_reg_read(sc, EVENT_D));
if (node != 0) {
sc->sc_ih = fdt_intr_establish_idx(node, 0, IPL_CLOCK,
dapmic_intr, sc, sc->sc_dev.dv_xname);
if (sc->sc_ih == NULL)
printf(", can't establish interrupt");
}
printf("\n");
}
uint8_t
dapmic_reg_read(struct dapmic_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("%s: can't read register 0x%02x\n",
sc->sc_dev.dv_xname, reg);
val = 0xff;
}
return val;
}
void
dapmic_reg_write(struct dapmic_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);
}
}
int
dapmic_clock_read(struct dapmic_softc *sc, struct clock_ymdhms *dt)
{
uint8_t regs[6];
uint8_t cmd = COUNT_S;
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), regs, sizeof(regs), I2C_F_POLL);
iic_release_bus(sc->sc_tag, I2C_F_POLL);
if (error)
return error;
dt->dt_sec = (regs[0] & COUNT_S_COUNT_SEC);
dt->dt_min = (regs[1] & COUNT_MI_COUNT_MIN);
dt->dt_hour = (regs[2] & COUNT_H_COUNT_HOUR);
dt->dt_day = (regs[3] & COUNT_D_COUNT_DAY);
dt->dt_mon = (regs[4] & COUNT_MO_COUNT_MONTH);
dt->dt_year = (regs[5] & COUNT_Y_COUNT_YEAR) + 2000;
if ((regs[5] & COUNT_Y_MONITOR) == 0)
return EINVAL;
return 0;
}
int
dapmic_clock_write(struct dapmic_softc *sc, struct clock_ymdhms *dt)
{
uint8_t regs[6];
uint8_t cmd = COUNT_S;
int error;
regs[0] = dt->dt_sec;
regs[1] = dt->dt_min;
regs[2] = dt->dt_hour;
regs[3] = dt->dt_day;
regs[4] = dt->dt_mon;
regs[5] = (dt->dt_year - 2000) | COUNT_Y_MONITOR;
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), regs, sizeof(regs), I2C_F_POLL);
iic_release_bus(sc->sc_tag, I2C_F_POLL);
if (error)
return error;
return 0;
}
int
dapmic_gettime(struct todr_chip_handle *handle, struct timeval *tv)
{
struct dapmic_softc *sc = handle->cookie;
struct clock_ymdhms dt;
int error;
error = dapmic_clock_read(sc, &dt);
if (error)
return error;
if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 ||
dt.dt_day > 31 || dt.dt_day == 0 ||
dt.dt_mon > 12 || dt.dt_mon == 0 ||
dt.dt_year < POSIX_BASE_YEAR)
return EINVAL;
tv->tv_sec = clock_ymdhms_to_secs(&dt);
tv->tv_usec = 0;
return 0;
}
int
dapmic_settime(struct todr_chip_handle *handle, struct timeval *tv)
{
struct dapmic_softc *sc = handle->cookie;
struct clock_ymdhms dt;
clock_secs_to_ymdhms(tv->tv_sec, &dt);
return dapmic_clock_write(sc, &dt);
}
void
dapmic_reset_irq_mask(struct dapmic_softc *sc)
{
dapmic_reg_write(sc, IRQ_MASK_A, 0);
dapmic_reg_write(sc, IRQ_MASK_B, 0);
dapmic_reg_write(sc, IRQ_MASK_C, 0);
dapmic_reg_write(sc, IRQ_MASK_D, 0);
}
void
dapmic_reset(void)
{
struct dapmic_softc *sc = dapmic_cd.cd_devs[0];
uint8_t reg;
dapmic_reset_irq_mask(sc);
reg = dapmic_reg_read(sc, ALARM_MO);
reg &= ~ALARM_MO_TICK_TYPE;
reg |= ALARM_MO_TICK_WAKE;
dapmic_reg_write(sc, ALARM_MO, reg);
reg = dapmic_reg_read(sc, ALARM_Y);
reg |= ALARM_Y_TICK_ON;
dapmic_reg_write(sc, ALARM_Y, reg);
dapmic_reg_write(sc, EVENT_A, dapmic_reg_read(sc, EVENT_A));
dapmic_reg_write(sc, CONTROL_F, CONTROL_F_SHUTDOWN);
}
void
dapmic_powerdown(void)
{
struct dapmic_softc *sc = dapmic_cd.cd_devs[0];
uint8_t reg;
dapmic_reset_irq_mask(sc);
reg = dapmic_reg_read(sc, ALARM_Y);
reg &= ~ALARM_Y_TICK_ON;
dapmic_reg_write(sc, ALARM_Y, reg);
dapmic_reg_write(sc, CONTROL_F, CONTROL_F_SHUTDOWN);
}
int
dapmic_intr(void *arg)
{
struct dapmic_softc *sc = arg;
uint8_t event_a, event_b, event_c, event_d, fault;
event_b = event_c = event_d = 0;
event_a = dapmic_reg_read(sc, EVENT_A);
DPRINTF(("%s: %s: event_a %#02.2hhx", sc->sc_dev.dv_xname, __func__,
event_a));
if (event_a & EVENT_A_EVENTS_B) {
event_b = dapmic_reg_read(sc, EVENT_B);
DPRINTF((", event_b %#02.2hhx", event_b));
if (event_b != 0)
dapmic_reg_write(sc, EVENT_B, event_b);
}
if (event_a & EVENT_A_EVENTS_C) {
event_c = dapmic_reg_read(sc, EVENT_C);
DPRINTF((", event_c %#02.2hhx", event_c));
if (event_c != 0)
dapmic_reg_write(sc, EVENT_C, event_c);
}
if (event_a & EVENT_A_EVENTS_D) {
event_d = dapmic_reg_read(sc, EVENT_D);
DPRINTF((", event_d %#02.2hhx", event_d));
if (event_d != 0)
dapmic_reg_write(sc, EVENT_D, event_d);
}
event_a &= ~(EVENT_A_EVENTS_B|EVENT_A_EVENTS_C|EVENT_A_EVENTS_D);
if (event_a != 0)
dapmic_reg_write(sc, EVENT_A, event_a);
DPRINTF(("\n"));
fault = dapmic_reg_read(sc, FAULT_LOG);
if (fault != 0) {
static int warned;
if (!warned) {
warned = 1;
printf("%s: FAULT_LOG %#02.2hhx\n", sc->sc_dev.dv_xname,
fault);
}
}
if (event_a & EVENT_A_E_nONKEY)
powerbutton_event();
if (event_a | event_b | event_c | event_d)
return 1;
return 0;
}