RTC for 6.12

New driver:
  - DFRobot SD2405AL
 
 Drivers:
  - stm32: add alarm A out and LSCO support
  - sun6i: disable automatic clock input switching
  - m48t59: set range
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmbzMJ8ACgkQY6TcMGxw
 OjIVpw//YU/pOMIjD7I+cVcedK8UNGSP5GKlpd/4rK8sN7U3MiowbnvpSQT8rHvG
 N8WaRUjKkZRV6+Yo8iX5JQXacGTFv/WFfqQDCr3QgnU934AGibQT4WAQCooc8IrP
 fKB3Wzljcdr5cZZCWL5IBDiB6P7oWRdbdAHvwQUeTDMbF8zFd2ortLKDdCdHykXd
 iiK8gDDh7iVt2eiQ8duhhvruQXMLmxc7GdNNkTG/hhO6kis4sMUuwNwf+ifuO0fD
 00HOJr0wEJkje5CeTlcU+R2z2Nf3PMnQMMyQzrQnoUv0AEaTcUDx4MiNN9YZ7Kds
 IGkJpVkH5UCMT2RyvVuKVcL2OWA9m2RP63NVfq1iV+lmFRf7neJlQ4Ul/sYcMaL8
 wEL8UwIx4ya61O7mIZT8wI4ntl0yNm+hJw5r2yrIX1Cb7BACK4g0PzK6SoOOsMwN
 xbDkMrFQlpQm5YNL1vSV7ojFYvBwbvJXJfU3WTxF9V0O5RgqFFUSn/mlYog/DCZD
 zenPXYBQo4tp6Jkmg2A8J5g1R/274PQf08k4IQo4HTqtkjHyNRMEILDnneFX8Q0r
 f5W0k1HaWeYR8k08n6XI8A12lAj232RoGvFAtwaxRrqaEM5I5plcwrZIsMcpLSfK
 z0qnreEs9OAL2ZxNUYNNpWyphT4X3IDDSpRkh0Vj0chwgu1IQwo=
 =sjE7
 -----END PGP SIGNATURE-----

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

Pull RTC updates from Alexandre Belloni:
 "More conversions of DT bindings to yaml. There is one new driver, for
  the DFRobot SD2405AL and support for important features of the stm32
  RTC. Summary:

  New driver:
   - DFRobot SD2405AL

  Drivers:
   - stm32: add alarm A out and LSCO support
   - sun6i: disable automatic clock input switching
   - m48t59: set range"

* tag 'rtc-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux:
  rtc: rc5t619: use proper module tables
  rtc: m48t59: set range
  dt-bindings: rtc: microcrystal,rv3028: add #clock-cells property
  rtc: m48t59: Remove division condition with direct comparison
  rtc: at91sam9: fix OF node leak in probe() error path
  rtc: sun6i: disable automatic clock input switching
  dt-bindings: rtc: Drop non-trivial duplicate compatibles
  dt-bindings: vendor-prefixes: Add DFRobot.
  dt-bindings: rtc: Add support for SD2405AL.
  rtc: Add driver for SD2405AL
  rtc: s35390a: Drop vendorless compatible string from match table
  rtc: twl: convert comma to semicolon
  dt-bindings: rtc: sprd,sc2731-rtc: convert to YAML
  rtc: stm32: add alarm A out feature
  rtc: stm32: add Low Speed Clock Output (LSCO) support
  rtc: stm32: add pinctrl and pinmux interfaces
  dt-bindings: rtc: stm32: describe pinmux nodes
This commit is contained in:
Linus Torvalds 2024-09-25 14:38:37 -07:00
commit b2149f948c
17 changed files with 633 additions and 39 deletions

View File

@ -22,6 +22,9 @@ properties:
interrupts:
maxItems: 1
"#clock-cells":
const: 0
trickle-resistor-ohms:
enum:
- 3000

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/sprd,sc2731-rtc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Spreadtrum SC2731 Real Time Clock
maintainers:
- Orson Zhai <orsonzhai@gmail.com>
- Baolin Wang <baolin.wang7@gmail.com>
- Chunyan Zhang <zhang.lyra@gmail.com>
properties:
compatible:
const: sprd,sc2731-rtc
reg:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg
- interrupts
allOf:
- $ref: rtc.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
pmic {
#address-cells = <1>;
#size-cells = <0>;
rtc@280 {
compatible = "sprd,sc2731-rtc";
reg = <0x280>;
interrupt-parent = <&sc2731_pmic>;
interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
};
};
...

View File

@ -1,26 +0,0 @@
Spreadtrum SC27xx Real Time Clock
Required properties:
- compatible: should be "sprd,sc2731-rtc".
- reg: address offset of rtc register.
- interrupts: rtc alarm interrupt.
Example:
sc2731_pmic: pmic@0 {
compatible = "sprd,sc2731";
reg = <0>;
spi-max-frequency = <26000000>;
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
interrupt-controller;
#interrupt-cells = <2>;
#address-cells = <1>;
#size-cells = <0>;
rtc@280 {
compatible = "sprd,sc2731-rtc";
reg = <0x280>;
interrupt-parent = <&sc2731_pmic>;
interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
};
};

View File

@ -53,6 +53,28 @@ properties:
override default rtc_ck parent clock phandle of the new parent clock of rtc_ck
maxItems: 1
patternProperties:
"^rtc-[a-z]+-[0-9]+$":
type: object
$ref: /schemas/pinctrl/pinmux-node.yaml
description: |
Configuration of STM32 RTC pins description. STM32 RTC is able to output
some signals on specific pins:
- LSCO (Low Speed Clock Output) that allow to output LSE clock on a pin.
- Alarm out that allow to send a pulse on a pin when alarm A of the RTC
expires.
additionalProperties: false
properties:
function:
enum:
- lsco
- alarm-a
pins:
enum:
- out1
- out2
- out2_rmp
allOf:
- if:
properties:
@ -68,6 +90,9 @@ allOf:
clock-names: false
patternProperties:
"^rtc-[a-z]+-[0-9]+$": false
required:
- st,syscfg
@ -83,6 +108,9 @@ allOf:
minItems: 2
maxItems: 2
patternProperties:
"^rtc-[a-z]+-[0-9]+$": false
required:
- clock-names
- st,syscfg

View File

@ -38,12 +38,13 @@ properties:
- dallas,ds1672
# Extremely Accurate I²C RTC with Integrated Crystal and SRAM
- dallas,ds3232
# SD2405AL Real-Time Clock
- dfrobot,sd2405al
# EM Microelectronic EM3027 RTC
- emmicro,em3027
# I2C-BUS INTERFACE REAL TIME CLOCK MODULE
- epson,rx8010
# I2C-BUS INTERFACE REAL TIME CLOCK MODULE
- epson,rx8025
- epson,rx8035
# I2C-BUS INTERFACE REAL TIME CLOCK MODULE with Battery Backed RAM
- epson,rx8111
@ -52,10 +53,6 @@ properties:
- epson,rx8581
# Android Goldfish Real-time Clock
- google,goldfish-rtc
# Intersil ISL1208 Low Power RTC with Battery Backed SRAM
- isil,isl1208
# Intersil ISL1218 Low Power RTC with Battery Backed SRAM
- isil,isl1218
# Mvebu Real-time Clock
- marvell,orion-rtc
# Maxim DS1742/DS1743 Real-time Clock
@ -68,8 +65,6 @@ properties:
- microcrystal,rv8523
# NXP LPC32xx SoC Real-time Clock
- nxp,lpc3220-rtc
# Real-time Clock Module
- pericom,pt7c4338
# I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
- ricoh,r2025sd
# I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC

View File

@ -368,6 +368,8 @@ patternProperties:
description: Devantech, Ltd.
"^dfi,.*":
description: DFI Inc.
"^dfrobot,.*":
description: DFRobot Corporation
"^dh,.*":
description: DH electronics GmbH
"^difrnce,.*":

View File

@ -6557,6 +6557,12 @@ F: include/net/devlink.h
F: include/uapi/linux/devlink.h
F: net/devlink/
DFROBOT SD2405AL RTC DRIVER
M: Tóth János <gomba007@gmail.com>
L: linux-rtc@vger.kernel.org
S: Maintained
F: drivers/rtc/rtc-sd2405al.c
DH ELECTRONICS IMX6 DHCOM/DHCOR BOARD SUPPORT
M: Christoph Niedermaier <cniedermaier@dh-electronics.com>
L: kernel@dh-electronics.com

View File

@ -743,6 +743,16 @@ config RTC_DRV_S5M
This driver can also be built as a module. If so, the module
will be called rtc-s5m.
config RTC_DRV_SD2405AL
tristate "DFRobot SD2405AL"
select REGMAP_I2C
help
If you say yes here you will get support for the
DFRobot SD2405AL I2C RTC Module.
This driver can also be built as a module. If so, the module
will be called rtc-sd2405al.
config RTC_DRV_SD3078
tristate "ZXW Shenzhen whwave SD3078"
select REGMAP_I2C
@ -1934,6 +1944,12 @@ config RTC_DRV_STM32
tristate "STM32 RTC"
select REGMAP_MMIO
depends on ARCH_STM32 || COMPILE_TEST
depends on OF
depends on PINCTRL
select PINMUX
select PINCONF
select GENERIC_PINCONF
depends on COMMON_CLK
help
If you say yes here you get support for the STM32 On-Chip
Real Time Clock.

View File

@ -163,6 +163,7 @@ obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o
obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
obj-$(CONFIG_RTC_DRV_SC27XX) += rtc-sc27xx.o
obj-$(CONFIG_RTC_DRV_SD2405AL) += rtc-sd2405al.o
obj-$(CONFIG_RTC_DRV_SD3078) += rtc-sd3078.o
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o

View File

@ -368,6 +368,7 @@ static int at91_rtc_probe(struct platform_device *pdev)
return ret;
rtc->gpbr = syscon_node_to_regmap(args.np);
of_node_put(args.np);
rtc->gpbr_offset = args.args[0];
if (IS_ERR(rtc->gpbr)) {
dev_err(&pdev->dev, "failed to retrieve gpbr regmap, aborting.\n");

View File

@ -132,7 +132,7 @@ static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm)
M48T59_WRITE((bin2bcd(tm->tm_mon + 1) & 0x1F), M48T59_MONTH);
M48T59_WRITE(bin2bcd(year % 100), M48T59_YEAR);
if (pdata->type == M48T59RTC_TYPE_M48T59 && (year / 100))
if (pdata->type == M48T59RTC_TYPE_M48T59 && (year >= 100))
val = (M48T59_WDAY_CEB | M48T59_WDAY_CB);
val |= (bin2bcd(tm->tm_wday) & 0x07);
M48T59_WRITE(val, M48T59_WDAY);
@ -458,6 +458,8 @@ static int m48t59_rtc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, m48t59);
m48t59->rtc->ops = &m48t59_rtc_ops;
m48t59->rtc->range_min = RTC_TIMESTAMP_BEGIN_1900;
m48t59->rtc->range_max = RTC_TIMESTAMP_END_2099;
nvmem_cfg.size = pdata->offset;
ret = devm_rtc_nvmem_register(m48t59->rtc, &nvmem_cfg);

View File

@ -429,14 +429,23 @@ static int rc5t619_rtc_probe(struct platform_device *pdev)
return devm_rtc_register_device(rtc->rtc);
}
static const struct platform_device_id rc5t619_rtc_id[] = {
{
.name = "rc5t619-rtc",
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(platform, rc5t619_rtc_id);
static struct platform_driver rc5t619_rtc_driver = {
.driver = {
.name = "rc5t619-rtc",
},
.probe = rc5t619_rtc_probe,
.id_table = rc5t619_rtc_id,
};
module_platform_driver(rc5t619_rtc_driver);
MODULE_ALIAS("platform:rc5t619-rtc");
MODULE_DESCRIPTION("RICOH RC5T619 RTC driver");
MODULE_LICENSE("GPL");

View File

@ -56,7 +56,6 @@ static const struct i2c_device_id s35390a_id[] = {
MODULE_DEVICE_TABLE(i2c, s35390a_id);
static const __maybe_unused struct of_device_id s35390a_of_match[] = {
{ .compatible = "s35390a" },
{ .compatible = "sii,s35390a" },
{ }
};

227
drivers/rtc/rtc-sd2405al.c Normal file
View File

@ -0,0 +1,227 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* RTC driver for the SD2405AL Real-Time Clock
*
* Datasheet:
* https://image.dfrobot.com/image/data/TOY0021/SD2405AL%20datasheet%20(Angelo%20v0.1).pdf
*
* Copyright (C) 2024 Tóth János <gomba007@gmail.com>
*/
#include <linux/bcd.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/rtc.h>
/* Real time clock registers */
#define SD2405AL_REG_T_SEC 0x00
#define SD2405AL_REG_T_MIN 0x01
#define SD2405AL_REG_T_HOUR 0x02
# define SD2405AL_BIT_12H_PM BIT(5)
# define SD2405AL_BIT_24H BIT(7)
#define SD2405AL_REG_T_WEEK 0x03
#define SD2405AL_REG_T_DAY 0x04
#define SD2405AL_REG_T_MON 0x05
#define SD2405AL_REG_T_YEAR 0x06
#define SD2405AL_NUM_T_REGS (SD2405AL_REG_T_YEAR - SD2405AL_REG_T_SEC + 1)
/* Control registers */
#define SD2405AL_REG_CTR1 0x0F
# define SD2405AL_BIT_WRTC2 BIT(2)
# define SD2405AL_BIT_WRTC3 BIT(7)
#define SD2405AL_REG_CTR2 0x10
# define SD2405AL_BIT_WRTC1 BIT(7)
#define SD2405AL_REG_CTR3 0x11
#define SD2405AL_REG_TTF 0x12
#define SD2405AL_REG_CNTDWN 0x13
/* General RAM */
#define SD2405AL_REG_M_START 0x14
#define SD2405AL_REG_M_END 0x1F
struct sd2405al {
struct device *dev;
struct rtc_device *rtc;
struct regmap *regmap;
};
static int sd2405al_enable_reg_write(struct sd2405al *sd2405al)
{
int ret;
/* order of writes is important */
ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR2,
SD2405AL_BIT_WRTC1, SD2405AL_BIT_WRTC1);
if (ret < 0)
return ret;
ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR1,
SD2405AL_BIT_WRTC2 | SD2405AL_BIT_WRTC3,
SD2405AL_BIT_WRTC2 | SD2405AL_BIT_WRTC3);
if (ret < 0)
return ret;
return 0;
}
static int sd2405al_disable_reg_write(struct sd2405al *sd2405al)
{
int ret;
/* order of writes is important */
ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR1,
SD2405AL_BIT_WRTC2 | SD2405AL_BIT_WRTC3, 0x00);
if (ret < 0)
return ret;
ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR2,
SD2405AL_BIT_WRTC1, 0x00);
if (ret < 0)
return ret;
return 0;
}
static int sd2405al_read_time(struct device *dev, struct rtc_time *time)
{
u8 data[SD2405AL_NUM_T_REGS] = { 0 };
struct sd2405al *sd2405al = dev_get_drvdata(dev);
int ret;
ret = regmap_bulk_read(sd2405al->regmap, SD2405AL_REG_T_SEC, data,
SD2405AL_NUM_T_REGS);
if (ret < 0)
return ret;
time->tm_sec = bcd2bin(data[SD2405AL_REG_T_SEC] & 0x7F);
time->tm_min = bcd2bin(data[SD2405AL_REG_T_MIN] & 0x7F);
if (data[SD2405AL_REG_T_HOUR] & SD2405AL_BIT_24H)
time->tm_hour = bcd2bin(data[SD2405AL_REG_T_HOUR] & 0x3F);
else
if (data[SD2405AL_REG_T_HOUR] & SD2405AL_BIT_12H_PM)
time->tm_hour = bcd2bin(data[SD2405AL_REG_T_HOUR]
& 0x1F) + 12;
else /* 12 hour mode, AM */
time->tm_hour = bcd2bin(data[SD2405AL_REG_T_HOUR]
& 0x1F);
time->tm_wday = bcd2bin(data[SD2405AL_REG_T_WEEK] & 0x07);
time->tm_mday = bcd2bin(data[SD2405AL_REG_T_DAY] & 0x3F);
time->tm_mon = bcd2bin(data[SD2405AL_REG_T_MON] & 0x1F) - 1;
time->tm_year = bcd2bin(data[SD2405AL_REG_T_YEAR]) + 100;
dev_dbg(sd2405al->dev, "read time: %ptR (%d)\n", time, time->tm_wday);
return 0;
}
static int sd2405al_set_time(struct device *dev, struct rtc_time *time)
{
u8 data[SD2405AL_NUM_T_REGS];
struct sd2405al *sd2405al = dev_get_drvdata(dev);
int ret;
data[SD2405AL_REG_T_SEC] = bin2bcd(time->tm_sec);
data[SD2405AL_REG_T_MIN] = bin2bcd(time->tm_min);
data[SD2405AL_REG_T_HOUR] = bin2bcd(time->tm_hour) | SD2405AL_BIT_24H;
data[SD2405AL_REG_T_DAY] = bin2bcd(time->tm_mday);
data[SD2405AL_REG_T_WEEK] = bin2bcd(time->tm_wday);
data[SD2405AL_REG_T_MON] = bin2bcd(time->tm_mon) + 1;
data[SD2405AL_REG_T_YEAR] = bin2bcd(time->tm_year - 100);
ret = sd2405al_enable_reg_write(sd2405al);
if (ret < 0)
return ret;
ret = regmap_bulk_write(sd2405al->regmap, SD2405AL_REG_T_SEC, data,
SD2405AL_NUM_T_REGS);
if (ret < 0)
return ret;
ret = regmap_write(sd2405al->regmap, SD2405AL_REG_TTF, 0x00);
if (ret < 0)
return ret;
ret = sd2405al_disable_reg_write(sd2405al);
if (ret < 0)
return ret;
dev_dbg(sd2405al->dev, "set time: %ptR (%d)\n", time, time->tm_wday);
return 0;
}
static const struct rtc_class_ops sd2405al_rtc_ops = {
.read_time = sd2405al_read_time,
.set_time = sd2405al_set_time,
};
static const struct regmap_config sd2405al_regmap_conf = {
.reg_bits = 8,
.val_bits = 8,
.max_register = SD2405AL_REG_M_END,
};
static int sd2405al_probe(struct i2c_client *client)
{
struct sd2405al *sd2405al;
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
sd2405al = devm_kzalloc(&client->dev, sizeof(*sd2405al), GFP_KERNEL);
if (!sd2405al)
return -ENOMEM;
sd2405al->dev = &client->dev;
sd2405al->regmap = devm_regmap_init_i2c(client, &sd2405al_regmap_conf);
if (IS_ERR(sd2405al->regmap))
return PTR_ERR(sd2405al->regmap);
sd2405al->rtc = devm_rtc_allocate_device(&client->dev);
if (IS_ERR(sd2405al->rtc))
return PTR_ERR(sd2405al->rtc);
sd2405al->rtc->ops = &sd2405al_rtc_ops;
sd2405al->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
sd2405al->rtc->range_max = RTC_TIMESTAMP_END_2099;
dev_set_drvdata(&client->dev, sd2405al);
ret = devm_rtc_register_device(sd2405al->rtc);
if (ret < 0)
return ret;
return 0;
}
static const struct i2c_device_id sd2405al_id[] = {
{ "sd2405al" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, sd2405al_id);
static const __maybe_unused struct of_device_id sd2405al_of_match[] = {
{ .compatible = "dfrobot,sd2405al" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sd2405al_of_match);
static struct i2c_driver sd2405al_driver = {
.driver = {
.name = "sd2405al",
.of_match_table = of_match_ptr(sd2405al_of_match),
},
.probe = sd2405al_probe,
.id_table = sd2405al_id,
};
module_i2c_driver(sd2405al_driver);
MODULE_AUTHOR("Tóth János <gomba007@gmail.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SD2405AL RTC driver");

View File

@ -7,12 +7,16 @@
#include <linux/bcd.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/errno.h>
#include <linux/iopoll.h>
#include <linux/ioport.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/platform_device.h>
#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
@ -42,6 +46,12 @@
#define STM32_RTC_CR_FMT BIT(6)
#define STM32_RTC_CR_ALRAE BIT(8)
#define STM32_RTC_CR_ALRAIE BIT(12)
#define STM32_RTC_CR_OSEL GENMASK(22, 21)
#define STM32_RTC_CR_OSEL_ALARM_A FIELD_PREP(STM32_RTC_CR_OSEL, 0x01)
#define STM32_RTC_CR_COE BIT(23)
#define STM32_RTC_CR_TAMPOE BIT(26)
#define STM32_RTC_CR_TAMPALRM_TYPE BIT(30)
#define STM32_RTC_CR_OUT2EN BIT(31)
/* STM32_RTC_ISR/STM32_RTC_ICSR bit fields */
#define STM32_RTC_ISR_ALRAWF BIT(0)
@ -78,6 +88,12 @@
/* STM32_RTC_SR/_SCR bit fields */
#define STM32_RTC_SR_ALRA BIT(0)
/* STM32_RTC_CFGR bit fields */
#define STM32_RTC_CFGR_OUT2_RMP BIT(0)
#define STM32_RTC_CFGR_LSCOEN GENMASK(2, 1)
#define STM32_RTC_CFGR_LSCOEN_OUT1 1
#define STM32_RTC_CFGR_LSCOEN_OUT2_RMP 2
/* STM32_RTC_VERR bit fields */
#define STM32_RTC_VERR_MINREV_SHIFT 0
#define STM32_RTC_VERR_MINREV GENMASK(3, 0)
@ -107,6 +123,14 @@
/* STM32 RTC driver time helpers */
#define SEC_PER_DAY (24 * 60 * 60)
/* STM32 RTC pinctrl helpers */
#define STM32_RTC_PINMUX(_name, _action, ...) { \
.name = (_name), \
.action = (_action), \
.groups = ((const char *[]){ __VA_ARGS__ }), \
.num_groups = ARRAY_SIZE(((const char *[]){ __VA_ARGS__ })), \
}
struct stm32_rtc;
struct stm32_rtc_registers {
@ -119,6 +143,7 @@ struct stm32_rtc_registers {
u16 wpr;
u16 sr;
u16 scr;
u16 cfgr;
u16 verr;
};
@ -134,6 +159,8 @@ struct stm32_rtc_data {
bool need_dbp;
bool need_accuracy;
bool rif_protected;
bool has_lsco;
bool has_alarm_out;
};
struct stm32_rtc {
@ -146,6 +173,7 @@ struct stm32_rtc {
struct clk *rtc_ck;
const struct stm32_rtc_data *data;
int irq_alarm;
struct clk *clk_lsco;
};
struct stm32_rtc_rif_resource {
@ -171,6 +199,209 @@ static void stm32_rtc_wpr_lock(struct stm32_rtc *rtc)
writel_relaxed(RTC_WPR_WRONG_KEY, rtc->base + regs->wpr);
}
enum stm32_rtc_pin_name {
NONE,
OUT1,
OUT2,
OUT2_RMP
};
static const struct pinctrl_pin_desc stm32_rtc_pinctrl_pins[] = {
PINCTRL_PIN(OUT1, "out1"),
PINCTRL_PIN(OUT2, "out2"),
PINCTRL_PIN(OUT2_RMP, "out2_rmp"),
};
static int stm32_rtc_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
{
return ARRAY_SIZE(stm32_rtc_pinctrl_pins);
}
static const char *stm32_rtc_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
unsigned int selector)
{
return stm32_rtc_pinctrl_pins[selector].name;
}
static int stm32_rtc_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
unsigned int selector,
const unsigned int **pins,
unsigned int *num_pins)
{
*pins = &stm32_rtc_pinctrl_pins[selector].number;
*num_pins = 1;
return 0;
}
static const struct pinctrl_ops stm32_rtc_pinctrl_ops = {
.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
.dt_free_map = pinconf_generic_dt_free_map,
.get_groups_count = stm32_rtc_pinctrl_get_groups_count,
.get_group_name = stm32_rtc_pinctrl_get_group_name,
.get_group_pins = stm32_rtc_pinctrl_get_group_pins,
};
struct stm32_rtc_pinmux_func {
const char *name;
const char * const *groups;
const unsigned int num_groups;
int (*action)(struct pinctrl_dev *pctl_dev, unsigned int pin);
};
static int stm32_rtc_pinmux_action_alarm(struct pinctrl_dev *pctldev, unsigned int pin)
{
struct stm32_rtc *rtc = pinctrl_dev_get_drvdata(pctldev);
struct stm32_rtc_registers regs = rtc->data->regs;
unsigned int cr = readl_relaxed(rtc->base + regs.cr);
unsigned int cfgr = readl_relaxed(rtc->base + regs.cfgr);
if (!rtc->data->has_alarm_out)
return -EPERM;
cr &= ~STM32_RTC_CR_OSEL;
cr |= STM32_RTC_CR_OSEL_ALARM_A;
cr &= ~STM32_RTC_CR_TAMPOE;
cr &= ~STM32_RTC_CR_COE;
cr &= ~STM32_RTC_CR_TAMPALRM_TYPE;
switch (pin) {
case OUT1:
cr &= ~STM32_RTC_CR_OUT2EN;
cfgr &= ~STM32_RTC_CFGR_OUT2_RMP;
break;
case OUT2:
cr |= STM32_RTC_CR_OUT2EN;
cfgr &= ~STM32_RTC_CFGR_OUT2_RMP;
break;
case OUT2_RMP:
cr |= STM32_RTC_CR_OUT2EN;
cfgr |= STM32_RTC_CFGR_OUT2_RMP;
break;
default:
return -EINVAL;
}
stm32_rtc_wpr_unlock(rtc);
writel_relaxed(cr, rtc->base + regs.cr);
writel_relaxed(cfgr, rtc->base + regs.cfgr);
stm32_rtc_wpr_lock(rtc);
return 0;
}
static int stm32_rtc_pinmux_lsco_available(struct pinctrl_dev *pctldev, unsigned int pin)
{
struct stm32_rtc *rtc = pinctrl_dev_get_drvdata(pctldev);
struct stm32_rtc_registers regs = rtc->data->regs;
unsigned int cr = readl_relaxed(rtc->base + regs.cr);
unsigned int cfgr = readl_relaxed(rtc->base + regs.cfgr);
unsigned int calib = STM32_RTC_CR_COE;
unsigned int tampalrm = STM32_RTC_CR_TAMPOE | STM32_RTC_CR_OSEL;
switch (pin) {
case OUT1:
if ((!(cr & STM32_RTC_CR_OUT2EN) &&
((cr & calib) || cr & tampalrm)) ||
((cr & calib) && (cr & tampalrm)))
return -EBUSY;
break;
case OUT2_RMP:
if ((cr & STM32_RTC_CR_OUT2EN) &&
(cfgr & STM32_RTC_CFGR_OUT2_RMP) &&
((cr & calib) || (cr & tampalrm)))
return -EBUSY;
break;
default:
return -EINVAL;
}
if (clk_get_rate(rtc->rtc_ck) != 32768)
return -ERANGE;
return 0;
}
static int stm32_rtc_pinmux_action_lsco(struct pinctrl_dev *pctldev, unsigned int pin)
{
struct stm32_rtc *rtc = pinctrl_dev_get_drvdata(pctldev);
struct stm32_rtc_registers regs = rtc->data->regs;
struct device *dev = rtc->rtc_dev->dev.parent;
u8 lscoen;
int ret;
if (!rtc->data->has_lsco)
return -EPERM;
ret = stm32_rtc_pinmux_lsco_available(pctldev, pin);
if (ret)
return ret;
lscoen = (pin == OUT1) ? STM32_RTC_CFGR_LSCOEN_OUT1 : STM32_RTC_CFGR_LSCOEN_OUT2_RMP;
rtc->clk_lsco = clk_register_gate(dev, "rtc_lsco", __clk_get_name(rtc->rtc_ck),
CLK_IGNORE_UNUSED | CLK_IS_CRITICAL,
rtc->base + regs.cfgr, lscoen, 0, NULL);
if (IS_ERR(rtc->clk_lsco))
return PTR_ERR(rtc->clk_lsco);
of_clk_add_provider(dev->of_node, of_clk_src_simple_get, rtc->clk_lsco);
return 0;
}
static const struct stm32_rtc_pinmux_func stm32_rtc_pinmux_functions[] = {
STM32_RTC_PINMUX("lsco", &stm32_rtc_pinmux_action_lsco, "out1", "out2_rmp"),
STM32_RTC_PINMUX("alarm-a", &stm32_rtc_pinmux_action_alarm, "out1", "out2", "out2_rmp"),
};
static int stm32_rtc_pinmux_get_functions_count(struct pinctrl_dev *pctldev)
{
return ARRAY_SIZE(stm32_rtc_pinmux_functions);
}
static const char *stm32_rtc_pinmux_get_fname(struct pinctrl_dev *pctldev, unsigned int selector)
{
return stm32_rtc_pinmux_functions[selector].name;
}
static int stm32_rtc_pinmux_get_groups(struct pinctrl_dev *pctldev, unsigned int selector,
const char * const **groups, unsigned int * const num_groups)
{
*groups = stm32_rtc_pinmux_functions[selector].groups;
*num_groups = stm32_rtc_pinmux_functions[selector].num_groups;
return 0;
}
static int stm32_rtc_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int selector,
unsigned int group)
{
struct stm32_rtc_pinmux_func selected_func = stm32_rtc_pinmux_functions[selector];
struct pinctrl_pin_desc pin = stm32_rtc_pinctrl_pins[group];
/* Call action */
if (selected_func.action)
return selected_func.action(pctldev, pin.number);
return -EINVAL;
}
static const struct pinmux_ops stm32_rtc_pinmux_ops = {
.get_functions_count = stm32_rtc_pinmux_get_functions_count,
.get_function_name = stm32_rtc_pinmux_get_fname,
.get_function_groups = stm32_rtc_pinmux_get_groups,
.set_mux = stm32_rtc_pinmux_set_mux,
.strict = true,
};
static struct pinctrl_desc stm32_rtc_pdesc = {
.name = DRIVER_NAME,
.pins = stm32_rtc_pinctrl_pins,
.npins = ARRAY_SIZE(stm32_rtc_pinctrl_pins),
.owner = THIS_MODULE,
.pctlops = &stm32_rtc_pinctrl_ops,
.pmxops = &stm32_rtc_pinmux_ops,
};
static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc)
{
const struct stm32_rtc_registers *regs = &rtc->data->regs;
@ -576,6 +807,8 @@ static const struct stm32_rtc_data stm32_rtc_data = {
.need_dbp = true,
.need_accuracy = false,
.rif_protected = false,
.has_lsco = false,
.has_alarm_out = false,
.regs = {
.tr = 0x00,
.dr = 0x04,
@ -586,6 +819,7 @@ static const struct stm32_rtc_data stm32_rtc_data = {
.wpr = 0x24,
.sr = 0x0C, /* set to ISR offset to ease alarm management */
.scr = UNDEF_REG,
.cfgr = UNDEF_REG,
.verr = UNDEF_REG,
},
.events = {
@ -599,6 +833,8 @@ static const struct stm32_rtc_data stm32h7_rtc_data = {
.need_dbp = true,
.need_accuracy = false,
.rif_protected = false,
.has_lsco = false,
.has_alarm_out = false,
.regs = {
.tr = 0x00,
.dr = 0x04,
@ -609,6 +845,7 @@ static const struct stm32_rtc_data stm32h7_rtc_data = {
.wpr = 0x24,
.sr = 0x0C, /* set to ISR offset to ease alarm management */
.scr = UNDEF_REG,
.cfgr = UNDEF_REG,
.verr = UNDEF_REG,
},
.events = {
@ -631,6 +868,8 @@ static const struct stm32_rtc_data stm32mp1_data = {
.need_dbp = false,
.need_accuracy = true,
.rif_protected = false,
.has_lsco = true,
.has_alarm_out = true,
.regs = {
.tr = 0x00,
.dr = 0x04,
@ -641,6 +880,7 @@ static const struct stm32_rtc_data stm32mp1_data = {
.wpr = 0x24,
.sr = 0x50,
.scr = 0x5C,
.cfgr = 0x60,
.verr = 0x3F4,
},
.events = {
@ -654,6 +894,8 @@ static const struct stm32_rtc_data stm32mp25_data = {
.need_dbp = false,
.need_accuracy = true,
.rif_protected = true,
.has_lsco = true,
.has_alarm_out = true,
.regs = {
.tr = 0x00,
.dr = 0x04,
@ -664,6 +906,7 @@ static const struct stm32_rtc_data stm32mp25_data = {
.wpr = 0x24,
.sr = 0x50,
.scr = 0x5C,
.cfgr = 0x60,
.verr = 0x3F4,
},
.events = {
@ -681,6 +924,30 @@ static const struct of_device_id stm32_rtc_of_match[] = {
};
MODULE_DEVICE_TABLE(of, stm32_rtc_of_match);
static void stm32_rtc_clean_outs(struct stm32_rtc *rtc)
{
struct stm32_rtc_registers regs = rtc->data->regs;
unsigned int cr = readl_relaxed(rtc->base + regs.cr);
cr &= ~STM32_RTC_CR_OSEL;
cr &= ~STM32_RTC_CR_TAMPOE;
cr &= ~STM32_RTC_CR_COE;
cr &= ~STM32_RTC_CR_TAMPALRM_TYPE;
cr &= ~STM32_RTC_CR_OUT2EN;
stm32_rtc_wpr_unlock(rtc);
writel_relaxed(cr, rtc->base + regs.cr);
stm32_rtc_wpr_lock(rtc);
if (regs.cfgr != UNDEF_REG) {
unsigned int cfgr = readl_relaxed(rtc->base + regs.cfgr);
cfgr &= ~STM32_RTC_CFGR_LSCOEN;
cfgr &= ~STM32_RTC_CFGR_OUT2_RMP;
writel_relaxed(cfgr, rtc->base + regs.cfgr);
}
}
static int stm32_rtc_check_rif(struct stm32_rtc *stm32_rtc,
struct stm32_rtc_rif_resource res)
{
@ -791,6 +1058,7 @@ static int stm32_rtc_probe(struct platform_device *pdev)
{
struct stm32_rtc *rtc;
const struct stm32_rtc_registers *regs;
struct pinctrl_dev *pctl;
int ret;
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
@ -912,6 +1180,16 @@ static int stm32_rtc_probe(struct platform_device *pdev)
goto err;
}
stm32_rtc_clean_outs(rtc);
ret = devm_pinctrl_register_and_init(&pdev->dev, &stm32_rtc_pdesc, rtc, &pctl);
if (ret)
return dev_err_probe(&pdev->dev, ret, "pinctrl register failed");
ret = pinctrl_enable(pctl);
if (ret)
return dev_err_probe(&pdev->dev, ret, "pinctrl enable failed");
/*
* If INITS flag is reset (calendar year field set to 0x00), calendar
* must be initialized
@ -950,6 +1228,9 @@ static void stm32_rtc_remove(struct platform_device *pdev)
const struct stm32_rtc_registers *regs = &rtc->data->regs;
unsigned int cr;
if (!IS_ERR_OR_NULL(rtc->clk_lsco))
clk_unregister_gate(rtc->clk_lsco);
/* Disable interrupts */
stm32_rtc_wpr_unlock(rtc);
cr = readl_relaxed(rtc->base + regs->cr);

View File

@ -402,6 +402,7 @@ CLK_OF_DECLARE_DRIVER(sun8i_r40_rtc_clk, "allwinner,sun8i-r40-rtc",
static const struct sun6i_rtc_clk_data sun8i_v3_rtc_data = {
.rc_osc_rate = 32000,
.has_out_clk = 1,
.has_auto_swt = 1,
};
static void __init sun8i_v3_rtc_clk_init(struct device_node *node)

View File

@ -591,8 +591,8 @@ static int twl_rtc_probe(struct platform_device *pdev)
memset(&nvmem_cfg, 0, sizeof(nvmem_cfg));
nvmem_cfg.name = "twl-secured-";
nvmem_cfg.type = NVMEM_TYPE_BATTERY_BACKED;
nvmem_cfg.reg_read = twl_nvram_read,
nvmem_cfg.reg_write = twl_nvram_write,
nvmem_cfg.reg_read = twl_nvram_read;
nvmem_cfg.reg_write = twl_nvram_write;
nvmem_cfg.word_size = 1;
nvmem_cfg.stride = 1;
if (twl_class_is_4030()) {