linux/drivers/rtc/rtc-ds1374.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * RTC client/driver for the Maxim/Dallas DS1374 Real-Time Clock over I2C
 *
 * Based on code by Randy Vinson <[email protected]>,
 * which was based on the m41t00.c by Mark Greer <[email protected]>.
 *
 * Copyright (C) 2014 Rose Technology
 * Copyright (C) 2006-2007 Freescale Semiconductor
 * Copyright (c) 2005 MontaVista Software, Inc.
 */
/*
 * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
 * recommended in .../Documentation/i2c/writing-clients.rst section
 * "Sending and receiving", using SMBus level communication is preferred.
 */

#define pr_fmt(fmt)

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/pm.h>
#ifdef CONFIG_RTC_DRV_DS1374_WDT
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/miscdevice.h>
#include <linux/reboot.h>
#include <linux/watchdog.h>
#endif

#define DS1374_REG_TOD0
#define DS1374_REG_TOD1
#define DS1374_REG_TOD2
#define DS1374_REG_TOD3
#define DS1374_REG_WDALM0
#define DS1374_REG_WDALM1
#define DS1374_REG_WDALM2
#define DS1374_REG_CR
#define DS1374_REG_CR_AIE
#define DS1374_REG_CR_WDSTR
#define DS1374_REG_CR_WDALM
#define DS1374_REG_CR_WACE
#define DS1374_REG_SR
#define DS1374_REG_SR_OSF
#define DS1374_REG_SR_AF
#define DS1374_REG_TCR

static const struct i2c_device_id ds1374_id[] =;
MODULE_DEVICE_TABLE(i2c, ds1374_id);

#ifdef CONFIG_OF
static const struct of_device_id ds1374_of_match[] =;
MODULE_DEVICE_TABLE(of, ds1374_of_match);
#endif

struct ds1374 {};

static struct i2c_driver ds1374_driver;

static int ds1374_read_rtc(struct i2c_client *client, u32 *time,
			   int reg, int nbytes)
{}

static int ds1374_write_rtc(struct i2c_client *client, u32 time,
			    int reg, int nbytes)
{}

static int ds1374_check_rtc_status(struct i2c_client *client)
{}

static int ds1374_read_time(struct device *dev, struct rtc_time *time)
{}

static int ds1374_set_time(struct device *dev, struct rtc_time *time)
{}

#ifndef CONFIG_RTC_DRV_DS1374_WDT
/* The ds1374 has a decrementer for an alarm, rather than a comparator.
 * If the time of day is changed, then the alarm will need to be
 * reset.
 */
static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct ds1374 *ds1374 = i2c_get_clientdata(client);
	u32 now, cur_alarm;
	int cr, sr;
	int ret = 0;

	if (client->irq <= 0)
		return -EINVAL;

	mutex_lock(&ds1374->mutex);

	cr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
	if (ret < 0)
		goto out;

	sr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
	if (ret < 0)
		goto out;

	ret = ds1374_read_rtc(client, &now, DS1374_REG_TOD0, 4);
	if (ret)
		goto out;

	ret = ds1374_read_rtc(client, &cur_alarm, DS1374_REG_WDALM0, 3);
	if (ret)
		goto out;

	rtc_time64_to_tm(now + cur_alarm, &alarm->time);
	alarm->enabled = !!(cr & DS1374_REG_CR_WACE);
	alarm->pending = !!(sr & DS1374_REG_SR_AF);

out:
	mutex_unlock(&ds1374->mutex);
	return ret;
}

static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct ds1374 *ds1374 = i2c_get_clientdata(client);
	struct rtc_time now;
	unsigned long new_alarm, itime;
	int cr;
	int ret = 0;

	if (client->irq <= 0)
		return -EINVAL;

	ret = ds1374_read_time(dev, &now);
	if (ret < 0)
		return ret;

	new_alarm = rtc_tm_to_time64(&alarm->time);
	itime = rtc_tm_to_time64(&now);

	/* This can happen due to races, in addition to dates that are
	 * truly in the past.  To avoid requiring the caller to check for
	 * races, dates in the past are assumed to be in the recent past
	 * (i.e. not something that we'd rather the caller know about via
	 * an error), and the alarm is set to go off as soon as possible.
	 */
	if (time_before_eq(new_alarm, itime))
		new_alarm = 1;
	else
		new_alarm -= itime;

	mutex_lock(&ds1374->mutex);

	ret = cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
	if (ret < 0)
		goto out;

	/* Disable any existing alarm before setting the new one
	 * (or lack thereof). */
	cr &= ~DS1374_REG_CR_WACE;

	ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
	if (ret < 0)
		goto out;

	ret = ds1374_write_rtc(client, new_alarm, DS1374_REG_WDALM0, 3);
	if (ret)
		goto out;

	if (alarm->enabled) {
		cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
		cr &= ~DS1374_REG_CR_WDALM;

		ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
	}

out:
	mutex_unlock(&ds1374->mutex);
	return ret;
}
#endif

static irqreturn_t ds1374_irq(int irq, void *dev_id)
{}

static void ds1374_work(struct work_struct *work)
{}

#ifndef CONFIG_RTC_DRV_DS1374_WDT
static int ds1374_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct ds1374 *ds1374 = i2c_get_clientdata(client);
	int ret;

	mutex_lock(&ds1374->mutex);

	ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
	if (ret < 0)
		goto out;

	if (enabled) {
		ret |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
		ret &= ~DS1374_REG_CR_WDALM;
	} else {
		ret &= ~DS1374_REG_CR_WACE;
	}
	ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, ret);

out:
	mutex_unlock(&ds1374->mutex);
	return ret;
}
#endif

static const struct rtc_class_ops ds1374_rtc_ops =;

#ifdef CONFIG_RTC_DRV_DS1374_WDT
/*
 *****************************************************************************
 *
 * Watchdog Driver
 *
 *****************************************************************************
 */
/* Default margin */
#define TIMER_MARGIN_DEFAULT
#define TIMER_MARGIN_MIN
#define TIMER_MARGIN_MAX

static int wdt_margin;
module_param(wdt_margin, int, 0);
MODULE_PARM_DESC();

static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC();

static const struct watchdog_info ds1374_wdt_info =;

static int ds1374_wdt_settimeout(struct watchdog_device *wdt, unsigned int timeout)
{}

/*
 * Reload the watchdog timer.  (ie, pat the watchdog)
 */
static int ds1374_wdt_start(struct watchdog_device *wdt)
{}

static int ds1374_wdt_stop(struct watchdog_device *wdt)
{}

static const struct watchdog_ops ds1374_wdt_ops =;
#endif /*CONFIG_RTC_DRV_DS1374_WDT*/
/*
 *****************************************************************************
 *
 *	Driver Interface
 *
 *****************************************************************************
 */
static int ds1374_probe(struct i2c_client *client)
{}

static void ds1374_remove(struct i2c_client *client)
{}

#ifdef CONFIG_PM_SLEEP
static int ds1374_suspend(struct device *dev)
{}

static int ds1374_resume(struct device *dev)
{}
#endif

static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);

static struct i2c_driver ds1374_driver =;

module_i2c_driver();

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();