summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-11-05 18:49:40 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2023-11-05 18:49:40 -0800
commitd2f51b3516dade79269ff45eae2a7668ae711b25 (patch)
tree06f13c8e0078869b44f9fd00a99128e9d607df10 /drivers
parent7b2c9e41e73fbe50f519072009b1e624ea230163 (diff)
parentcfb67623ce281e045ec11e3eddb1b68b879b53a1 (diff)
Merge tag 'rtc-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux
Pull RTC updates from Alexandre Belloni: "There is a new driver for the RTC of the Mstar SSD202D SoC. The rtc7301 driver gains support for byte addresses to support the USRobotics USR8200. Then we have many non user visible changes and typo fixes. Summary: Subsytem: - convert platform drivers to remove_new - prevent modpost warnings for unremovable platform drivers New driver: - Mstar SSD202D Drivers: - brcmstb-waketimer: support level alarm_irq - ep93xx: add DT support - rtc7301: support byte-addressed IO" * tag 'rtc-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (28 commits) dt-bindings: rtc: Add Mstar SSD202D RTC rtc: Add support for the SSD202D RTC rtc: at91rm9200: annotate at91_rtc_remove with __exit again dt-bindings: rtc: microcrystal,rv3032: Document wakeup-source property dt-bindings: rtc: pcf8523: Convert to YAML dt-bindings: rtc: mcp795: move to trivial-rtc rtc: ep93xx: add DT support for Cirrus EP93xx dt-bindings: rtc: Add Cirrus EP93xx dt-bindings: rtc: pcf2123: convert to YAML rtc: efi: fixed typo in efi_procfs() rtc: omap: Use device_get_match_data() rtc: pcf85363: fix wrong mask/val parameters in regmap_update_bits call rtc: rtc7301: Support byte-addressed IO rtc: rtc7301: Rewrite bindings in schema rtc: sh: Convert to platform remove callback returning void rtc: pxa: Convert to platform remove callback returning void rtc: mv: Convert to platform remove callback returning void rtc: imxdi: Convert to platform remove callback returning void rtc: at91rm9200: Convert to platform remove callback returning void rtc: pcap: Drop no-op remove function ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/rtc/Kconfig13
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-at91rm9200.c14
-rw-r--r--drivers/rtc/rtc-brcmstb-waketimer.c47
-rw-r--r--drivers/rtc/rtc-efi.c2
-rw-r--r--drivers/rtc/rtc-ep93xx.c8
-rw-r--r--drivers/rtc/rtc-imxdi.c14
-rw-r--r--drivers/rtc/rtc-mv.c14
-rw-r--r--drivers/rtc/rtc-omap.c8
-rw-r--r--drivers/rtc/rtc-pcap.c6
-rw-r--r--drivers/rtc/rtc-pcf85363.c2
-rw-r--r--drivers/rtc/rtc-pxa.c13
-rw-r--r--drivers/rtc/rtc-r7301.c35
-rw-r--r--drivers/rtc/rtc-sh.c16
-rw-r--r--drivers/rtc/rtc-ssd202d.c249
15 files changed, 396 insertions, 46 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index d7502433c78a..3814e0845e77 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1351,7 +1351,7 @@ config RTC_DRV_DIGICOLOR
config RTC_DRV_IMXDI
tristate "Freescale IMX DryIce Real Time Clock"
- depends on ARCH_MXC
+ depends on ARCH_MXC || COMPILE_TEST
depends on OF
help
Support for Freescale IMX DryIce RTC
@@ -1984,4 +1984,15 @@ config RTC_DRV_POLARFIRE_SOC
This driver can also be built as a module, if so, the module
will be called "rtc-mpfs".
+config RTC_DRV_SSD202D
+ tristate "SigmaStar SSD202D RTC"
+ depends on ARCH_MSTARV7 || COMPILE_TEST
+ default ARCH_MSTARV7
+ help
+ If you say yes here you get support for the SigmaStar SSD202D On-Chip
+ Real Time Clock.
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-ssd20xd".
+
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index fd209883ee2e..7b03c3abfd78 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -103,6 +103,7 @@ obj-$(CONFIG_RTC_DRV_MESON) += rtc-meson.o
obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o
obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
obj-$(CONFIG_RTC_DRV_MSC313) += rtc-msc313.o
+obj-$(CONFIG_RTC_DRV_SSD202D) += rtc-ssd202d.o
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
obj-$(CONFIG_RTC_DRV_MT2712) += rtc-mt2712.o
obj-$(CONFIG_RTC_DRV_MT6397) += rtc-mt6397.o
diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c
index add4f71d7b3b..c16fe711a0d9 100644
--- a/drivers/rtc/rtc-at91rm9200.c
+++ b/drivers/rtc/rtc-at91rm9200.c
@@ -558,7 +558,7 @@ err_clk:
/*
* Disable and remove the RTC driver
*/
-static int __exit at91_rtc_remove(struct platform_device *pdev)
+static void __exit at91_rtc_remove(struct platform_device *pdev)
{
/* Disable all interrupts */
at91_rtc_write_idr(AT91_RTC_ACKUPD | AT91_RTC_ALARM |
@@ -566,8 +566,6 @@ static int __exit at91_rtc_remove(struct platform_device *pdev)
AT91_RTC_CALEV);
clk_disable_unprepare(sclk);
-
- return 0;
}
static void at91_rtc_shutdown(struct platform_device *pdev)
@@ -635,8 +633,14 @@ static int at91_rtc_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume);
-static struct platform_driver at91_rtc_driver = {
- .remove = __exit_p(at91_rtc_remove),
+/*
+ * at91_rtc_remove() lives in .exit.text. For drivers registered via
+ * module_platform_driver_probe() this is ok because they cannot get unbound at
+ * runtime. So mark the driver struct with __refdata to prevent modpost
+ * triggering a section mismatch warning.
+ */
+static struct platform_driver at91_rtc_driver __refdata = {
+ .remove_new = __exit_p(at91_rtc_remove),
.shutdown = at91_rtc_shutdown,
.driver = {
.name = "at91_rtc",
diff --git a/drivers/rtc/rtc-brcmstb-waketimer.c b/drivers/rtc/rtc-brcmstb-waketimer.c
index 3cdc015692ca..1a65a4e0dc00 100644
--- a/drivers/rtc/rtc-brcmstb-waketimer.c
+++ b/drivers/rtc/rtc-brcmstb-waketimer.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright © 2014-2017 Broadcom
+ * Copyright © 2014-2023 Broadcom
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -34,6 +34,7 @@ struct brcmstb_waketmr {
u32 rate;
unsigned long rtc_alarm;
bool alarm_en;
+ bool alarm_expired;
};
#define BRCMSTB_WKTMR_EVENT 0x00
@@ -64,6 +65,11 @@ static inline void brcmstb_waketmr_clear_alarm(struct brcmstb_waketmr *timer)
writel_relaxed(reg - 1, timer->base + BRCMSTB_WKTMR_ALARM);
writel_relaxed(WKTMR_ALARM_EVENT, timer->base + BRCMSTB_WKTMR_EVENT);
(void)readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT);
+ if (timer->alarm_expired) {
+ timer->alarm_expired = false;
+ /* maintain call balance */
+ enable_irq(timer->alarm_irq);
+ }
}
static void brcmstb_waketmr_set_alarm(struct brcmstb_waketmr *timer,
@@ -105,10 +111,17 @@ static irqreturn_t brcmstb_alarm_irq(int irq, void *data)
return IRQ_HANDLED;
if (timer->alarm_en) {
- if (!device_may_wakeup(timer->dev))
+ if (device_may_wakeup(timer->dev)) {
+ disable_irq_nosync(irq);
+ timer->alarm_expired = true;
+ } else {
writel_relaxed(WKTMR_ALARM_EVENT,
timer->base + BRCMSTB_WKTMR_EVENT);
+ }
rtc_update_irq(timer->rtc, 1, RTC_IRQF | RTC_AF);
+ } else {
+ writel_relaxed(WKTMR_ALARM_EVENT,
+ timer->base + BRCMSTB_WKTMR_EVENT);
}
return IRQ_HANDLED;
@@ -221,8 +234,14 @@ static int brcmstb_waketmr_alarm_enable(struct device *dev,
!brcmstb_waketmr_is_pending(timer))
return -EINVAL;
timer->alarm_en = true;
- if (timer->alarm_irq)
+ if (timer->alarm_irq) {
+ if (timer->alarm_expired) {
+ timer->alarm_expired = false;
+ /* maintain call balance */
+ enable_irq(timer->alarm_irq);
+ }
enable_irq(timer->alarm_irq);
+ }
} else if (!enabled && timer->alarm_en) {
if (timer->alarm_irq)
disable_irq(timer->alarm_irq);
@@ -352,6 +371,17 @@ static int brcmstb_waketmr_suspend(struct device *dev)
return brcmstb_waketmr_prepare_suspend(timer);
}
+static int brcmstb_waketmr_suspend_noirq(struct device *dev)
+{
+ struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
+
+ /* Catch any alarms occurring prior to noirq */
+ if (timer->alarm_expired && device_may_wakeup(dev))
+ return -EBUSY;
+
+ return 0;
+}
+
static int brcmstb_waketmr_resume(struct device *dev)
{
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
@@ -368,10 +398,17 @@ static int brcmstb_waketmr_resume(struct device *dev)
return ret;
}
+#else
+#define brcmstb_waketmr_suspend NULL
+#define brcmstb_waketmr_suspend_noirq NULL
+#define brcmstb_waketmr_resume NULL
#endif /* CONFIG_PM_SLEEP */
-static SIMPLE_DEV_PM_OPS(brcmstb_waketmr_pm_ops,
- brcmstb_waketmr_suspend, brcmstb_waketmr_resume);
+static const struct dev_pm_ops brcmstb_waketmr_pm_ops = {
+ .suspend = brcmstb_waketmr_suspend,
+ .suspend_noirq = brcmstb_waketmr_suspend_noirq,
+ .resume = brcmstb_waketmr_resume,
+};
static const __maybe_unused struct of_device_id brcmstb_waketmr_of_match[] = {
{ .compatible = "brcm,brcmstb-waketimer" },
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index dc6b0f4a54e2..fa8bf82df948 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -227,7 +227,7 @@ static int efi_procfs(struct device *dev, struct seq_file *seq)
enabled == 1 ? "yes" : "no",
pending == 1 ? "yes" : "no");
- if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE)
+ if (alm.timezone == EFI_UNSPECIFIED_TIMEZONE)
seq_puts(seq, "Timezone\t: unspecified\n");
else
/* XXX fixme: convert to string? */
diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c
index acae7f16808f..1fdd20d01560 100644
--- a/drivers/rtc/rtc-ep93xx.c
+++ b/drivers/rtc/rtc-ep93xx.c
@@ -7,6 +7,7 @@
*/
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/rtc.h>
#include <linux/platform_device.h>
#include <linux/io.h>
@@ -148,9 +149,16 @@ static int ep93xx_rtc_probe(struct platform_device *pdev)
return devm_rtc_register_device(ep93xx_rtc->rtc);
}
+static const struct of_device_id ep93xx_rtc_of_ids[] = {
+ { .compatible = "cirrus,ep9301-rtc" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ep93xx_rtc_of_ids);
+
static struct platform_driver ep93xx_rtc_driver = {
.driver = {
.name = "ep93xx-rtc",
+ .of_match_table = ep93xx_rtc_of_ids,
},
.probe = ep93xx_rtc_probe,
};
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
index 4b712e5ab08a..284011c419db 100644
--- a/drivers/rtc/rtc-imxdi.c
+++ b/drivers/rtc/rtc-imxdi.c
@@ -830,7 +830,7 @@ err:
return rc;
}
-static int __exit dryice_rtc_remove(struct platform_device *pdev)
+static void __exit dryice_rtc_remove(struct platform_device *pdev)
{
struct imxdi_dev *imxdi = platform_get_drvdata(pdev);
@@ -840,8 +840,6 @@ static int __exit dryice_rtc_remove(struct platform_device *pdev)
writel(0, imxdi->ioaddr + DIER);
clk_disable_unprepare(imxdi->clk);
-
- return 0;
}
static const struct of_device_id dryice_dt_ids[] = {
@@ -851,12 +849,18 @@ static const struct of_device_id dryice_dt_ids[] = {
MODULE_DEVICE_TABLE(of, dryice_dt_ids);
-static struct platform_driver dryice_rtc_driver = {
+/*
+ * dryice_rtc_remove() lives in .exit.text. For drivers registered via
+ * module_platform_driver_probe() this is ok because they cannot get unbound at
+ * runtime. So mark the driver struct with __refdata to prevent modpost
+ * triggering a section mismatch warning.
+ */
+static struct platform_driver dryice_rtc_driver __refdata = {
.driver = {
.name = "imxdi_rtc",
.of_match_table = dryice_dt_ids,
},
- .remove = __exit_p(dryice_rtc_remove),
+ .remove_new = __exit_p(dryice_rtc_remove),
};
module_platform_driver_probe(dryice_rtc_driver, dryice_rtc_probe);
diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c
index 6c526e2ec56d..db31da56bfa7 100644
--- a/drivers/rtc/rtc-mv.c
+++ b/drivers/rtc/rtc-mv.c
@@ -282,7 +282,7 @@ out:
return ret;
}
-static int __exit mv_rtc_remove(struct platform_device *pdev)
+static void __exit mv_rtc_remove(struct platform_device *pdev)
{
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
@@ -291,8 +291,6 @@ static int __exit mv_rtc_remove(struct platform_device *pdev)
if (!IS_ERR(pdata->clk))
clk_disable_unprepare(pdata->clk);
-
- return 0;
}
#ifdef CONFIG_OF
@@ -303,8 +301,14 @@ static const struct of_device_id rtc_mv_of_match_table[] = {
MODULE_DEVICE_TABLE(of, rtc_mv_of_match_table);
#endif
-static struct platform_driver mv_rtc_driver = {
- .remove = __exit_p(mv_rtc_remove),
+/*
+ * mv_rtc_remove() lives in .exit.text. For drivers registered via
+ * module_platform_driver_probe() this is ok because they cannot get unbound at
+ * runtime. So mark the driver struct with __refdata to prevent modpost
+ * triggering a section mismatch warning.
+ */
+static struct platform_driver mv_rtc_driver __refdata = {
+ .remove_new = __exit_p(mv_rtc_remove),
.driver = {
.name = "rtc-mv",
.of_match_table = of_match_ptr(rtc_mv_of_match_table),
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index 5b10ab06cd2e..c6155c48a4ac 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -18,12 +18,12 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/rtc.h>
#include <linux/rtc/rtc-omap.h>
@@ -729,16 +729,14 @@ static int omap_rtc_probe(struct platform_device *pdev)
struct omap_rtc *rtc;
u8 reg, mask, new_ctrl;
const struct platform_device_id *id_entry;
- const struct of_device_id *of_id;
int ret;
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
if (!rtc)
return -ENOMEM;
- of_id = of_match_device(omap_rtc_of_match, &pdev->dev);
- if (of_id) {
- rtc->type = of_id->data;
+ rtc->type = device_get_match_data(&pdev->dev);
+ if (rtc->type) {
rtc->is_pmic_controller = rtc->type->has_pmic_mode &&
of_device_is_system_power_controller(pdev->dev.of_node);
} else {
diff --git a/drivers/rtc/rtc-pcap.c b/drivers/rtc/rtc-pcap.c
index 8c7a98a5452c..d6651611a0c6 100644
--- a/drivers/rtc/rtc-pcap.c
+++ b/drivers/rtc/rtc-pcap.c
@@ -166,13 +166,7 @@ static int __init pcap_rtc_probe(struct platform_device *pdev)
return devm_rtc_register_device(pcap_rtc->rtc);
}
-static int __exit pcap_rtc_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static struct platform_driver pcap_rtc_driver = {
- .remove = __exit_p(pcap_rtc_remove),
.driver = {
.name = "pcap-rtc",
},
diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c
index 06194674d71c..540042b9eec8 100644
--- a/drivers/rtc/rtc-pcf85363.c
+++ b/drivers/rtc/rtc-pcf85363.c
@@ -438,7 +438,7 @@ static int pcf85363_probe(struct i2c_client *client)
if (client->irq > 0 || wakeup_source) {
regmap_write(pcf85363->regmap, CTRL_FLAGS, 0);
regmap_update_bits(pcf85363->regmap, CTRL_PIN_IO,
- PIN_IO_INTA_OUT, PIN_IO_INTAPM);
+ PIN_IO_INTAPM, PIN_IO_INTA_OUT);
}
if (client->irq > 0) {
diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c
index e400c78252e8..cdb39fc4cab5 100644
--- a/drivers/rtc/rtc-pxa.c
+++ b/drivers/rtc/rtc-pxa.c
@@ -365,12 +365,11 @@ static int __init pxa_rtc_probe(struct platform_device *pdev)
return 0;
}
-static int __exit pxa_rtc_remove(struct platform_device *pdev)
+static void __exit pxa_rtc_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
pxa_rtc_release(dev);
- return 0;
}
#ifdef CONFIG_OF
@@ -403,8 +402,14 @@ static int pxa_rtc_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(pxa_rtc_pm_ops, pxa_rtc_suspend, pxa_rtc_resume);
-static struct platform_driver pxa_rtc_driver = {
- .remove = __exit_p(pxa_rtc_remove),
+/*
+ * pxa_rtc_remove() lives in .exit.text. For drivers registered via
+ * module_platform_driver_probe() this is ok because they cannot get unbound at
+ * runtime. So mark the driver struct with __refdata to prevent modpost
+ * triggering a section mismatch warning.
+ */
+static struct platform_driver pxa_rtc_driver __refdata = {
+ .remove_new = __exit_p(pxa_rtc_remove),
.driver = {
.name = "pxa-rtc",
.of_match_table = of_match_ptr(pxa_rtc_dt_ids),
diff --git a/drivers/rtc/rtc-r7301.c b/drivers/rtc/rtc-r7301.c
index 5dbaeb7af648..ef913cf8593f 100644
--- a/drivers/rtc/rtc-r7301.c
+++ b/drivers/rtc/rtc-r7301.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/delay.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
@@ -55,12 +56,23 @@ struct rtc7301_priv {
u8 bank;
};
-static const struct regmap_config rtc7301_regmap_config = {
+/*
+ * When the device is memory-mapped, some platforms pack the registers into
+ * 32-bit access using the lower 8 bits at each 4-byte stride, while others
+ * expose them as simply consecutive bytes.
+ */
+static const struct regmap_config rtc7301_regmap_32_config = {
.reg_bits = 32,
.val_bits = 8,
.reg_stride = 4,
};
+static const struct regmap_config rtc7301_regmap_8_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .reg_stride = 1,
+};
+
static u8 rtc7301_read(struct rtc7301_priv *priv, unsigned int reg)
{
int reg_stride = regmap_get_reg_stride(priv->regmap);
@@ -356,7 +368,9 @@ static int __init rtc7301_rtc_probe(struct platform_device *dev)
void __iomem *regs;
struct rtc7301_priv *priv;
struct rtc_device *rtc;
+ static const struct regmap_config *mapconf;
int ret;
+ u32 val;
priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -366,8 +380,25 @@ static int __init rtc7301_rtc_probe(struct platform_device *dev)
if (IS_ERR(regs))
return PTR_ERR(regs);
+ ret = device_property_read_u32(&dev->dev, "reg-io-width", &val);
+ if (ret)
+ /* Default to 32bit accesses */
+ val = 4;
+
+ switch (val) {
+ case 1:
+ mapconf = &rtc7301_regmap_8_config;
+ break;
+ case 4:
+ mapconf = &rtc7301_regmap_32_config;
+ break;
+ default:
+ dev_err(&dev->dev, "invalid reg-io-width %d\n", val);
+ return -EINVAL;
+ }
+
priv->regmap = devm_regmap_init_mmio(&dev->dev, regs,
- &rtc7301_regmap_config);
+ mapconf);
if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap);
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
index cd146b574143..27a191fa3704 100644
--- a/drivers/rtc/rtc-sh.c
+++ b/drivers/rtc/rtc-sh.c
@@ -469,7 +469,7 @@ static int __init sh_rtc_probe(struct platform_device *pdev)
{
struct sh_rtc *rtc;
struct resource *res;
- char clk_name[6];
+ char clk_name[14];
int clk_id, ret;
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
@@ -620,7 +620,7 @@ err_unmap:
return ret;
}
-static int __exit sh_rtc_remove(struct platform_device *pdev)
+static void __exit sh_rtc_remove(struct platform_device *pdev)
{
struct sh_rtc *rtc = platform_get_drvdata(pdev);
@@ -628,8 +628,6 @@ static int __exit sh_rtc_remove(struct platform_device *pdev)
sh_rtc_setcie(&pdev->dev, 0);
clk_disable(rtc->clk);
-
- return 0;
}
static void sh_rtc_set_irq_wake(struct device *dev, int enabled)
@@ -668,13 +666,19 @@ static const struct of_device_id sh_rtc_of_match[] = {
};
MODULE_DEVICE_TABLE(of, sh_rtc_of_match);
-static struct platform_driver sh_rtc_platform_driver = {
+/*
+ * sh_rtc_remove() lives in .exit.text. For drivers registered via
+ * module_platform_driver_probe() this is ok because they cannot get unbound at
+ * runtime. So mark the driver struct with __refdata to prevent modpost
+ * triggering a section mismatch warning.
+ */
+static struct platform_driver sh_rtc_platform_driver __refdata = {
.driver = {
.name = DRV_NAME,
.pm = &sh_rtc_pm_ops,
.of_match_table = sh_rtc_of_match,
},
- .remove = __exit_p(sh_rtc_remove),
+ .remove_new = __exit_p(sh_rtc_remove),
};
module_platform_driver_probe(sh_rtc_platform_driver, sh_rtc_probe);
diff --git a/drivers/rtc/rtc-ssd202d.c b/drivers/rtc/rtc-ssd202d.c
new file mode 100644
index 000000000000..ed6493260096
--- /dev/null
+++ b/drivers/rtc/rtc-ssd202d.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Real time clocks driver for MStar/SigmaStar SSD202D SoCs.
+ *
+ * (C) 2021 Daniel Palmer
+ * (C) 2023 Romain Perier
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/regmap.h>
+#include <linux/pm.h>
+
+#define REG_CTRL 0x0
+#define REG_CTRL1 0x4
+#define REG_ISO_CTRL 0xc
+#define REG_WRDATA_L 0x10
+#define REG_WRDATA_H 0x14
+#define REG_ISOACK 0x20
+#define REG_RDDATA_L 0x24
+#define REG_RDDATA_H 0x28
+#define REG_RDCNT_L 0x30
+#define REG_RDCNT_H 0x34
+#define REG_CNT_TRIG 0x38
+#define REG_PWRCTRL 0x3c
+#define REG_RTC_TEST 0x54
+
+#define CNT_RD_TRIG_BIT BIT(0)
+#define CNT_RD_BIT BIT(0)
+#define BASE_WR_BIT BIT(1)
+#define BASE_RD_BIT BIT(2)
+#define CNT_RST_BIT BIT(3)
+#define ISO_CTRL_ACK_MASK BIT(3)
+#define ISO_CTRL_ACK_SHIFT 3
+#define SW0_WR_BIT BIT(5)
+#define SW1_WR_BIT BIT(6)
+#define SW0_RD_BIT BIT(7)
+#define SW1_RD_BIT BIT(8)
+
+#define ISO_CTRL_MASK GENMASK(2, 0)
+
+struct ssd202d_rtc {
+ struct rtc_device *rtc_dev;
+ void __iomem *base;
+};
+
+static u8 read_iso_en(void __iomem *base)
+{
+ return readb(base + REG_RTC_TEST) & 0x1;
+}
+
+static u8 read_iso_ctrl_ack(void __iomem *base)
+{
+ return (readb(base + REG_ISOACK) & ISO_CTRL_ACK_MASK) >> ISO_CTRL_ACK_SHIFT;
+}
+
+static int ssd202d_rtc_isoctrl(struct ssd202d_rtc *priv)
+{
+ static const unsigned int sequence[] = { 0x0, 0x1, 0x3, 0x7, 0x5, 0x1, 0x0 };
+ unsigned int val;
+ struct device *dev = &priv->rtc_dev->dev;
+ int i, ret;
+
+ /*
+ * This gates iso_en by writing a special sequence of bytes to iso_ctrl
+ * and ensuring that it has been correctly applied by reading iso_ctrl_ack
+ */
+ for (i = 0; i < ARRAY_SIZE(sequence); i++) {
+ writeb(sequence[i] & ISO_CTRL_MASK, priv->base + REG_ISO_CTRL);
+
+ ret = read_poll_timeout(read_iso_ctrl_ack, val, val == (i % 2), 100,
+ 20 * 100, true, priv->base);
+ if (ret) {
+ dev_dbg(dev, "Timeout waiting for ack byte %i (%x) of sequence\n", i,
+ sequence[i]);
+ return ret;
+ }
+ }
+
+ /*
+ * At this point iso_en should be raised for 1ms
+ */
+ ret = read_poll_timeout(read_iso_en, val, val, 100, 22 * 100, true, priv->base);
+ if (ret)
+ dev_dbg(dev, "Timeout waiting for iso_en\n");
+ mdelay(2);
+ return 0;
+}
+
+static void ssd202d_rtc_read_reg(struct ssd202d_rtc *priv, unsigned int reg,
+ unsigned int field, unsigned int *base)
+{
+ unsigned int l, h;
+ u16 val;
+
+ /* Ask for the content of an RTC value into RDDATA by gating iso_en,
+ * then iso_en is gated and the content of RDDATA can be read
+ */
+ val = readw(priv->base + reg);
+ writew(val | field, priv->base + reg);
+ ssd202d_rtc_isoctrl(priv);
+ writew(val & ~field, priv->base + reg);
+
+ l = readw(priv->base + REG_RDDATA_L);
+ h = readw(priv->base + REG_RDDATA_H);
+
+ *base = (h << 16) | l;
+}
+
+static void ssd202d_rtc_write_reg(struct ssd202d_rtc *priv, unsigned int reg,
+ unsigned int field, u32 base)
+{
+ u16 val;
+
+ /* Set the content of an RTC value from WRDATA by gating iso_en */
+ val = readw(priv->base + reg);
+ writew(val | field, priv->base + reg);
+ writew(base, priv->base + REG_WRDATA_L);
+ writew(base >> 16, priv->base + REG_WRDATA_H);
+ ssd202d_rtc_isoctrl(priv);
+ writew(val & ~field, priv->base + reg);
+}
+
+static int ssd202d_rtc_read_counter(struct ssd202d_rtc *priv, unsigned int *counter)
+{
+ unsigned int l, h;
+ u16 val;
+
+ val = readw(priv->base + REG_CTRL1);
+ writew(val | CNT_RD_BIT, priv->base + REG_CTRL1);
+ ssd202d_rtc_isoctrl(priv);
+ writew(val & ~CNT_RD_BIT, priv->base + REG_CTRL1);
+
+ val = readw(priv->base + REG_CTRL1);
+ writew(val | CNT_RD_TRIG_BIT, priv->base + REG_CNT_TRIG);
+ writew(val & ~CNT_RD_TRIG_BIT, priv->base + REG_CNT_TRIG);
+
+ l = readw(priv->base + REG_RDCNT_L);
+ h = readw(priv->base + REG_RDCNT_H);
+
+ *counter = (h << 16) | l;
+
+ return 0;
+}
+
+static int ssd202d_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct ssd202d_rtc *priv = dev_get_drvdata(dev);
+ unsigned int sw0, base, counter;
+ u32 seconds;
+ int ret;
+
+ /* Check that RTC is enabled by SW */
+ ssd202d_rtc_read_reg(priv, REG_CTRL, SW0_RD_BIT, &sw0);
+ if (sw0 != 1)
+ return -EINVAL;
+
+ /* Get RTC base value from RDDATA */
+ ssd202d_rtc_read_reg(priv, REG_CTRL, BASE_RD_BIT, &base);
+ /* Get RTC counter value from RDDATA */
+ ret = ssd202d_rtc_read_counter(priv, &counter);
+ if (ret)
+ return ret;
+
+ seconds = base + counter;
+
+ rtc_time64_to_tm(seconds, tm);
+
+ return 0;
+}
+
+static int ssd202d_rtc_reset_counter(struct ssd202d_rtc *priv)
+{
+ u16 val;
+
+ val = readw(priv->base + REG_CTRL);
+ writew(val | CNT_RST_BIT, priv->base + REG_CTRL);
+ ssd202d_rtc_isoctrl(priv);
+ writew(val & ~CNT_RST_BIT, priv->base + REG_CTRL);
+ ssd202d_rtc_isoctrl(priv);
+
+ return 0;
+}
+
+static int ssd202d_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct ssd202d_rtc *priv = dev_get_drvdata(dev);
+ unsigned long seconds = rtc_tm_to_time64(tm);
+
+ ssd202d_rtc_write_reg(priv, REG_CTRL, BASE_WR_BIT, seconds);
+ ssd202d_rtc_reset_counter(priv);
+ ssd202d_rtc_write_reg(priv, REG_CTRL, SW0_WR_BIT, 1);
+
+ return 0;
+}
+
+static const struct rtc_class_ops ssd202d_rtc_ops = {
+ .read_time = ssd202d_rtc_read_time,
+ .set_time = ssd202d_rtc_set_time,
+};
+
+static int ssd202d_rtc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ssd202d_rtc *priv;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct ssd202d_rtc), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ priv->rtc_dev = devm_rtc_allocate_device(dev);
+ if (IS_ERR(priv->rtc_dev))
+ return PTR_ERR(priv->rtc_dev);
+
+ priv->rtc_dev->ops = &ssd202d_rtc_ops;
+ priv->rtc_dev->range_max = U32_MAX;
+
+ platform_set_drvdata(pdev, priv);
+
+ return devm_rtc_register_device(priv->rtc_dev);
+}
+
+static const struct of_device_id ssd202d_rtc_of_match_table[] = {
+ { .compatible = "mstar,ssd202d-rtc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ssd202d_rtc_of_match_table);
+
+static struct platform_driver ssd202d_rtc_driver = {
+ .probe = ssd202d_rtc_probe,
+ .driver = {
+ .name = "ssd202d-rtc",
+ .of_match_table = ssd202d_rtc_of_match_table,
+ },
+};
+module_platform_driver(ssd202d_rtc_driver);
+
+MODULE_AUTHOR("Daniel Palmer <daniel@thingy.jp>");
+MODULE_AUTHOR("Romain Perier <romain.perier@gmail.com>");
+MODULE_DESCRIPTION("MStar SSD202D RTC Driver");
+MODULE_LICENSE("GPL");