mirror of
https://github.com/torvalds/linux.git
synced 2025-08-15 14:11:42 +02:00
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:
commit
d2eedaa390
25 changed files with 507 additions and 247 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
49
Documentation/devicetree/bindings/rtc/nxp,lpc3220-rtc.yaml
Normal file
49
Documentation/devicetree/bindings/rtc/nxp,lpc3220-rtc.yaml
Normal 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>;
|
||||
};
|
||||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue