summaryrefslogtreecommitdiff
path: root/drivers/rtc/rtc-ds1307.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-06-27 11:00:18 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-06-27 11:00:18 -0700
commitda7ca04e331e3e83f661e29c30d381a91e6ca245 (patch)
tree60b462354bb5ed314712aaf236b724d1eaa7cd25 /drivers/rtc/rtc-ds1307.c
parent6ca693ea903df5748809f61b290831004036978d (diff)
parent3c8f28578a0d68bc7fb91d881b832d55f734270c (diff)
Merge tag 'rtc-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux
Pull RTC updates from Alexandre Belloni: "Most of the work and improvements are for features of the m41t93. The ds1307 also gets support for OSF (Oscillator Stop Flag) for new variants. The pcap driver is being removed as the Motorola EZX support was removed a while ago. Subsystem: - add rtc_read_next_alarm() to read next expiring timer Drivers: - ds1307: handle OSF for ds1337/ds1339/ds3231, add clock provider for ds1307, fix wday for rx8130 - m41t93: DT support, alarm, clock provider, watchdog support - mv: add suspend/resume support for wakeup - pcap: remove driver - renesas-rtca3: many fixes" * tag 'rtc-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (36 commits) rtc: ds1307: update reference to removed CONFIG_RTC_DRV_DS1307_HWMON platform/x86: amd-pmc: Fix S0i3 wakeup with alarmtimer rtc: s35390a: fix typo in comment rtc: cmos: unregister HPET IRQ handler on probe failure rtc: ds1307: Fix off-by-one issue with wday for rx8130 dt-bindings: rtc: ds1307: Add epson,rx8901 rtc: bq32000: add delay between RTC reads rtc: m41t93: Add watchdog support rtc: m41t93: Add square wave clock provider support rtc: m41t93: Add alarm support rtc: m41t93: migrate to regmap api for register access rtc: m41t93: add device tree support dt-bindings: rtc: Add ST m41t93 rtc: ds1307: add support for clock provider in ds1307 rtc: mv: add suspend/resume support for wakeup rtc: aspeed: add AST2700 compatible dt-bindings: rtc: add ASPEED AST2700 compatible rtc: interface: fix typos in rtc_handle_legacy_irq() documentation rtc: msc313: fix NULL deref in shared IRQ handler at probe rtc: remove unused pcap driver ...
Diffstat (limited to 'drivers/rtc/rtc-ds1307.c')
-rw-r--r--drivers/rtc/rtc-ds1307.c207
1 files changed, 174 insertions, 33 deletions
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 7205c59ff729..0707ded5368b 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -269,6 +269,16 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
if (tmp & DS1338_BIT_OSF)
return -EINVAL;
break;
+ case ds_1337:
+ case ds_1339:
+ case ds_1341:
+ case ds_3231:
+ ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp);
+ if (ret)
+ return ret;
+ if (tmp & DS1337_BIT_OSF)
+ return -EINVAL;
+ break;
case ds_1340:
if (tmp & DS1340_BIT_nEOSC)
return -EINVAL;
@@ -279,13 +289,6 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
if (tmp & DS1340_BIT_OSF)
return -EINVAL;
break;
- case ds_1341:
- ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp);
- if (ret)
- return ret;
- if (tmp & DS1337_BIT_OSF)
- return -EINVAL;
- break;
case ds_1388:
ret = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &tmp);
if (ret)
@@ -308,7 +311,7 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
t->tm_hour = bcd2bin(tmp);
/* rx8130 is bit position, not BCD */
if (ds1307->type == rx_8130)
- t->tm_wday = fls(regs[DS1307_REG_WDAY] & 0x7f);
+ t->tm_wday = fls(regs[DS1307_REG_WDAY] & 0x7f) - 1;
else
t->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1;
t->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f);
@@ -380,14 +383,17 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL,
DS1338_BIT_OSF, 0);
break;
- case ds_1340:
- regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG,
- DS1340_BIT_OSF, 0);
- break;
+ case ds_1337:
+ case ds_1339:
case ds_1341:
+ case ds_3231:
regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS,
DS1337_BIT_OSF, 0);
break;
+ case ds_1340:
+ regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG,
+ DS1340_BIT_OSF, 0);
+ break;
case ds_1388:
regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG,
DS1388_BIT_OSF, 0);
@@ -1063,24 +1069,24 @@ static const struct chip_desc chips[last_ds_type] = {
};
static const struct i2c_device_id ds1307_id[] = {
- { "ds1307", ds_1307 },
- { "ds1308", ds_1308 },
- { "ds1337", ds_1337 },
- { "ds1338", ds_1338 },
- { "ds1339", ds_1339 },
- { "ds1388", ds_1388 },
- { "ds1340", ds_1340 },
- { "ds1341", ds_1341 },
- { "ds3231", ds_3231 },
- { "m41t0", m41t0 },
- { "m41t00", m41t00 },
- { "m41t11", m41t11 },
- { "mcp7940x", mcp794xx },
- { "mcp7941x", mcp794xx },
- { "pt7c4338", ds_1307 },
- { "rx8025", rx_8025 },
- { "isl12057", ds_1337 },
- { "rx8130", rx_8130 },
+ { .name = "ds1307", .driver_data = ds_1307 },
+ { .name = "ds1308", .driver_data = ds_1308 },
+ { .name = "ds1337", .driver_data = ds_1337 },
+ { .name = "ds1338", .driver_data = ds_1338 },
+ { .name = "ds1339", .driver_data = ds_1339 },
+ { .name = "ds1388", .driver_data = ds_1388 },
+ { .name = "ds1340", .driver_data = ds_1340 },
+ { .name = "ds1341", .driver_data = ds_1341 },
+ { .name = "ds3231", .driver_data = ds_3231 },
+ { .name = "m41t0", .driver_data = m41t0 },
+ { .name = "m41t00", .driver_data = m41t00 },
+ { .name = "m41t11", .driver_data = m41t11 },
+ { .name = "mcp7940x", .driver_data = mcp794xx },
+ { .name = "mcp7941x", .driver_data = mcp794xx },
+ { .name = "pt7c4338", .driver_data = ds_1307 },
+ { .name = "rx8025", .driver_data = rx_8025 },
+ { .name = "isl12057", .driver_data = ds_1337 },
+ { .name = "rx8130", .driver_data = rx_8130 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ds1307_id);
@@ -1409,7 +1415,7 @@ static void ds1307_hwmon_register(struct ds1307 *ds1307)
{
}
-#endif /* CONFIG_RTC_DRV_DS1307_HWMON */
+#endif /* IS_REACHABLE(CONFIG_HWMON) */
/*----------------------------------------------------------------------*/
@@ -1658,18 +1664,153 @@ static int ds3231_clks_register(struct ds1307 *ds1307)
return 0;
}
+/* ds1307 RTC clock output support */
+static unsigned long ds1307_clk_rates[] = {
+ 1,
+ 4096,
+ 8192,
+ 32768,
+};
+
+static unsigned long ds1307_clk_sqw_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ int ret;
+ unsigned int rate_id;
+ struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
+
+ ret = regmap_read(ds1307->regmap, DS1307_REG_CONTROL, &rate_id);
+ if (ret)
+ return ret;
+
+ rate_id &= (DS1307_BIT_RS1 | DS1307_BIT_RS0);
+
+ return ds1307_clk_rates[rate_id];
+}
+
+static int ds1307_clk_sqw_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ds1307_clk_rates); i++) {
+ if (req->rate <= ds1307_clk_rates[i]) {
+ req->rate = ds1307_clk_rates[i];
+ return 0;
+ }
+ }
+
+ /* Default rate 1Hz */
+ req->rate = ds1307_clk_rates[0];
+
+ return 0;
+}
+
+static int ds1307_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ int id, ret;
+ struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
+
+ for (id = 0; id < ARRAY_SIZE(ds1307_clk_rates); id++) {
+ if (ds1307_clk_rates[id] == rate)
+ break;
+ }
+
+ if (id >= ARRAY_SIZE(ds1307_clk_rates))
+ return -EINVAL;
+
+ ret = regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL,
+ DS1307_BIT_RS0 | DS1307_BIT_RS1, id);
+
+ return ret;
+}
+
+static int ds1307_clk_sqw_prepare(struct clk_hw *hw)
+{
+ int ret;
+ struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
+
+ ret = regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL,
+ DS1307_BIT_SQWE, DS1307_BIT_SQWE);
+
+ return ret;
+}
+
+static void ds1307_clk_sqw_unprepare(struct clk_hw *hw)
+{
+ struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
+
+ regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL,
+ DS1307_BIT_SQWE, ~DS1307_BIT_SQWE);
+}
+
+static int ds1307_clk_sqw_is_prepared(struct clk_hw *hw)
+{
+ int ret;
+ struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
+ unsigned int status;
+
+ ret = regmap_read(ds1307->regmap, DS1307_REG_CONTROL, &status);
+ if (ret)
+ return ret;
+
+ return !!(status & DS1307_BIT_SQWE);
+}
+
+static const struct clk_ops ds1307_clk_sqw_ops = {
+ .prepare = ds1307_clk_sqw_prepare,
+ .unprepare = ds1307_clk_sqw_unprepare,
+ .is_prepared = ds1307_clk_sqw_is_prepared,
+ .recalc_rate = ds1307_clk_sqw_recalc_rate,
+ .set_rate = ds1307_clk_sqw_set_rate,
+ .determine_rate = ds1307_clk_sqw_determine_rate,
+};
+
+static int rtc_ds1307_clks_register(struct ds1307 *ds1307)
+{
+ struct device_node *node = ds1307->dev->of_node;
+ struct clk *clk;
+ struct clk_init_data init = {0};
+
+ init.name = "ds1307_clk_sqw";
+ init.ops = &ds1307_clk_sqw_ops;
+
+ ds1307->clks[0].init = &init;
+
+ /* Register the clock with CCF */
+ clk = devm_clk_register(ds1307->dev, &ds1307->clks[0]);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ if (node)
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+ return 0;
+}
+
static void ds1307_clks_register(struct ds1307 *ds1307)
{
int ret;
- if (ds1307->type != ds_3231)
+ switch (ds1307->type) {
+ case ds_3231:
+ ret = ds3231_clks_register(ds1307);
+ break;
+
+ case ds_1307:
+ ret = rtc_ds1307_clks_register(ds1307);
+ break;
+
+ default:
return;
+ }
- ret = ds3231_clks_register(ds1307);
if (ret) {
dev_warn(ds1307->dev, "unable to register clock device %d\n",
ret);
}
+
}
#else