RTC for 6.17

Subsystem:
  - Convert drivers exposing a clock from round_rate() to determine_rate()
 
 Drivers:
  - ds1307: oscillator stop flag handling for ds1341
  - pcf85063: add support for RV8063
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmiQISYACgkQY6TcMGxw
 OjLDUw//ZpiAMJQ85tySlGwciHAgqmJi6XJJO/jyJ7cvcizK+BSN7WF5q0fshcGF
 PMgPiwNqdvmHlLtD3fdFRV+V7maB9Sxfx6avDOqpQQ2wC8xA3tmSSSN04oNCR6kj
 aOoPJPVY6YfRHNN7UN5HEdAouwVMj6ds6hoBaKEE35r/tAYiOkC1bQv6UI3CYtz3
 2gbuYCTDKqVWndqS2aZmqOADcK4J2SjJmuNBpb5IT6S7TR+/c+SPxe4sKRW8Aaxk
 hT4AdUPDnm0QdxRjb1ekOp90EZPYqHs5fJLRUavaUUnesxwKMmnbgDHQ9ZE1IgDS
 bKm8PJ4aUq2SfqU+bf9BSTf+3mXl0FQOMMmay30OiMB6BeU4pctsHmuNgu9TlpYC
 URAMzFVzJCchIyundTBtl9e+Dz/x7W1J9Q64pHjbgBifmYd+0fYfiR7tvd6FHqUc
 aqCxcVylzXCarCQD0NEYxzvRwtYy4GXcxfHqefgQ+1sb0CgroX7C/msC3woyCQ0c
 9JqznMdjDqVkPihM2Y/7kg303SIYcc2dKXpEvmo9aH/SGBZkv2pEff6Stz58B2Dn
 Y79XqYNk4YE2xuzq2yXawjQST9EP41n6potcjG06WpXvU7a8GDC8+VZvgaqNFFFc
 x2IyZv/ISOW6oWH73HtYuUyy+u93JOZ0ERBYm3sAaopT/ExWYcw=
 =+Y1R
 -----END PGP SIGNATURE-----

Merge tag 'rtc-6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux

Pull RTC updates from Alexandre Belloni:
 "Support for a new RTC in an existing driver and all the drivers
  exposing clocks using the common clock framework have been converted
  to determine_rate(). Summary:

  Subsystem:
   - Convert drivers exposing a clock from round_rate() to determine_rate()

  Drivers:
   - ds1307: oscillator stop flag handling for ds1341
   - pcf85063: add support for RV8063"

* tag 'rtc-6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (34 commits)
  rtc: ds1685: Update Joshua Kinard's email address.
  rtc: rv3032: convert from round_rate() to determine_rate()
  rtc: rv3028: convert from round_rate() to determine_rate()
  rtc: pcf8563: convert from round_rate() to determine_rate()
  rtc: pcf85063: convert from round_rate() to determine_rate()
  rtc: nct3018y: convert from round_rate() to determine_rate()
  rtc: max31335: convert from round_rate() to determine_rate()
  rtc: m41t80: convert from round_rate() to determine_rate()
  rtc: hym8563: convert from round_rate() to determine_rate()
  rtc: ds1307: convert from round_rate() to determine_rate()
  rtc: rv3028: fix incorrect maximum clock rate handling
  rtc: pcf8563: fix incorrect maximum clock rate handling
  rtc: pcf85063: fix incorrect maximum clock rate handling
  rtc: nct3018y: fix incorrect maximum clock rate handling
  rtc: hym8563: fix incorrect maximum clock rate handling
  rtc: ds1307: fix incorrect maximum clock rate handling
  rtc: pcf85063: scope pcf85063_config structures
  rtc: Optimize calculations in rtc_time64_to_tm()
  dt-bindings: rtc: amlogic,a4-rtc: Add compatible string for C3
  rtc: ds1307: handle oscillator stop flag (OSF) for ds1341
  ...
This commit is contained in:
Linus Torvalds 2025-08-03 20:17:34 -07:00
commit d2eedaa390
25 changed files with 507 additions and 247 deletions

View file

@ -16,9 +16,14 @@ allOf:
properties:
compatible:
enum:
- amlogic,a4-rtc
- amlogic,a5-rtc
oneOf:
- enum:
- amlogic,a4-rtc
- amlogic,a5-rtc
- items:
- enum:
- amlogic,c3-rtc
- const: amlogic,a5-rtc
reg:
maxItems: 1

View file

@ -18,7 +18,12 @@ allOf:
properties:
compatible:
const: nxp,lpc1788-rtc
oneOf:
- items:
- enum:
- nxp,lpc1850-rtc
- const: nxp,lpc1788-rtc
- const: nxp,lpc1788-rtc
reg:
maxItems: 1

View file

@ -0,0 +1,49 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/nxp,lpc3220-rtc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP LPC32xx SoC Real-time Clock
maintainers:
- Frank Li <Frank.Li@nxp.com>
properties:
compatible:
enum:
- nxp,lpc3220-rtc
reg:
maxItems: 1
clocks:
maxItems: 1
interrupts:
maxItems: 1
start-year: true
required:
- compatible
- reg
allOf:
- $ref: rtc.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/clock/lpc32xx-clock.h>
rtc@40024000 {
compatible = "nxp,lpc3220-rtc";
reg = <0x40024000 0x1000>;
interrupt-parent = <&sic1>;
interrupts = <20 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk LPC32XX_CLK_RTC>;
};

View file

@ -12,6 +12,7 @@ maintainers:
properties:
compatible:
enum:
- microcrystal,rv8063
- microcrystal,rv8263
- nxp,pcf85063
- nxp,pcf85063a
@ -44,13 +45,19 @@ properties:
wakeup-source: true
spi-cs-high: true
spi-3wire: true
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
- $ref: rtc.yaml#
- if:
properties:
compatible:
contains:
enum:
- microcrystal,rv8063
- microcrystal,rv8263
then:
properties:
@ -65,12 +72,23 @@ allOf:
properties:
quartz-load-femtofarads:
const: 7000
- if:
properties:
compatible:
not:
contains:
enum:
- microcrystal,rv8063
then:
properties:
spi-cs-high: false
spi-3wire: false
required:
- compatible
- reg
additionalProperties: false
unevaluatedProperties: false
examples:
- |
@ -90,3 +108,16 @@ examples:
};
};
};
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
rtc@0 {
compatible = "microcrystal,rv8063";
reg = <0>;
spi-cs-high;
spi-3wire;
};
};

View file

@ -1,7 +1,7 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sophgo/sophgo,cv1800b-rtc.yaml#
$id: http://devicetree.org/schemas/rtc/sophgo,cv1800b-rtc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Real Time Clock of the Sophgo CV1800 SoC

View file

@ -63,8 +63,6 @@ properties:
- microcrystal,rv3029
# Real Time Clock
- microcrystal,rv8523
# NXP LPC32xx SoC Real-time Clock
- nxp,lpc3220-rtc
# I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
- ricoh,r2025sd
# I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC

View file

@ -6713,7 +6713,7 @@ S: Supported
F: drivers/input/keyboard/dlink-dir685-touchkeys.c
DALLAS/MAXIM DS1685-FAMILY REAL TIME CLOCK
M: Joshua Kinard <kumba@gentoo.org>
M: Joshua Kinard <linux@kumba.dev>
S: Maintained
F: drivers/rtc/rtc-ds1685.c
F: include/linux/rtc/ds1685.h

View file

@ -483,15 +483,6 @@ config RTC_DRV_PCF8523
This driver can also be built as a module. If so, the module
will be called rtc-pcf8523.
config RTC_DRV_PCF85063
tristate "NXP PCF85063"
select REGMAP_I2C
help
If you say yes here you get support for the PCF85063 RTC chip
This driver can also be built as a module. If so, the module
will be called rtc-pcf85063.
config RTC_DRV_PCF85363
tristate "NXP PCF85363"
select REGMAP_I2C
@ -971,6 +962,18 @@ config RTC_DRV_PCF2127
This driver can also be built as a module. If so, the module
will be called rtc-pcf2127.
config RTC_DRV_PCF85063
tristate "NXP PCF85063"
depends on RTC_I2C_AND_SPI
select REGMAP_I2C if I2C
select REGMAP_SPI if SPI_MASTER
help
If you say yes here you get support for the PCF85063 and RV8063
RTC chips.
This driver can also be built as a module. If so, the module
will be called rtc-pcf85063.
config RTC_DRV_RV3029C2
tristate "Micro Crystal RV3029/3049"
depends on RTC_I2C_AND_SPI

View file

@ -15,7 +15,7 @@ rtc-core-$(CONFIG_RTC_INTF_DEV) += dev.o
rtc-core-$(CONFIG_RTC_INTF_PROC) += proc.o
rtc-core-$(CONFIG_RTC_INTF_SYSFS) += sysfs.o
obj-$(CONFIG_RTC_LIB_KUNIT_TEST) += lib_test.o
obj-$(CONFIG_RTC_LIB_KUNIT_TEST) += test_rtc_lib.o
# Keep the list ordered.

View file

@ -51,7 +51,7 @@ EXPORT_SYMBOL(rtc_year_days);
*/
void rtc_time64_to_tm(time64_t time, struct rtc_time *tm)
{
int days, secs;
int secs;
u64 u64tmp;
u32 u32tmp, udays, century, day_of_century, year_of_century, year,
@ -59,28 +59,26 @@ void rtc_time64_to_tm(time64_t time, struct rtc_time *tm)
bool is_Jan_or_Feb, is_leap_year;
/*
* Get days and seconds while preserving the sign to
* handle negative time values (dates before 1970-01-01)
* The time represented by `time` is given in seconds since 1970-01-01
* (UTC). As the division done below might misbehave for negative
* values, we convert it to seconds since 0000-03-01 and then assume it
* will be non-negative.
* Below we do 4 * udays + 3 which should fit into a 32 bit unsigned
* variable. So the latest date this algorithm works for is 1073741823
* days after 0000-03-01 which is in the year 2939805.
*/
days = div_s64_rem(time, 86400, &secs);
time += (u64)719468 * 86400;
udays = div_s64_rem(time, 86400, &secs);
/*
* We need 0 <= secs < 86400 which isn't given for negative
* values of time. Fixup accordingly.
* day of the week, 0000-03-01 was a Wednesday (in the proleptic
* Gregorian calendar)
*/
if (secs < 0) {
days -= 1;
secs += 86400;
}
/* day of the week, 1970-01-01 was a Thursday */
tm->tm_wday = (days + 4) % 7;
/* Ensure tm_wday is always positive */
if (tm->tm_wday < 0)
tm->tm_wday += 7;
tm->tm_wday = (udays + 3) % 7;
/*
* The following algorithm is, basically, Proposition 6.3 of Neri
* The following algorithm is, basically, Figure 12 of Neri
* and Schneider [1]. In a few words: it works on the computational
* (fictitious) calendar where the year starts in March, month = 2
* (*), and finishes in February, month = 13. This calendar is
@ -100,15 +98,15 @@ void rtc_time64_to_tm(time64_t time, struct rtc_time *tm)
* (using just arithmetics) it's easy to convert it to the
* corresponding date in the Gregorian calendar.
*
* [1] "Euclidean Affine Functions and Applications to Calendar
* Algorithms". https://arxiv.org/abs/2102.06959
* [1] Neri C, Schneider L. Euclidean affine functions and their
* application to calendar algorithms. Softw Pract Exper.
* 2023;53(4):937-970. doi: 10.1002/spe.3172
* https://doi.org/10.1002/spe.3172
*
* (*) The numbering of months follows rtc_time more closely and
* thus, is slightly different from [1].
*/
udays = days + 719468;
u32tmp = 4 * udays + 3;
century = u32tmp / 146097;
day_of_century = u32tmp % 146097 / 4;

View file

@ -279,6 +279,13 @@ 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)
@ -377,6 +384,10 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG,
DS1340_BIT_OSF, 0);
break;
case ds_1341:
regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS,
DS1337_BIT_OSF, 0);
break;
case ds_1388:
regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG,
DS1388_BIT_OSF, 0);
@ -1456,16 +1467,21 @@ static unsigned long ds3231_clk_sqw_recalc_rate(struct clk_hw *hw,
return ds3231_clk_sqw_rates[rate_sel];
}
static long ds3231_clk_sqw_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
static int ds3231_clk_sqw_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
int i;
for (i = ARRAY_SIZE(ds3231_clk_sqw_rates) - 1; i >= 0; i--) {
if (ds3231_clk_sqw_rates[i] <= rate)
return ds3231_clk_sqw_rates[i];
if (ds3231_clk_sqw_rates[i] <= req->rate) {
req->rate = ds3231_clk_sqw_rates[i];
return 0;
}
}
req->rate = ds3231_clk_sqw_rates[ARRAY_SIZE(ds3231_clk_sqw_rates) - 1];
return 0;
}
@ -1525,7 +1541,7 @@ static const struct clk_ops ds3231_clk_sqw_ops = {
.unprepare = ds3231_clk_sqw_unprepare,
.is_prepared = ds3231_clk_sqw_is_prepared,
.recalc_rate = ds3231_clk_sqw_recalc_rate,
.round_rate = ds3231_clk_sqw_round_rate,
.determine_rate = ds3231_clk_sqw_determine_rate,
.set_rate = ds3231_clk_sqw_set_rate,
};
@ -1813,10 +1829,8 @@ static int ds1307_probe(struct i2c_client *client)
regmap_write(ds1307->regmap, DS1337_REG_CONTROL,
regs[0]);
/* oscillator fault? clear flag, and warn */
/* oscillator fault? warn */
if (regs[1] & DS1337_BIT_OSF) {
regmap_write(ds1307->regmap, DS1337_REG_STATUS,
regs[1] & ~DS1337_BIT_OSF);
dev_warn(ds1307->dev, "SET TIME!\n");
}
break;

View file

@ -3,7 +3,7 @@
* An rtc driver for the Dallas/Maxim DS1685/DS1687 and related real-time
* chips.
*
* Copyright (C) 2011-2014 Joshua Kinard <kumba@gentoo.org>.
* Copyright (C) 2011-2014 Joshua Kinard <linux@kumba.dev>.
* Copyright (C) 2009 Matthias Fuchs <matthias.fuchs@esd-electronics.com>.
*
* References:
@ -1436,7 +1436,7 @@ EXPORT_SYMBOL_GPL(ds1685_rtc_poweroff);
/* ----------------------------------------------------------------------- */
MODULE_AUTHOR("Joshua Kinard <kumba@gentoo.org>");
MODULE_AUTHOR("Joshua Kinard <linux@kumba.dev>");
MODULE_AUTHOR("Matthias Fuchs <matthias.fuchs@esd-electronics.com>");
MODULE_DESCRIPTION("Dallas/Maxim DS1685/DS1687-series RTC driver");
MODULE_LICENSE("GPL");

View file

@ -285,14 +285,19 @@ static unsigned long hym8563_clkout_recalc_rate(struct clk_hw *hw,
return clkout_rates[ret];
}
static long hym8563_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
static int hym8563_clkout_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
int i;
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
if (clkout_rates[i] <= rate)
return clkout_rates[i];
if (clkout_rates[i] <= req->rate) {
req->rate = clkout_rates[i];
return 0;
}
req->rate = clkout_rates[0];
return 0;
}
@ -363,7 +368,7 @@ static const struct clk_ops hym8563_clkout_ops = {
.unprepare = hym8563_clkout_unprepare,
.is_prepared = hym8563_clkout_is_prepared,
.recalc_rate = hym8563_clkout_recalc_rate,
.round_rate = hym8563_clkout_round_rate,
.determine_rate = hym8563_clkout_determine_rate,
.set_rate = hym8563_clkout_set_rate,
};

View file

@ -72,7 +72,7 @@
static const struct i2c_device_id m41t80_id[] = {
{ "m41t62", M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT },
{ "m41t65", M41T80_FEATURE_HT | M41T80_FEATURE_WD },
{ "m41t65", M41T80_FEATURE_WD },
{ "m41t80", M41T80_FEATURE_SQ },
{ "m41t81", M41T80_FEATURE_HT | M41T80_FEATURE_SQ},
{ "m41t81s", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
@ -93,7 +93,7 @@ static const __maybe_unused struct of_device_id m41t80_of_match[] = {
},
{
.compatible = "st,m41t65",
.data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_WD)
.data = (void *)(M41T80_FEATURE_WD)
},
{
.compatible = "st,m41t80",
@ -484,16 +484,17 @@ static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw,
return sqw_to_m41t80_data(hw)->freq;
}
static long m41t80_sqw_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
static int m41t80_sqw_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
if (rate >= M41T80_SQW_MAX_FREQ)
return M41T80_SQW_MAX_FREQ;
if (rate >= M41T80_SQW_MAX_FREQ / 4)
return M41T80_SQW_MAX_FREQ / 4;
if (!rate)
return 0;
return 1 << ilog2(rate);
if (req->rate >= M41T80_SQW_MAX_FREQ)
req->rate = M41T80_SQW_MAX_FREQ;
else if (req->rate >= M41T80_SQW_MAX_FREQ / 4)
req->rate = M41T80_SQW_MAX_FREQ / 4;
else if (req->rate)
req->rate = 1 << ilog2(req->rate);
return 0;
}
static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
@ -564,7 +565,7 @@ static const struct clk_ops m41t80_sqw_ops = {
.unprepare = m41t80_sqw_unprepare,
.is_prepared = m41t80_sqw_is_prepared,
.recalc_rate = m41t80_sqw_recalc_rate,
.round_rate = m41t80_sqw_round_rate,
.determine_rate = m41t80_sqw_determine_rate,
.set_rate = m41t80_sqw_set_rate,
};

View file

@ -497,15 +497,17 @@ static unsigned long max31335_clkout_recalc_rate(struct clk_hw *hw,
return max31335_clkout_freq[reg & freq_mask];
}
static long max31335_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
static int max31335_clkout_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
int index;
index = find_closest(rate, max31335_clkout_freq,
index = find_closest(req->rate, max31335_clkout_freq,
ARRAY_SIZE(max31335_clkout_freq));
return max31335_clkout_freq[index];
req->rate = max31335_clkout_freq[index];
return 0;
}
static int max31335_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
@ -554,7 +556,7 @@ static int max31335_clkout_is_enabled(struct clk_hw *hw)
static const struct clk_ops max31335_clkout_ops = {
.recalc_rate = max31335_clkout_recalc_rate,
.round_rate = max31335_clkout_round_rate,
.determine_rate = max31335_clkout_determine_rate,
.set_rate = max31335_clkout_set_rate,
.enable = max31335_clkout_enable,
.disable = max31335_clkout_disable,

View file

@ -367,14 +367,19 @@ static unsigned long nct3018y_clkout_recalc_rate(struct clk_hw *hw,
return clkout_rates[flags];
}
static long nct3018y_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
static int nct3018y_clkout_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
int i;
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
if (clkout_rates[i] <= rate)
return clkout_rates[i];
if (clkout_rates[i] <= req->rate) {
req->rate = clkout_rates[i];
return 0;
}
req->rate = clkout_rates[0];
return 0;
}
@ -446,7 +451,7 @@ static const struct clk_ops nct3018y_clkout_ops = {
.unprepare = nct3018y_clkout_unprepare,
.is_prepared = nct3018y_clkout_is_prepared,
.recalc_rate = nct3018y_clkout_recalc_rate,
.round_rate = nct3018y_clkout_round_rate,
.determine_rate = nct3018y_clkout_determine_rate,
.set_rate = nct3018y_clkout_set_rate,
};

View file

@ -17,6 +17,7 @@
#include <linux/of.h>
#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
/*
* Information for this driver was pulled from the following datasheets.
@ -29,6 +30,9 @@
*
* https://www.microcrystal.com/fileadmin/Media/Products/RTC/App.Manual/RV-8263-C7_App-Manual.pdf
* RV8263 -- Rev. 1.0 January 2019
*
* https://www.microcrystal.com/fileadmin/Media/Products/RTC/App.Manual/RV-8063-C7_App-Manual.pdf
* RV8063 -- Rev. 1.1 - October 2018
*/
#define PCF85063_REG_CTRL1 0x00 /* status */
@ -401,14 +405,19 @@ static unsigned long pcf85063_clkout_recalc_rate(struct clk_hw *hw,
return clkout_rates[buf];
}
static long pcf85063_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
static int pcf85063_clkout_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
int i;
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
if (clkout_rates[i] <= rate)
return clkout_rates[i];
if (clkout_rates[i] <= req->rate) {
req->rate = clkout_rates[i];
return 0;
}
req->rate = clkout_rates[0];
return 0;
}
@ -482,7 +491,7 @@ static const struct clk_ops pcf85063_clkout_ops = {
.unprepare = pcf85063_clkout_unprepare,
.is_prepared = pcf85063_clkout_is_prepared,
.recalc_rate = pcf85063_clkout_recalc_rate,
.round_rate = pcf85063_clkout_round_rate,
.determine_rate = pcf85063_clkout_determine_rate,
.set_rate = pcf85063_clkout_set_rate,
};
@ -524,6 +533,102 @@ static struct clk *pcf85063_clkout_register_clk(struct pcf85063 *pcf85063)
}
#endif
static int pcf85063_probe(struct device *dev, struct regmap *regmap, int irq,
const struct pcf85063_config *config)
{
struct pcf85063 *pcf85063;
unsigned int tmp;
int err;
struct nvmem_config nvmem_cfg = {
.name = "pcf85063_nvram",
.reg_read = pcf85063_nvmem_read,
.reg_write = pcf85063_nvmem_write,
.type = NVMEM_TYPE_BATTERY_BACKED,
.size = 1,
};
dev_dbg(dev, "%s\n", __func__);
pcf85063 = devm_kzalloc(dev, sizeof(struct pcf85063),
GFP_KERNEL);
if (!pcf85063)
return -ENOMEM;
pcf85063->regmap = regmap;
dev_set_drvdata(dev, pcf85063);
err = regmap_read(pcf85063->regmap, PCF85063_REG_SC, &tmp);
if (err)
return dev_err_probe(dev, err, "RTC chip is not present\n");
pcf85063->rtc = devm_rtc_allocate_device(dev);
if (IS_ERR(pcf85063->rtc))
return PTR_ERR(pcf85063->rtc);
/*
* If a Power loss is detected, SW reset the device.
* From PCF85063A datasheet:
* There is a low probability that some devices will have corruption
* of the registers after the automatic power-on reset...
*/
if (tmp & PCF85063_REG_SC_OS) {
dev_warn(dev, "POR issue detected, sending a SW reset\n");
err = regmap_write(pcf85063->regmap, PCF85063_REG_CTRL1,
PCF85063_REG_CTRL1_SWR);
if (err < 0)
dev_warn(dev, "SW reset failed, trying to continue\n");
}
err = pcf85063_load_capacitance(pcf85063, dev->of_node,
config->force_cap_7000 ? 7000 : 0);
if (err < 0)
dev_warn(dev, "failed to set xtal load capacitance: %d",
err);
pcf85063->rtc->ops = &pcf85063_rtc_ops;
pcf85063->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
pcf85063->rtc->range_max = RTC_TIMESTAMP_END_2099;
set_bit(RTC_FEATURE_ALARM_RES_2S, pcf85063->rtc->features);
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf85063->rtc->features);
clear_bit(RTC_FEATURE_ALARM, pcf85063->rtc->features);
if (config->has_alarms && irq > 0) {
unsigned long irqflags = IRQF_TRIGGER_LOW;
if (dev_fwnode(dev))
irqflags = 0;
err = devm_request_threaded_irq(dev, irq,
NULL, pcf85063_rtc_handle_irq,
irqflags | IRQF_ONESHOT,
"pcf85063", pcf85063);
if (err) {
dev_warn(&pcf85063->rtc->dev,
"unable to request IRQ, alarms disabled\n");
} else {
set_bit(RTC_FEATURE_ALARM, pcf85063->rtc->features);
device_init_wakeup(dev, true);
err = dev_pm_set_wake_irq(dev, irq);
if (err)
dev_err(&pcf85063->rtc->dev,
"failed to enable irq wake\n");
}
}
nvmem_cfg.priv = pcf85063->regmap;
devm_rtc_nvmem_register(pcf85063->rtc, &nvmem_cfg);
#ifdef CONFIG_COMMON_CLK
/* register clk in common clk framework */
pcf85063_clkout_register_clk(pcf85063);
#endif
return devm_rtc_register_device(pcf85063->rtc);
}
#if IS_ENABLED(CONFIG_I2C)
static const struct pcf85063_config config_pcf85063 = {
.regmap = {
.reg_bits = 8,
@ -559,108 +664,6 @@ static const struct pcf85063_config config_rv8263 = {
.force_cap_7000 = 1,
};
static int pcf85063_probe(struct i2c_client *client)
{
struct pcf85063 *pcf85063;
unsigned int tmp;
int err;
const struct pcf85063_config *config;
struct nvmem_config nvmem_cfg = {
.name = "pcf85063_nvram",
.reg_read = pcf85063_nvmem_read,
.reg_write = pcf85063_nvmem_write,
.type = NVMEM_TYPE_BATTERY_BACKED,
.size = 1,
};
dev_dbg(&client->dev, "%s\n", __func__);
pcf85063 = devm_kzalloc(&client->dev, sizeof(struct pcf85063),
GFP_KERNEL);
if (!pcf85063)
return -ENOMEM;
config = i2c_get_match_data(client);
if (!config)
return -ENODEV;
pcf85063->regmap = devm_regmap_init_i2c(client, &config->regmap);
if (IS_ERR(pcf85063->regmap))
return PTR_ERR(pcf85063->regmap);
i2c_set_clientdata(client, pcf85063);
err = regmap_read(pcf85063->regmap, PCF85063_REG_SC, &tmp);
if (err)
return dev_err_probe(&client->dev, err, "RTC chip is not present\n");
pcf85063->rtc = devm_rtc_allocate_device(&client->dev);
if (IS_ERR(pcf85063->rtc))
return PTR_ERR(pcf85063->rtc);
/*
* If a Power loss is detected, SW reset the device.
* From PCF85063A datasheet:
* There is a low probability that some devices will have corruption
* of the registers after the automatic power-on reset...
*/
if (tmp & PCF85063_REG_SC_OS) {
dev_warn(&client->dev,
"POR issue detected, sending a SW reset\n");
err = regmap_write(pcf85063->regmap, PCF85063_REG_CTRL1,
PCF85063_REG_CTRL1_SWR);
if (err < 0)
dev_warn(&client->dev,
"SW reset failed, trying to continue\n");
}
err = pcf85063_load_capacitance(pcf85063, client->dev.of_node,
config->force_cap_7000 ? 7000 : 0);
if (err < 0)
dev_warn(&client->dev, "failed to set xtal load capacitance: %d",
err);
pcf85063->rtc->ops = &pcf85063_rtc_ops;
pcf85063->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
pcf85063->rtc->range_max = RTC_TIMESTAMP_END_2099;
set_bit(RTC_FEATURE_ALARM_RES_2S, pcf85063->rtc->features);
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf85063->rtc->features);
clear_bit(RTC_FEATURE_ALARM, pcf85063->rtc->features);
if (config->has_alarms && client->irq > 0) {
unsigned long irqflags = IRQF_TRIGGER_LOW;
if (dev_fwnode(&client->dev))
irqflags = 0;
err = devm_request_threaded_irq(&client->dev, client->irq,
NULL, pcf85063_rtc_handle_irq,
irqflags | IRQF_ONESHOT,
"pcf85063", pcf85063);
if (err) {
dev_warn(&pcf85063->rtc->dev,
"unable to request IRQ, alarms disabled\n");
} else {
set_bit(RTC_FEATURE_ALARM, pcf85063->rtc->features);
device_init_wakeup(&client->dev, true);
err = dev_pm_set_wake_irq(&client->dev, client->irq);
if (err)
dev_err(&pcf85063->rtc->dev,
"failed to enable irq wake\n");
}
}
nvmem_cfg.priv = pcf85063->regmap;
devm_rtc_nvmem_register(pcf85063->rtc, &nvmem_cfg);
#ifdef CONFIG_COMMON_CLK
/* register clk in common clk framework */
pcf85063_clkout_register_clk(pcf85063);
#endif
return devm_rtc_register_device(pcf85063->rtc);
}
static const struct i2c_device_id pcf85063_ids[] = {
{ "pca85073a", .driver_data = (kernel_ulong_t)&config_pcf85063a },
{ "pcf85063", .driver_data = (kernel_ulong_t)&config_pcf85063 },
@ -683,16 +686,146 @@ static const struct of_device_id pcf85063_of_match[] = {
MODULE_DEVICE_TABLE(of, pcf85063_of_match);
#endif
static int pcf85063_i2c_probe(struct i2c_client *client)
{
const struct pcf85063_config *config;
struct regmap *regmap;
config = i2c_get_match_data(client);
if (!config)
return -ENODEV;
regmap = devm_regmap_init_i2c(client, &config->regmap);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
return pcf85063_probe(&client->dev, regmap, client->irq, config);
}
static struct i2c_driver pcf85063_driver = {
.driver = {
.name = "rtc-pcf85063",
.of_match_table = of_match_ptr(pcf85063_of_match),
},
.probe = pcf85063_probe,
.probe = pcf85063_i2c_probe,
.id_table = pcf85063_ids,
};
module_i2c_driver(pcf85063_driver);
static int pcf85063_register_driver(void)
{
return i2c_add_driver(&pcf85063_driver);
}
static void pcf85063_unregister_driver(void)
{
i2c_del_driver(&pcf85063_driver);
}
#else
static int pcf85063_register_driver(void)
{
return 0;
}
static void pcf85063_unregister_driver(void)
{
}
#endif /* IS_ENABLED(CONFIG_I2C) */
#if IS_ENABLED(CONFIG_SPI_MASTER)
static const struct pcf85063_config config_rv8063 = {
.regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x11,
.read_flag_mask = BIT(7) | BIT(5),
.write_flag_mask = BIT(5),
},
.has_alarms = 1,
.force_cap_7000 = 1,
};
static const struct spi_device_id rv8063_id[] = {
{ "rv8063" },
{}
};
MODULE_DEVICE_TABLE(spi, rv8063_id);
static const struct of_device_id rv8063_of_match[] = {
{ .compatible = "microcrystal,rv8063" },
{}
};
MODULE_DEVICE_TABLE(of, rv8063_of_match);
static int rv8063_probe(struct spi_device *spi)
{
const struct pcf85063_config *config = &config_rv8063;
struct regmap *regmap;
regmap = devm_regmap_init_spi(spi, &config->regmap);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
return pcf85063_probe(&spi->dev, regmap, spi->irq, config);
}
static struct spi_driver rv8063_driver = {
.driver = {
.name = "rv8063",
.of_match_table = rv8063_of_match,
},
.probe = rv8063_probe,
.id_table = rv8063_id,
};
static int __init rv8063_register_driver(void)
{
return spi_register_driver(&rv8063_driver);
}
static void __exit rv8063_unregister_driver(void)
{
spi_unregister_driver(&rv8063_driver);
}
#else
static int __init rv8063_register_driver(void)
{
return 0;
}
static void __exit rv8063_unregister_driver(void)
{
}
#endif /* IS_ENABLED(CONFIG_SPI_MASTER) */
static int __init pcf85063_init(void)
{
int ret;
ret = pcf85063_register_driver();
if (ret)
return ret;
ret = rv8063_register_driver();
if (ret)
pcf85063_unregister_driver();
return ret;
}
module_init(pcf85063_init);
static void __exit pcf85063_exit(void)
{
rv8063_unregister_driver();
pcf85063_unregister_driver();
}
module_exit(pcf85063_exit);
MODULE_AUTHOR("Søren Andersen <san@rosetechnology.dk>");
MODULE_DESCRIPTION("PCF85063 RTC driver");

View file

@ -330,14 +330,19 @@ static unsigned long pcf8563_clkout_recalc_rate(struct clk_hw *hw,
return clkout_rates[buf];
}
static long pcf8563_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
static int pcf8563_clkout_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
int i;
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
if (clkout_rates[i] <= rate)
return clkout_rates[i];
if (clkout_rates[i] <= req->rate) {
req->rate = clkout_rates[i];
return 0;
}
req->rate = clkout_rates[0];
return 0;
}
@ -413,7 +418,7 @@ static const struct clk_ops pcf8563_clkout_ops = {
.unprepare = pcf8563_clkout_unprepare,
.is_prepared = pcf8563_clkout_is_prepared,
.recalc_rate = pcf8563_clkout_recalc_rate,
.round_rate = pcf8563_clkout_round_rate,
.determine_rate = pcf8563_clkout_determine_rate,
.set_rate = pcf8563_clkout_set_rate,
};

View file

@ -731,14 +731,19 @@ static unsigned long rv3028_clkout_recalc_rate(struct clk_hw *hw,
return clkout_rates[clkout];
}
static long rv3028_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
static int rv3028_clkout_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
int i;
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
if (clkout_rates[i] <= rate)
return clkout_rates[i];
if (clkout_rates[i] <= req->rate) {
req->rate = clkout_rates[i];
return 0;
}
req->rate = clkout_rates[0];
return 0;
}
@ -802,7 +807,7 @@ static const struct clk_ops rv3028_clkout_ops = {
.unprepare = rv3028_clkout_unprepare,
.is_prepared = rv3028_clkout_is_prepared,
.recalc_rate = rv3028_clkout_recalc_rate,
.round_rate = rv3028_clkout_round_rate,
.determine_rate = rv3028_clkout_determine_rate,
.set_rate = rv3028_clkout_set_rate,
};

View file

@ -646,19 +646,24 @@ static unsigned long rv3032_clkout_recalc_rate(struct clk_hw *hw,
return clkout_xtal_rates[FIELD_GET(RV3032_CLKOUT2_FD_MSK, clkout)];
}
static long rv3032_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
static int rv3032_clkout_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
int i, hfd;
if (rate < RV3032_HFD_STEP)
if (req->rate < RV3032_HFD_STEP)
for (i = 0; i < ARRAY_SIZE(clkout_xtal_rates); i++)
if (clkout_xtal_rates[i] <= rate)
return clkout_xtal_rates[i];
if (clkout_xtal_rates[i] <= req->rate) {
req->rate = clkout_xtal_rates[i];
hfd = DIV_ROUND_CLOSEST(rate, RV3032_HFD_STEP);
return 0;
}
return RV3032_HFD_STEP * clamp(hfd, 0, 8192);
hfd = DIV_ROUND_CLOSEST(req->rate, RV3032_HFD_STEP);
req->rate = RV3032_HFD_STEP * clamp(hfd, 0, 8192);
return 0;
}
static int rv3032_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
@ -738,7 +743,7 @@ static const struct clk_ops rv3032_clkout_ops = {
.unprepare = rv3032_clkout_unprepare,
.is_prepared = rv3032_clkout_is_prepared,
.recalc_rate = rv3032_clkout_recalc_rate,
.round_rate = rv3032_clkout_round_rate,
.determine_rate = rv3032_clkout_determine_rate,
.set_rate = rv3032_clkout_set_rate,
};

View file

@ -549,25 +549,25 @@ static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask)
writeb(mask, info->base + S3C2410_INTP);
}
static struct s3c_rtc_data const s3c2410_rtc_data = {
static const struct s3c_rtc_data s3c2410_rtc_data = {
.irq_handler = s3c24xx_rtc_irq,
.enable = s3c24xx_rtc_enable,
.disable = s3c24xx_rtc_disable,
};
static struct s3c_rtc_data const s3c2416_rtc_data = {
static const struct s3c_rtc_data s3c2416_rtc_data = {
.irq_handler = s3c24xx_rtc_irq,
.enable = s3c24xx_rtc_enable,
.disable = s3c24xx_rtc_disable,
};
static struct s3c_rtc_data const s3c2443_rtc_data = {
static const struct s3c_rtc_data s3c2443_rtc_data = {
.irq_handler = s3c24xx_rtc_irq,
.enable = s3c24xx_rtc_enable,
.disable = s3c24xx_rtc_disable,
};
static struct s3c_rtc_data const s3c6410_rtc_data = {
static const struct s3c_rtc_data s3c6410_rtc_data = {
.needs_src_clk = true,
.irq_handler = s3c6410_rtc_irq,
.enable = s3c24xx_rtc_enable,

View file

@ -455,7 +455,7 @@ static void __exit sh_rtc_remove(struct platform_device *pdev)
clk_disable(rtc->clk);
}
static int __maybe_unused sh_rtc_suspend(struct device *dev)
static int sh_rtc_suspend(struct device *dev)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
@ -465,7 +465,7 @@ static int __maybe_unused sh_rtc_suspend(struct device *dev)
return 0;
}
static int __maybe_unused sh_rtc_resume(struct device *dev)
static int sh_rtc_resume(struct device *dev)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
@ -475,7 +475,7 @@ static int __maybe_unused sh_rtc_resume(struct device *dev)
return 0;
}
static SIMPLE_DEV_PM_OPS(sh_rtc_pm_ops, sh_rtc_suspend, sh_rtc_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(sh_rtc_pm_ops, sh_rtc_suspend, sh_rtc_resume);
static const struct of_device_id sh_rtc_of_match[] = {
{ .compatible = "renesas,sh-rtc", },
@ -492,7 +492,7 @@ MODULE_DEVICE_TABLE(of, sh_rtc_of_match);
static struct platform_driver sh_rtc_platform_driver __refdata = {
.driver = {
.name = DRV_NAME,
.pm = &sh_rtc_pm_ops,
.pm = pm_sleep_ptr(&sh_rtc_pm_ops),
.of_match_table = sh_rtc_of_match,
},
.remove = __exit_p(sh_rtc_remove),

View file

@ -24,8 +24,8 @@
static ssize_t
name_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s %s\n", dev_driver_string(dev->parent),
dev_name(dev->parent));
return sysfs_emit(buf, "%s %s\n", dev_driver_string(dev->parent),
dev_name(dev->parent));
}
static DEVICE_ATTR_RO(name);
@ -39,7 +39,7 @@ date_show(struct device *dev, struct device_attribute *attr, char *buf)
if (retval)
return retval;
return sprintf(buf, "%ptRd\n", &tm);
return sysfs_emit(buf, "%ptRd\n", &tm);
}
static DEVICE_ATTR_RO(date);
@ -53,7 +53,7 @@ time_show(struct device *dev, struct device_attribute *attr, char *buf)
if (retval)
return retval;
return sprintf(buf, "%ptRt\n", &tm);
return sysfs_emit(buf, "%ptRt\n", &tm);
}
static DEVICE_ATTR_RO(time);
@ -64,21 +64,17 @@ since_epoch_show(struct device *dev, struct device_attribute *attr, char *buf)
struct rtc_time tm;
retval = rtc_read_time(to_rtc_device(dev), &tm);
if (retval == 0) {
time64_t time;
if (retval)
return retval;
time = rtc_tm_to_time64(&tm);
retval = sprintf(buf, "%lld\n", time);
}
return retval;
return sysfs_emit(buf, "%lld\n", rtc_tm_to_time64(&tm));
}
static DEVICE_ATTR_RO(since_epoch);
static ssize_t
max_user_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", to_rtc_device(dev)->max_user_freq);
return sysfs_emit(buf, "%d\n", to_rtc_device(dev)->max_user_freq);
}
static ssize_t
@ -118,9 +114,9 @@ hctosys_show(struct device *dev, struct device_attribute *attr, char *buf)
if (rtc_hctosys_ret == 0 &&
strcmp(dev_name(&to_rtc_device(dev)->dev),
CONFIG_RTC_HCTOSYS_DEVICE) == 0)
return sprintf(buf, "1\n");
return sysfs_emit(buf, "1\n");
#endif
return sprintf(buf, "0\n");
return sysfs_emit(buf, "0\n");
}
static DEVICE_ATTR_RO(hctosys);
@ -128,7 +124,6 @@ static ssize_t
wakealarm_show(struct device *dev, struct device_attribute *attr, char *buf)
{
ssize_t retval;
time64_t alarm;
struct rtc_wkalrm alm;
/* Don't show disabled alarms. For uniformity, RTC alarms are
@ -140,12 +135,13 @@ wakealarm_show(struct device *dev, struct device_attribute *attr, char *buf)
* alarms after they trigger, to ensure one-shot semantics.
*/
retval = rtc_read_alarm(to_rtc_device(dev), &alm);
if (retval == 0 && alm.enabled) {
alarm = rtc_tm_to_time64(&alm.time);
retval = sprintf(buf, "%lld\n", alarm);
}
if (retval)
return retval;
return retval;
if (alm.enabled)
return sysfs_emit(buf, "%lld\n", rtc_tm_to_time64(&alm.time));
return 0;
}
static ssize_t
@ -222,10 +218,10 @@ offset_show(struct device *dev, struct device_attribute *attr, char *buf)
long offset;
retval = rtc_read_offset(to_rtc_device(dev), &offset);
if (retval == 0)
retval = sprintf(buf, "%ld\n", offset);
if (retval)
return retval;
return retval;
return sysfs_emit(buf, "%ld\n", offset);
}
static ssize_t
@ -246,8 +242,8 @@ static DEVICE_ATTR_RW(offset);
static ssize_t
range_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "[%lld,%llu]\n", to_rtc_device(dev)->range_min,
to_rtc_device(dev)->range_max);
return sysfs_emit(buf, "[%lld,%llu]\n", to_rtc_device(dev)->range_min,
to_rtc_device(dev)->range_max);
}
static DEVICE_ATTR_RO(range);
@ -302,11 +298,7 @@ static struct attribute_group rtc_attr_group = {
.is_visible = rtc_attr_is_visible,
.attrs = rtc_attrs,
};
static const struct attribute_group *rtc_attr_groups[] = {
&rtc_attr_group,
NULL
};
__ATTRIBUTE_GROUPS(rtc_attr);
const struct attribute_group **rtc_get_dev_attribute_groups(void)
{
@ -318,17 +310,21 @@ int rtc_add_groups(struct rtc_device *rtc, const struct attribute_group **grps)
size_t old_cnt = 0, add_cnt = 0, new_cnt;
const struct attribute_group **groups, **old;
if (!grps)
if (grps) {
for (groups = grps; *groups; groups++)
add_cnt++;
/* No need to modify current groups if nothing new is provided */
if (add_cnt == 0)
return 0;
} else {
return -EINVAL;
}
groups = rtc->dev.groups;
if (groups)
for (; *groups; groups++)
old_cnt++;
for (groups = grps; *groups; groups++)
add_cnt++;
new_cnt = old_cnt + add_cnt + 1;
groups = devm_kcalloc(&rtc->dev, new_cnt, sizeof(*groups), GFP_KERNEL);
if (!groups)

View file

@ -8,7 +8,7 @@
* include larger, battery-backed NV-SRAM, burst-mode access, and an RTC
* write counter.
*
* Copyright (C) 2011-2014 Joshua Kinard <kumba@gentoo.org>.
* Copyright (C) 2011-2014 Joshua Kinard <linux@kumba.dev>.
* Copyright (C) 2009 Matthias Fuchs <matthias.fuchs@esd-electronics.com>.
*
* References: