// 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 <rvinson@mvista.com>, * which was based on the m41t00.c by Mark Greer <mgreer@mvista.com>. * * 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.
*/
struct ds1374 { struct i2c_client *client; struct rtc_device *rtc; struct work_struct work; #ifdef CONFIG_RTC_DRV_DS1374_WDT struct watchdog_device wdt; #endif /* The mutex protects alarm operations, and prevents a race * between the enable_irq() in the workqueue and the free_irq() * in the remove function.
*/ struct mutex mutex; int exiting;
};
staticstruct i2c_driver ds1374_driver;
staticint ds1374_read_rtc(struct i2c_client *client, u32 *time, int reg, int nbytes)
{
u8 buf[4]; int ret; int i;
if (WARN_ON(nbytes > 4)) return -EINVAL;
ret = i2c_smbus_read_i2c_block_data(client, reg, nbytes, buf);
if (ret < 0) return ret; if (ret < nbytes) return -EIO;
for (i = nbytes - 1, *time = 0; i >= 0; i--)
*time = (*time << 8) | buf[i];
return 0;
}
staticint ds1374_write_rtc(struct i2c_client *client, u32 time, int reg, int nbytes)
{
u8 buf[4]; int i;
if (nbytes > 4) {
WARN_ON(1); return -EINVAL;
}
for (i = 0; i < nbytes; i++) {
buf[i] = time & 0xff;
time >>= 8;
}
#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.
*/ staticint 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;
/* 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;
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);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.