diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 17:39:57 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 17:39:57 +0000 |
commit | dc50eab76b709d68175a358d6e23a5a3890764d3 (patch) | |
tree | c754d0390db060af0213ff994f0ac310e4cfd6e9 /drivers/rtc | |
parent | Adding debian version 6.6.15-2. (diff) | |
download | linux-dc50eab76b709d68175a358d6e23a5a3890764d3.tar.xz linux-dc50eab76b709d68175a358d6e23a5a3890764d3.zip |
Merging upstream version 6.7.7.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 13 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/nvmem.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-at91rm9200.c | 14 | ||||
-rw-r--r-- | drivers/rtc/rtc-efi.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-ep93xx.c | 8 | ||||
-rw-r--r-- | drivers/rtc/rtc-imxdi.c | 14 | ||||
-rw-r--r-- | drivers/rtc/rtc-mv.c | 14 | ||||
-rw-r--r-- | drivers/rtc/rtc-omap.c | 8 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcap.c | 6 | ||||
-rw-r--r-- | drivers/rtc/rtc-pxa.c | 13 | ||||
-rw-r--r-- | drivers/rtc/rtc-r7301.c | 35 | ||||
-rw-r--r-- | drivers/rtc/rtc-sh.c | 16 | ||||
-rw-r--r-- | drivers/rtc/rtc-ssd202d.c | 249 |
14 files changed, 354 insertions, 40 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index d7502433c7..3814e0845e 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 fd209883ee..7b03c3abfd 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/nvmem.c b/drivers/rtc/nvmem.c index 07ede21cee..37df7e8052 100644 --- a/drivers/rtc/nvmem.c +++ b/drivers/rtc/nvmem.c @@ -21,6 +21,7 @@ int devm_rtc_nvmem_register(struct rtc_device *rtc, nvmem_config->dev = dev; nvmem_config->owner = rtc->owner; + nvmem_config->add_legacy_fixed_of_cells = true; nvmem = devm_nvmem_register(dev, nvmem_config); if (IS_ERR(nvmem)) dev_err(dev, "failed to register nvmem device for RTC\n"); diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index add4f71d7b..c16fe711a0 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-efi.c b/drivers/rtc/rtc-efi.c index dc6b0f4a54..fa8bf82df9 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 acae7f1680..1fdd20d015 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 4b712e5ab0..284011c419 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 6c526e2ec5..db31da56bf 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 5b10ab06cd..c6155c48a4 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 8c7a98a545..d6651611a0 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-pxa.c b/drivers/rtc/rtc-pxa.c index e400c78252..cdb39fc4ca 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 5dbaeb7af6..ef913cf859 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 cd146b5741..27a191fa37 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 0000000000..ed64932600 --- /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"); |