#include <linux/bcd.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include <linux/clk-provider.h>
#include <linux/watchdog.h>
#define M41T93_REG_SSEC 0
#define M41T93_REG_ST_SEC 1
#define M41T93_REG_MIN 2
#define M41T93_REG_CENT_HOUR 3
#define M41T93_REG_WDAY 4
#define M41T93_REG_DAY 5
#define M41T93_REG_MON 6
#define M41T93_REG_YEAR 7
#define M41T93_REG_AL1_MONTH 0xa
#define M41T93_REG_AL1_DATE 0xb
#define M41T93_REG_AL1_HOUR 0xc
#define M41T93_REG_AL1_MIN 0xd
#define M41T93_REG_AL1_SEC 0xe
#define M41T93_BIT_A1IE BIT(7)
#define M41T93_BIT_ABE BIT(5)
#define M41T93_FLAG_AF1 BIT(6)
#define M41T93_SRAM_BASE 0x19
#define M41T93_REG_SQW 0x13
#define M41T93_SQW_RS_MASK 0xf0
#define M41T93_SQW_RS_SHIFT 4
#define M41T93_BIT_SQWE BIT(6)
#define M41T93_REG_WATCHDOG 0x9
#define M41T93_WDT_RB_MASK 0x3
#define M41T93_WDT_BMB_MASK 0x7c
#define M41T93_WDT_BMB_SHIFT 2
#define M41T93_REG_ALM_HOUR_HT 0xc
#define M41T93_REG_FLAGS 0xf
#define M41T93_FLAG_ST (1 << 7)
#define M41T93_FLAG_OF (1 << 2)
#define M41T93_FLAG_BL (1 << 4)
#define M41T93_FLAG_HT (1 << 6)
struct m41t93_data {
struct rtc_device *rtc;
struct regmap *regmap;
#ifdef CONFIG_COMMON_CLK
struct clk_hw clks;
#endif
#ifdef CONFIG_WATCHDOG
struct watchdog_device wdd;
#endif
};
static int m41t93_set_time(struct device *dev, struct rtc_time *tm)
{
struct m41t93_data *m41t93 = dev_get_drvdata(dev);
int tmp, ret;
u8 buf[8] = {0};
u8 * const data = &buf[0];
dev_dbg(dev, "%s secs=%d, mins=%d, "
"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
"write", tm->tm_sec, tm->tm_min,
tm->tm_hour, tm->tm_mday,
tm->tm_mon, tm->tm_year, tm->tm_wday);
if (tm->tm_year < 100) {
dev_warn(dev, "unsupported date (before 2000-01-01).\n");
return -EINVAL;
}
ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &tmp);
if (ret < 0)
return ret;
if (tmp & M41T93_FLAG_OF) {
dev_warn(dev, "OF bit is set, resetting.\n");
regmap_write(m41t93->regmap, M41T93_REG_FLAGS, tmp & ~M41T93_FLAG_OF);
ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &tmp);
if (ret < 0) {
return ret;
} else if (tmp & M41T93_FLAG_OF) {
u8 reset_osc = buf[M41T93_REG_ST_SEC] | M41T93_FLAG_ST;
dev_warn(dev,
"OF bit is still set, kickstarting clock.\n");
regmap_write(m41t93->regmap, M41T93_REG_ST_SEC, reset_osc);
reset_osc &= ~M41T93_FLAG_ST;
regmap_write(m41t93->regmap, M41T93_REG_ST_SEC, reset_osc);
}
}
data[M41T93_REG_SSEC] = 0;
data[M41T93_REG_ST_SEC] = bin2bcd(tm->tm_sec);
data[M41T93_REG_MIN] = bin2bcd(tm->tm_min);
data[M41T93_REG_CENT_HOUR] = bin2bcd(tm->tm_hour) |
((tm->tm_year/100-1) << 6);
data[M41T93_REG_DAY] = bin2bcd(tm->tm_mday);
data[M41T93_REG_WDAY] = bin2bcd(tm->tm_wday + 1);
data[M41T93_REG_MON] = bin2bcd(tm->tm_mon + 1);
data[M41T93_REG_YEAR] = bin2bcd(tm->tm_year % 100);
return regmap_bulk_write(m41t93->regmap, M41T93_REG_SSEC, buf, sizeof(buf));
}
static int m41t93_get_time(struct device *dev, struct rtc_time *tm)
{
struct m41t93_data *m41t93 = dev_get_drvdata(dev);
u8 buf[8];
int century_after_1900;
int tmp;
int ret = 0;
ret = regmap_read(m41t93->regmap, M41T93_REG_ALM_HOUR_HT, &tmp);
if (ret < 0)
return ret;
if (tmp & M41T93_FLAG_HT) {
dev_dbg(dev, "HT bit is set, reenable clock update.\n");
regmap_write(m41t93->regmap, M41T93_REG_ALM_HOUR_HT,
tmp & ~M41T93_FLAG_HT);
}
ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &tmp);
if (ret < 0)
return ret;
if (tmp & M41T93_FLAG_OF) {
ret = -EINVAL;
dev_warn(dev, "OF bit is set, write time to restart.\n");
}
if (tmp & M41T93_FLAG_BL)
dev_warn(dev, "BL bit is set, replace battery.\n");
ret = regmap_bulk_read(m41t93->regmap, M41T93_REG_SSEC, buf, sizeof(buf));
if (ret < 0)
return ret;
tm->tm_sec = bcd2bin(buf[M41T93_REG_ST_SEC]);
tm->tm_min = bcd2bin(buf[M41T93_REG_MIN]);
tm->tm_hour = bcd2bin(buf[M41T93_REG_CENT_HOUR] & 0x3f);
tm->tm_mday = bcd2bin(buf[M41T93_REG_DAY]);
tm->tm_mon = bcd2bin(buf[M41T93_REG_MON]) - 1;
tm->tm_wday = bcd2bin(buf[M41T93_REG_WDAY] & 0x0f) - 1;
century_after_1900 = (buf[M41T93_REG_CENT_HOUR] >> 6) + 1;
tm->tm_year = bcd2bin(buf[M41T93_REG_YEAR]) + century_after_1900 * 100;
dev_dbg(dev, "%s secs=%d, mins=%d, "
"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
"read", tm->tm_sec, tm->tm_min,
tm->tm_hour, tm->tm_mday,
tm->tm_mon, tm->tm_year, tm->tm_wday);
return ret;
}
static int m41t93_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct m41t93_data *m41t93 = dev_get_drvdata(dev);
int ret;
unsigned int val;
u8 alarm_vals[5] = {0};
ret = regmap_bulk_write(m41t93->regmap, M41T93_REG_AL1_DATE, alarm_vals, 4);
if (ret)
return ret;
alarm_vals[0] = bin2bcd(alrm->time.tm_mon + 1) & 0x1f;
alarm_vals[1] = bin2bcd(alrm->time.tm_mday) & 0x3f;
alarm_vals[2] = bin2bcd(alrm->time.tm_hour) & 0x3f;
alarm_vals[3] = bin2bcd(alrm->time.tm_min) & 0x7f;
alarm_vals[4] = bin2bcd(alrm->time.tm_sec) & 0x7f;
if (alrm->enabled) {
alarm_vals[0] |= M41T93_BIT_A1IE | M41T93_BIT_ABE;
}
ret = regmap_read(m41t93->regmap, M41T93_REG_AL1_MONTH, &val);
if (ret)
return ret;
alarm_vals[0] |= val & 0x40;
ret = regmap_bulk_write(m41t93->regmap, M41T93_REG_AL1_MONTH,
alarm_vals, sizeof(alarm_vals));
if (ret)
return ret;
ret = regmap_read(m41t93->regmap, M41T93_REG_AL1_MONTH, &val);
if (ret)
return ret;
if (bcd2bin(val & 0x1f) == (alrm->time.tm_mon & 0x1f))
dev_notice(dev, "Alarm set successfully\n");
return 0;
}
static int m41t93_get_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct m41t93_data *m41t93 = dev_get_drvdata(dev);
int ret;
unsigned int val;
u8 alarm_vals[5] = {0};
ret = regmap_bulk_read(m41t93->regmap, M41T93_REG_AL1_MONTH,
alarm_vals, sizeof(alarm_vals));
if (ret)
return ret;
alrm->time.tm_mon = bcd2bin(alarm_vals[0] & 0x1f) - 1;
alrm->time.tm_mday = bcd2bin(alarm_vals[1] & 0x3f);
alrm->time.tm_hour = bcd2bin(alarm_vals[2] & 0x3f);
alrm->time.tm_min = bcd2bin(alarm_vals[3] & 0x7f);
alrm->time.tm_sec = bcd2bin(alarm_vals[4] & 0x7f);
alrm->enabled = !!(alarm_vals[0] & M41T93_BIT_A1IE);
ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &val);
if (ret)
return ret;
alrm->pending = (val & M41T93_FLAG_AF1) && alrm->enabled;
return 0;
}
static int m41t93_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct m41t93_data *m41t93 = dev_get_drvdata(dev);
unsigned int val;
int ret;
val = enabled ? M41T93_BIT_A1IE | M41T93_BIT_ABE : 0;
ret = regmap_update_bits(m41t93->regmap, M41T93_REG_AL1_MONTH,
M41T93_BIT_A1IE | M41T93_BIT_ABE, val);
if (ret)
return ret;
return 0;
}
static const struct rtc_class_ops m41t93_rtc_ops = {
.read_time = m41t93_get_time,
.set_time = m41t93_set_time,
.set_alarm = m41t93_set_alarm,
.read_alarm = m41t93_get_alarm,
.alarm_irq_enable = m41t93_alarm_irq_enable,
};
#ifdef CONFIG_COMMON_CLK
#define clk_sqw_to_m41t93_data(clk) \
container_of(clk, struct m41t93_data, clks)
static unsigned long m41t93_clk_rates[] = {
0,
32768,
8192,
4096,
2048,
1024,
512,
256,
128,
64,
32,
16,
8,
4,
2,
1,
};
static unsigned long m41t93_clk_sqw_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
int ret;
unsigned int rate_id;
struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw);
ret = regmap_read(m41t93->regmap, M41T93_REG_SQW, &rate_id);
if (ret)
return ret;
rate_id &= M41T93_SQW_RS_MASK;
rate_id >>= M41T93_SQW_RS_SHIFT;
return m41t93_clk_rates[rate_id];
}
static int m41t93_clk_sqw_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
int i;
for (i = 1; i < ARRAY_SIZE(m41t93_clk_rates); i++) {
if (req->rate >= m41t93_clk_rates[i]) {
req->rate = m41t93_clk_rates[i];
return 0;
}
}
return 0;
}
static int m41t93_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
int id, ret;
struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw);
for (id = 0; id < ARRAY_SIZE(m41t93_clk_rates); id++) {
if (m41t93_clk_rates[id] == rate)
break;
}
if (id >= ARRAY_SIZE(m41t93_clk_rates))
return -EINVAL;
ret = regmap_update_bits(m41t93->regmap, M41T93_REG_SQW,
M41T93_SQW_RS_MASK, id << M41T93_SQW_RS_SHIFT);
return ret;
}
static int m41t93_clk_sqw_prepare(struct clk_hw *hw)
{
int ret;
struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw);
ret = regmap_update_bits(m41t93->regmap, M41T93_REG_AL1_MONTH,
M41T93_BIT_SQWE, M41T93_BIT_SQWE);
return ret;
}
static void m41t93_clk_sqw_unprepare(struct clk_hw *hw)
{
struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw);
regmap_update_bits(m41t93->regmap, M41T93_REG_AL1_MONTH,
M41T93_BIT_SQWE, 0);
}
static int m41t93_clk_sqw_is_prepared(struct clk_hw *hw)
{
int ret;
struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw);
unsigned int status;
ret = regmap_read(m41t93->regmap, M41T93_REG_AL1_MONTH, &status);
if (ret)
return ret;
return !!(status & M41T93_BIT_SQWE);
}
static const struct clk_ops m41t93_clk_sqw_ops = {
.prepare = m41t93_clk_sqw_prepare,
.unprepare = m41t93_clk_sqw_unprepare,
.is_prepared = m41t93_clk_sqw_is_prepared,
.recalc_rate = m41t93_clk_sqw_recalc_rate,
.set_rate = m41t93_clk_sqw_set_rate,
.determine_rate = m41t93_clk_sqw_determine_rate,
};
static int rtc_m41t93_clks_register(struct device *dev, struct m41t93_data *m41t93)
{
struct device_node *node = dev->of_node;
struct clk *clk;
struct clk_init_data init = {0};
init.name = "m41t93_clk_sqw";
init.ops = &m41t93_clk_sqw_ops;
m41t93->clks.init = &init;
clk = devm_clk_register(dev, &m41t93->clks);
if (IS_ERR(clk))
return PTR_ERR(clk);
if (node)
of_clk_add_provider(node, of_clk_src_simple_get, clk);
return 0;
}
#endif
#ifdef CONFIG_WATCHDOG
static int m41t93_wdt_ping(struct watchdog_device *wdd)
{
u8 resolution, mult;
u8 val = 0;
int ret;
struct m41t93_data *m41t93 = watchdog_get_drvdata(wdd);
resolution = 0x2;
mult = wdd->timeout;
val = resolution | (mult << M41T93_WDT_BMB_SHIFT & M41T93_WDT_BMB_MASK);
ret = regmap_write_bits(m41t93->regmap, M41T93_REG_WATCHDOG,
M41T93_WDT_RB_MASK | M41T93_WDT_BMB_MASK, val);
return ret;
}
static int m41t93_wdt_start(struct watchdog_device *wdd)
{
return m41t93_wdt_ping(wdd);
}
static int m41t93_wdt_stop(struct watchdog_device *wdd)
{
struct m41t93_data *m41t93 = watchdog_get_drvdata(wdd);
return regmap_write_bits(m41t93->regmap, M41T93_REG_WATCHDOG,
M41T93_WDT_RB_MASK | M41T93_WDT_BMB_MASK, 0);
}
static int m41t93_wdt_set_timeout(struct watchdog_device *wdd,
unsigned int new_timeout)
{
wdd->timeout = new_timeout;
return 0;
}
static const struct watchdog_info m41t93_wdt_info = {
.identity = "m41t93 rtc Watchdog",
.options = WDIOF_ALARMONLY | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
};
static const struct watchdog_ops m41t93_watchdog_ops = {
.owner = THIS_MODULE,
.start = m41t93_wdt_start,
.stop = m41t93_wdt_stop,
.ping = m41t93_wdt_ping,
.set_timeout = m41t93_wdt_set_timeout,
};
static int m41t93_watchdog_register(struct device *dev, struct m41t93_data *m41t93)
{
int ret;
m41t93->wdd.parent = dev;
m41t93->wdd.info = &m41t93_wdt_info;
m41t93->wdd.ops = &m41t93_watchdog_ops;
m41t93->wdd.min_timeout = 0;
m41t93->wdd.max_timeout = 10;
m41t93->wdd.timeout = 3;
m41t93->wdd.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
watchdog_set_drvdata(&m41t93->wdd, m41t93);
ret = devm_watchdog_register_device(dev, &m41t93->wdd);
if (ret) {
dev_warn(dev, "Failed to register watchdog\n");
return ret;
}
ret = m41t93_wdt_stop(&m41t93->wdd);
return ret;
}
#endif
static struct spi_driver m41t93_driver;
static const struct regmap_config regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.read_flag_mask = 0x00,
.write_flag_mask = 0x80,
.zero_flag_mask = true,
};
static int m41t93_probe(struct spi_device *spi)
{
int res, ret;
struct m41t93_data *m41t93;
spi->bits_per_word = 8;
spi_setup(spi);
m41t93 = devm_kzalloc(&spi->dev, sizeof(struct m41t93_data), GFP_KERNEL);
if (!m41t93)
return -ENOMEM;
m41t93->regmap = devm_regmap_init_spi(spi, ®map_config);
if (IS_ERR(m41t93->regmap)) {
dev_err(&spi->dev, "regmap init failure\n");
return PTR_ERR(m41t93->regmap);
}
ret = regmap_read(m41t93->regmap, M41T93_REG_WDAY, &res);
if (ret < 0) {
dev_err(&spi->dev, "IO error\n");
return -EIO;
}
if (res < 0 || (res & 0xf8) != 0) {
dev_err(&spi->dev, "not found 0x%x.\n", res);
return -ENODEV;
}
spi_set_drvdata(spi, m41t93);
m41t93->rtc = devm_rtc_device_register(&spi->dev, m41t93_driver.driver.name,
&m41t93_rtc_ops, THIS_MODULE);
if (IS_ERR(m41t93->rtc))
return PTR_ERR(m41t93->rtc);
#ifdef CONFIG_COMMON_CLK
ret = rtc_m41t93_clks_register(&spi->dev, m41t93);
if (ret)
dev_warn(&spi->dev, "Unable to register clock\n");
#endif
#ifdef CONFIG_WATCHDOG
ret = m41t93_watchdog_register(&spi->dev, m41t93);
if (ret)
dev_warn(&spi->dev, "Unable to register watchdog\n");
#endif
return 0;
}
static const struct of_device_id m41t93_dt_match[] = {
{ .compatible = "st,m41t93" },
{ }
};
MODULE_DEVICE_TABLE(of, m41t93_dt_match);
static struct spi_driver m41t93_driver = {
.driver = {
.name = "rtc-m41t93",
.of_match_table = m41t93_dt_match,
},
.probe = m41t93_probe,
};
module_spi_driver(m41t93_driver);
MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
MODULE_DESCRIPTION("Driver for ST M41T93 SPI RTC");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:rtc-m41t93");