#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/clock.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/module.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/iicbus/iiconf.h>
#include <dev/iicbus/iicbus.h>
#include "clock_if.h"
#include "iicbus_if.h"
#define RS5C372_REG_SEC 0x0
#define RS5C372_REG_MIN 0x1
#define RS5C372_REG_HOUR 0x2
#define HOUR_HR_M 0x1f
#define HOUR_PM 0x20
#define RS5C372_REG_DOW 0x3
#define RS5C372_REG_DAY 0x4
#define RS5C372_REG_MON 0x5
#define RS5C372_REG_YEAR 0x6
#define RS5C372_REG_CTRL1 0xe
#define RS5C372_REG_CTRL2 0xf
#define CTRL_PM 0x20
static struct ofw_compat_data compat_data[] = {
{ "ricoh,rs5c372a", 1 },
{ NULL, 0 }
};
static int
rs5c372a_gettime(device_t dev, struct timespec *ts)
{
struct bcd_clocktime ct = {};
uint8_t clock_regs[7];
int err;
uint8_t ctrl2;
bool is_12hr = true;
err = iicdev_readfrom(dev, RS5C372_REG_CTRL2, &ctrl2,
sizeof(ctrl2), IIC_WAIT);
if (err != 0)
return (err);
err = iicdev_readfrom(dev, RS5C372_REG_SEC, clock_regs,
sizeof(clock_regs), IIC_WAIT);
if (err != 0)
return (err);
if (ctrl2 & CTRL_PM)
is_12hr = false;
ct.sec = clock_regs[RS5C372_REG_SEC];
ct.min = clock_regs[RS5C372_REG_MIN];
ct.hour = clock_regs[RS5C372_REG_HOUR];
ct.dow = clock_regs[RS5C372_REG_DOW];
ct.day = clock_regs[RS5C372_REG_DAY];
ct.mon = clock_regs[RS5C372_REG_MON];
ct.year = clock_regs[RS5C372_REG_YEAR];
if (is_12hr) {
ct.ispm = ct.hour & HOUR_PM;
ct.hour &= HOUR_HR_M;
}
clock_bcd_to_ts(&ct, ts, ct.ispm);
return (0);
}
static int
rs5c372a_settime(device_t dev, struct timespec *ts)
{
struct bcd_clocktime ct;
uint8_t clock_regs[7];
uint8_t ctrl2;
int err;
bool is_12hr = true;
err = iicdev_readfrom(dev, RS5C372_REG_CTRL2, &ctrl2,
sizeof(ctrl2), IIC_WAIT);
if (err != 0)
return (err);
if (ctrl2 & CTRL_PM)
is_12hr = false;
clock_ts_to_bcd(ts, &ct, is_12hr);
clock_regs[RS5C372_REG_SEC] = ct.sec;
clock_regs[RS5C372_REG_MIN] = ct.min;
clock_regs[RS5C372_REG_HOUR] = ct.hour;
clock_regs[RS5C372_REG_DAY] = ct.day;
clock_regs[RS5C372_REG_DOW] = ct.dow;
clock_regs[RS5C372_REG_MON] = ct.mon;
clock_regs[RS5C372_REG_YEAR] = ct.year & 0xff;
if (is_12hr) {
if (ct.ispm)
clock_regs[RS5C372_REG_HOUR] |= HOUR_PM;
}
err = iicdev_writeto(dev, RS5C372_REG_SEC, clock_regs,
sizeof(clock_regs), IIC_WAIT);
return (err);
}
static int
rs5c372a_probe(device_t dev)
{
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
return (ENXIO);
device_set_desc(dev, "Richo RS5C372A RTC");
return (BUS_PROBE_DEFAULT);
}
static int
rs5c372a_attach(device_t dev)
{
clock_register(dev, 1000000);
clock_schedule(dev, 1);
return (0);
}
static device_method_t rs5c372a_methods[] = {
DEVMETHOD(device_probe, rs5c372a_probe),
DEVMETHOD(device_attach, rs5c372a_attach),
DEVMETHOD(clock_gettime, rs5c372a_gettime),
DEVMETHOD(clock_settime, rs5c372a_settime),
DEVMETHOD_END
};
DEFINE_CLASS_0(rs5c372a, rs5c372a_driver, rs5c372a_methods, 0);
DRIVER_MODULE(rs5c372a, iicbus, rs5c372a_driver, NULL, NULL);
MODULE_VERSION(rs5c372a, 1);
MODULE_DEPEND(rs5c372a, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
IICBUS_FDT_PNP_INFO(compat_data);