linux/drivers/rtc/rtc-isl12022.c

625 lines
16 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
/*
* An I2C driver for the Intersil ISL 12022
*
* Author: Roman Fietze <roman.fietze@telemotive.de>
*
* Based on the Philips PCF8563 RTC
* by Alessandro Zummo <a.zummo@towertech.it>.
*/
#include <linux/bcd.h>
#include <linux/bitfield.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#include <asm/byteorder.h>
/* RTC - Real time clock registers */
#define ISL12022_REG_SC 0x00
#define ISL12022_REG_MN 0x01
#define ISL12022_REG_HR 0x02
#define ISL12022_REG_DT 0x03
#define ISL12022_REG_MO 0x04
#define ISL12022_REG_YR 0x05
#define ISL12022_REG_DW 0x06
/* CSR - Control and status registers */
#define ISL12022_REG_SR 0x07
#define ISL12022_REG_INT 0x08
#define ISL12022_REG_PWR_VBAT 0x0a
#define ISL12022_REG_BETA 0x0d
/* ALARM - Alarm registers */
#define ISL12022_REG_SCA0 0x10
#define ISL12022_REG_MNA0 0x11
#define ISL12022_REG_HRA0 0x12
#define ISL12022_REG_DTA0 0x13
#define ISL12022_REG_MOA0 0x14
#define ISL12022_REG_DWA0 0x15
#define ISL12022_ALARM ISL12022_REG_SCA0
#define ISL12022_ALARM_LEN (ISL12022_REG_DWA0 - ISL12022_REG_SCA0 + 1)
/* TEMP - Temperature sensor registers */
#define ISL12022_REG_TEMP_L 0x28
/* ISL register bits */
#define ISL12022_HR_MIL (1 << 7) /* military or 24 hour time */
#define ISL12022_SR_ALM (1 << 4)
#define ISL12022_SR_LBAT85 (1 << 2)
#define ISL12022_SR_LBAT75 (1 << 1)
#define ISL12022_INT_ARST (1 << 7)
#define ISL12022_INT_WRTC (1 << 6)
#define ISL12022_INT_IM (1 << 5)
#define ISL12022_INT_FOBATB (1 << 4)
#define ISL12022_INT_FO_MASK GENMASK(3, 0)
#define ISL12022_INT_FO_OFF 0x0
#define ISL12022_INT_FO_32K 0x1
#define ISL12022_REG_VB85_MASK GENMASK(5, 3)
#define ISL12022_REG_VB75_MASK GENMASK(2, 0)
#define ISL12022_ALARM_ENABLE (1 << 7) /* for all ALARM registers */
#define ISL12022_BETA_TSE (1 << 7)
static struct i2c_driver isl12022_driver;
struct isl12022 {
struct rtc_device *rtc;
struct regmap *regmap;
int irq;
bool irq_enabled;
};
static umode_t isl12022_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
if (type == hwmon_temp && attr == hwmon_temp_input)
return 0444;
return 0;
}
/*
* A user-initiated temperature conversion is not started by this function,
* so the temperature is updated once every ~60 seconds.
*/
static int isl12022_hwmon_read_temp(struct device *dev, long *mC)
{
struct regmap *regmap = dev_get_drvdata(dev);
int temp, ret;
__le16 buf;
ret = regmap_bulk_read(regmap, ISL12022_REG_TEMP_L, &buf, sizeof(buf));
if (ret)
return ret;
/*
* Temperature is represented as a 10-bit number, unit half-Kelvins.
*/
temp = le16_to_cpu(buf);
temp *= 500;
temp -= 273000;
*mC = temp;
return 0;
}
static int isl12022_hwmon_read(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
if (type == hwmon_temp && attr == hwmon_temp_input)
return isl12022_hwmon_read_temp(dev, val);
return -EOPNOTSUPP;
}
static const struct hwmon_channel_info * const isl12022_hwmon_info[] = {
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
NULL
};
static const struct hwmon_ops isl12022_hwmon_ops = {
.is_visible = isl12022_hwmon_is_visible,
.read = isl12022_hwmon_read,
};
static const struct hwmon_chip_info isl12022_hwmon_chip_info = {
.ops = &isl12022_hwmon_ops,
.info = isl12022_hwmon_info,
};
static void isl12022_hwmon_register(struct device *dev)
{
struct isl12022 *isl12022 = dev_get_drvdata(dev);
struct regmap *regmap = isl12022->regmap;
struct device *hwmon;
int ret;
if (!IS_REACHABLE(CONFIG_HWMON))
return;
ret = regmap_update_bits(regmap, ISL12022_REG_BETA,
ISL12022_BETA_TSE, ISL12022_BETA_TSE);
if (ret) {
dev_warn(dev, "unable to enable temperature sensor\n");
return;
}
hwmon = devm_hwmon_device_register_with_info(dev, "isl12022", regmap,
&isl12022_hwmon_chip_info,
NULL);
if (IS_ERR(hwmon))
dev_warn(dev, "unable to register hwmon device: %pe\n", hwmon);
}
/*
* In the routines that deal directly with the isl12022 hardware, we use
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
*/
static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct isl12022 *isl12022 = dev_get_drvdata(dev);
struct regmap *regmap = isl12022->regmap;
u8 buf[ISL12022_REG_INT + 1];
int ret;
ret = regmap_bulk_read(regmap, ISL12022_REG_SC, buf, sizeof(buf));
if (ret)
return ret;
dev_dbg(dev,
"raw data is sec=%02x, min=%02x, hr=%02x, mday=%02x, mon=%02x, year=%02x, wday=%02x, sr=%02x, int=%02x",
buf[ISL12022_REG_SC],
buf[ISL12022_REG_MN],
buf[ISL12022_REG_HR],
buf[ISL12022_REG_DT],
buf[ISL12022_REG_MO],
buf[ISL12022_REG_YR],
buf[ISL12022_REG_DW],
buf[ISL12022_REG_SR],
buf[ISL12022_REG_INT]);
tm->tm_sec = bcd2bin(buf[ISL12022_REG_SC] & 0x7F);
tm->tm_min = bcd2bin(buf[ISL12022_REG_MN] & 0x7F);
tm->tm_hour = bcd2bin(buf[ISL12022_REG_HR] & 0x3F);
tm->tm_mday = bcd2bin(buf[ISL12022_REG_DT] & 0x3F);
tm->tm_wday = buf[ISL12022_REG_DW] & 0x07;
tm->tm_mon = bcd2bin(buf[ISL12022_REG_MO] & 0x1F) - 1;
tm->tm_year = bcd2bin(buf[ISL12022_REG_YR]) + 100;
dev_dbg(dev, "%s: %ptR\n", __func__, tm);
return 0;
}
static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct isl12022 *isl12022 = dev_get_drvdata(dev);
struct regmap *regmap = isl12022->regmap;
int ret;
u8 buf[ISL12022_REG_DW + 1];
dev_dbg(dev, "%s: %ptR\n", __func__, tm);
/* Ensure the write enable bit is set. */
ret = regmap_update_bits(regmap, ISL12022_REG_INT,
ISL12022_INT_WRTC, ISL12022_INT_WRTC);
if (ret)
return ret;
/* hours, minutes and seconds */
buf[ISL12022_REG_SC] = bin2bcd(tm->tm_sec);
buf[ISL12022_REG_MN] = bin2bcd(tm->tm_min);
buf[ISL12022_REG_HR] = bin2bcd(tm->tm_hour) | ISL12022_HR_MIL;
buf[ISL12022_REG_DT] = bin2bcd(tm->tm_mday);
/* month, 1 - 12 */
buf[ISL12022_REG_MO] = bin2bcd(tm->tm_mon + 1);
/* year and century */
buf[ISL12022_REG_YR] = bin2bcd(tm->tm_year % 100);
buf[ISL12022_REG_DW] = tm->tm_wday & 0x07;
return regmap_bulk_write(regmap, ISL12022_REG_SC, buf, sizeof(buf));
}
static int isl12022_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct rtc_time *tm = &alarm->time;
struct isl12022 *isl12022 = dev_get_drvdata(dev);
struct regmap *regmap = isl12022->regmap;
u8 buf[ISL12022_ALARM_LEN];
unsigned int i, yr;
int ret;
ret = regmap_bulk_read(regmap, ISL12022_ALARM, buf, sizeof(buf));
if (ret) {
dev_dbg(dev, "%s: reading ALARM registers failed\n",
__func__);
return ret;
}
/* The alarm doesn't store the year so get it from the rtc section */
ret = regmap_read(regmap, ISL12022_REG_YR, &yr);
if (ret) {
dev_dbg(dev, "%s: reading YR register failed\n", __func__);
return ret;
}
dev_dbg(dev,
"%s: sc=%02x, mn=%02x, hr=%02x, dt=%02x, mo=%02x, dw=%02x yr=%u\n",
__func__, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], yr);
tm->tm_sec = bcd2bin(buf[ISL12022_REG_SCA0 - ISL12022_ALARM] & 0x7F);
tm->tm_min = bcd2bin(buf[ISL12022_REG_MNA0 - ISL12022_ALARM] & 0x7F);
tm->tm_hour = bcd2bin(buf[ISL12022_REG_HRA0 - ISL12022_ALARM] & 0x3F);
tm->tm_mday = bcd2bin(buf[ISL12022_REG_DTA0 - ISL12022_ALARM] & 0x3F);
tm->tm_mon = bcd2bin(buf[ISL12022_REG_MOA0 - ISL12022_ALARM] & 0x1F) - 1;
tm->tm_wday = buf[ISL12022_REG_DWA0 - ISL12022_ALARM] & 0x07;
tm->tm_year = bcd2bin(yr) + 100;
for (i = 0; i < ISL12022_ALARM_LEN; i++) {
if (buf[i] & ISL12022_ALARM_ENABLE) {
alarm->enabled = 1;
break;
}
}
dev_dbg(dev, "%s: %ptR\n", __func__, tm);
return 0;
}
static int isl12022_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct rtc_time *alarm_tm = &alarm->time;
struct isl12022 *isl12022 = dev_get_drvdata(dev);
struct regmap *regmap = isl12022->regmap;
u8 regs[ISL12022_ALARM_LEN] = { 0, };
struct rtc_time rtc_tm;
int ret, enable, dw;
ret = isl12022_rtc_read_time(dev, &rtc_tm);
if (ret)
return ret;
/* If the alarm time is before the current time disable the alarm */
if (!alarm->enabled || rtc_tm_sub(alarm_tm, &rtc_tm) <= 0)
enable = 0;
else
enable = ISL12022_ALARM_ENABLE;
/*
* Set non-matching day of the week to safeguard against early false
* matching while setting all the alarm registers (this rtc lacks a
* general alarm/irq enable/disable bit).
*/
ret = regmap_read(regmap, ISL12022_REG_DW, &dw);
if (ret) {
dev_dbg(dev, "%s: reading DW failed\n", __func__);
return ret;
}
/* ~4 days into the future should be enough to avoid match */
dw = ((dw + 4) % 7) | ISL12022_ALARM_ENABLE;
ret = regmap_write(regmap, ISL12022_REG_DWA0, dw);
if (ret) {
dev_dbg(dev, "%s: writing DWA0 failed\n", __func__);
return ret;
}
/* Program the alarm and enable it for each setting */
regs[ISL12022_REG_SCA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_sec) | enable;
regs[ISL12022_REG_MNA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_min) | enable;
regs[ISL12022_REG_HRA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_hour) | enable;
regs[ISL12022_REG_DTA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_mday) | enable;
regs[ISL12022_REG_MOA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_mon + 1) | enable;
regs[ISL12022_REG_DWA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_wday & 7) | enable;
/* write ALARM registers */
ret = regmap_bulk_write(regmap, ISL12022_ALARM, &regs, sizeof(regs));
if (ret) {
dev_dbg(dev, "%s: writing ALARM registers failed\n", __func__);
return ret;
}
return 0;
}
static irqreturn_t isl12022_rtc_interrupt(int irq, void *data)
{
struct isl12022 *isl12022 = data;
struct rtc_device *rtc = isl12022->rtc;
struct device *dev = &rtc->dev;
struct regmap *regmap = isl12022->regmap;
u32 val = 0;
unsigned long events = 0;
int ret;
ret = regmap_read(regmap, ISL12022_REG_SR, &val);
if (ret) {
dev_dbg(dev, "%s: reading SR failed\n", __func__);
return IRQ_HANDLED;
}
if (val & ISL12022_SR_ALM)
events |= RTC_IRQF | RTC_AF;
if (events & RTC_AF)
dev_dbg(dev, "alarm!\n");
if (!events)
return IRQ_NONE;
rtc_update_irq(rtc, 1, events);
return IRQ_HANDLED;
}
static int isl12022_rtc_alarm_irq_enable(struct device *dev,
unsigned int enabled)
{
struct isl12022 *isl12022 = dev_get_drvdata(dev);
/* Make sure enabled is 0 or 1 */
enabled = !!enabled;
if (isl12022->irq_enabled == enabled)
return 0;
if (enabled)
enable_irq(isl12022->irq);
else
disable_irq(isl12022->irq);
isl12022->irq_enabled = enabled;
return 0;
}
static int isl12022_setup_irq(struct device *dev, int irq)
{
struct isl12022 *isl12022 = dev_get_drvdata(dev);
struct regmap *regmap = isl12022->regmap;
unsigned int reg_mask, reg_val;
u8 buf[ISL12022_ALARM_LEN] = { 0, };
int ret;
/* Clear and disable all alarm registers */
ret = regmap_bulk_write(regmap, ISL12022_ALARM, buf, sizeof(buf));
if (ret)
return ret;
/*
* Enable automatic reset of ALM bit and enable single event interrupt
* mode.
*/
reg_mask = ISL12022_INT_ARST | ISL12022_INT_IM | ISL12022_INT_FO_MASK;
reg_val = ISL12022_INT_ARST | ISL12022_INT_FO_OFF;
ret = regmap_write_bits(regmap, ISL12022_REG_INT,
reg_mask, reg_val);
if (ret)
return ret;
ret = devm_request_threaded_irq(dev, irq, NULL,
isl12022_rtc_interrupt,
IRQF_SHARED | IRQF_ONESHOT,
isl12022_driver.driver.name,
isl12022);
if (ret)
return dev_err_probe(dev, ret, "Unable to request irq %d\n", irq);
isl12022->irq = irq;
return 0;
}
static int isl12022_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
struct isl12022 *isl12022 = dev_get_drvdata(dev);
struct regmap *regmap = isl12022->regmap;
u32 user, val;
int ret;
switch (cmd) {
case RTC_VL_READ:
ret = regmap_read(regmap, ISL12022_REG_SR, &val);
if (ret)
return ret;
user = 0;
if (val & ISL12022_SR_LBAT85)
user |= RTC_VL_BACKUP_LOW;
if (val & ISL12022_SR_LBAT75)
user |= RTC_VL_BACKUP_EMPTY;
return put_user(user, (u32 __user *)arg);
default:
return -ENOIOCTLCMD;
}
}
static const struct rtc_class_ops isl12022_rtc_ops = {
.ioctl = isl12022_rtc_ioctl,
.read_time = isl12022_rtc_read_time,
.set_time = isl12022_rtc_set_time,
.read_alarm = isl12022_rtc_read_alarm,
.set_alarm = isl12022_rtc_set_alarm,
.alarm_irq_enable = isl12022_rtc_alarm_irq_enable,
};
static const struct regmap_config regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.use_single_write = true,
};
static int isl12022_register_clock(struct device *dev)
{
struct isl12022 *isl12022 = dev_get_drvdata(dev);
struct regmap *regmap = isl12022->regmap;
struct clk_hw *hw;
int ret;
if (!device_property_present(dev, "#clock-cells")) {
/*
* Disabling the F_OUT pin reduces the power
* consumption in battery mode by ~25%.
*/
regmap_update_bits(regmap, ISL12022_REG_INT, ISL12022_INT_FO_MASK,
ISL12022_INT_FO_OFF);
return 0;
}
if (!IS_ENABLED(CONFIG_COMMON_CLK))
return 0;
/*
* For now, only support a fixed clock of 32768Hz (the reset default).
*/
ret = regmap_update_bits(regmap, ISL12022_REG_INT,
ISL12022_INT_FO_MASK, ISL12022_INT_FO_32K);
if (ret)
return ret;
hw = devm_clk_hw_register_fixed_rate(dev, "isl12022", NULL, 0, 32768);
if (IS_ERR(hw))
return PTR_ERR(hw);
return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
}
static const u32 trip_levels[2][7] = {
{ 2125000, 2295000, 2550000, 2805000, 3060000, 4250000, 4675000 },
{ 1875000, 2025000, 2250000, 2475000, 2700000, 3750000, 4125000 },
};
static void isl12022_set_trip_levels(struct device *dev)
{
struct isl12022 *isl12022 = dev_get_drvdata(dev);
struct regmap *regmap = isl12022->regmap;
u32 levels[2] = {0, 0};
int ret, i, j, x[2];
u8 val, mask;
device_property_read_u32_array(dev, "isil,battery-trip-levels-microvolt",
levels, 2);
for (i = 0; i < 2; i++) {
for (j = 0; j < ARRAY_SIZE(trip_levels[i]) - 1; j++) {
if (levels[i] <= trip_levels[i][j])
break;
}
x[i] = j;
}
val = FIELD_PREP(ISL12022_REG_VB85_MASK, x[0]) |
FIELD_PREP(ISL12022_REG_VB75_MASK, x[1]);
mask = ISL12022_REG_VB85_MASK | ISL12022_REG_VB75_MASK;
ret = regmap_update_bits(regmap, ISL12022_REG_PWR_VBAT, mask, val);
if (ret)
dev_warn(dev, "unable to set battery alarm levels: %d\n", ret);
/*
* Force a write of the TSE bit in the BETA register, in order
* to trigger an update of the LBAT75 and LBAT85 bits in the
* status register. In battery backup mode, those bits have
* another meaning, so without this, they may contain stale
* values for up to a minute after power-on.
*/
regmap_write_bits(regmap, ISL12022_REG_BETA,
ISL12022_BETA_TSE, ISL12022_BETA_TSE);
}
static int isl12022_probe(struct i2c_client *client)
{
struct isl12022 *isl12022;
struct rtc_device *rtc;
struct regmap *regmap;
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
/* Allocate driver state */
isl12022 = devm_kzalloc(&client->dev, sizeof(*isl12022), GFP_KERNEL);
if (!isl12022)
return -ENOMEM;
regmap = devm_regmap_init_i2c(client, &regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(&client->dev, PTR_ERR(regmap), "regmap allocation failed\n");
isl12022->regmap = regmap;
dev_set_drvdata(&client->dev, isl12022);
ret = isl12022_register_clock(&client->dev);
if (ret)
return ret;
isl12022_set_trip_levels(&client->dev);
isl12022_hwmon_register(&client->dev);
rtc = devm_rtc_allocate_device(&client->dev);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
isl12022->rtc = rtc;
rtc->ops = &isl12022_rtc_ops;
rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
rtc->range_max = RTC_TIMESTAMP_END_2099;
if (client->irq > 0) {
ret = isl12022_setup_irq(&client->dev, client->irq);
if (ret)
return ret;
} else {
clear_bit(RTC_FEATURE_ALARM, rtc->features);
}
return devm_rtc_register_device(rtc);
}
static const struct of_device_id isl12022_dt_match[] = {
rtc: isl12022: deprecate use of isl in compatible string for isil "isil" and "isl" prefixes are used at various locations inside the kernel to reference Intersil corporation. This patch is part of a series fixing those locations were "isl" is used in compatible strings to use the now expected "isil" prefix instead (NASDAQ symbol for Intersil and most used version). The old compatible string is kept for backward compatibility. Signed-off-by: Arnaud Ebalard <arno@natisbad.org> Cc: Rob Herring <robh+dt@kernel.org> Cc: Pawel Moll <pawel.moll@arm.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Ian Campbell <ijc+devicetree@hellion.org.uk> Cc: Kumar Gala <galak@codeaurora.org> Cc: Russell King <linux@arm.linux.org.uk> Cc: Stephen Warren <swarren@wwwdotorg.org> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Alexandre Courbot <gnurou@gmail.com> Cc: Uwe Kleine-Knig <uwe@kleine-koenig.org> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Peter Huewe <peter.huewe@infineon.com> Cc: Linus Walleij <linus.walleij@linaro.org> Cc: Mark Brown <broonie@kernel.org> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Darshana Padmadas <darshanapadmadas@gmail.com> Cc: Grant Likely <grant.likely@linaro.org> Cc: Rob Landley <rob@landley.net> Cc: Jason Cooper <jason@lakedaemon.net> Cc: Guenter Roeck <linux@roeck-us.net> Cc: Jason Gunthorpe <jgunthorpe@obsidianresearch.com> Cc: Uwe Kleine-König <uwe@kleine-koenig.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2015-02-16 23:58:31 +00:00
{ .compatible = "isl,isl12022" }, /* for backward compat., don't use */
{ .compatible = "isil,isl12022" },
{ },
};
MODULE_DEVICE_TABLE(of, isl12022_dt_match);
static const struct i2c_device_id isl12022_id[] = {
{ "isl12022" },
{ }
};
MODULE_DEVICE_TABLE(i2c, isl12022_id);
static struct i2c_driver isl12022_driver = {
.driver = {
.name = "rtc-isl12022",
.of_match_table = isl12022_dt_match,
},
.probe = isl12022_probe,
.id_table = isl12022_id,
};
module_i2c_driver(isl12022_driver);
MODULE_AUTHOR("roman.fietze@telemotive.de");
MODULE_DESCRIPTION("ISL 12022 RTC driver");
MODULE_LICENSE("GPL");