Commit d2f51b35 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 7b2c9e41 cfb67623
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/cirrus,ep9301-rtc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Cirrus EP93xx Real Time Clock controller
maintainers:
- Hartley Sweeten <hsweeten@visionengravers.com>
- Alexander Sverdlin <alexander.sverdlin@gmail.com>
allOf:
- $ref: rtc.yaml#
properties:
compatible:
oneOf:
- const: cirrus,ep9301-rtc
- items:
- enum:
- cirrus,ep9302-rtc
- cirrus,ep9307-rtc
- cirrus,ep9312-rtc
- cirrus,ep9315-rtc
- const: cirrus,ep9301-rtc
reg:
maxItems: 1
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
rtc@80920000 {
compatible = "cirrus,ep9301-rtc";
reg = <0x80920000 0x100>;
};
EPSON TOYOCOM RTC-7301SF/DG
Required properties:
- compatible: Should be "epson,rtc7301sf" or "epson,rtc7301dg"
- reg: Specifies base physical address and size of the registers.
- interrupts: A single interrupt specifier.
Example:
rtc: rtc@44a00000 {
compatible = "epson,rtc7301dg";
reg = <0x44a00000 0x10000>;
interrupt-parent = <&axi_intc_0>;
interrupts = <3 2>;
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/epson,rtc7301.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Epson Toyocom RTC-7301SF/DG
description:
The only difference between the two variants is the packaging.
The DG variant is a DIL package, and the SF variant is a flat
package.
maintainers:
- Akinobu Mita <akinobu.mita@gmail.com>
properties:
compatible:
enum:
- epson,rtc7301dg
- epson,rtc7301sf
reg:
maxItems: 1
reg-io-width:
description:
The size (in bytes) of the IO accesses that should be performed
on the device.
enum: [1, 4]
default: 4
interrupts:
maxItems: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
rtc: rtc@44a00000 {
compatible = "epson,rtc7301dg";
reg = <0x44a00000 0x10000>;
reg-io-width = <4>;
interrupt-parent = <&axi_intc_0>;
interrupts = <3 2>;
};
* Maxim MCP795 SPI Serial Real-Time Clock
Required properties:
- compatible: Should contain "maxim,mcp795".
- reg: SPI address for chip
Example:
mcp795: rtc@0 {
compatible = "maxim,mcp795";
reg = <0>;
};
......@@ -38,6 +38,8 @@ properties:
- 3000
- 4400
wakeup-source: true
required:
- compatible
- reg
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/mstar,ssd202d-rtc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Mstar SSD202D Real Time Clock
maintainers:
- Daniel Palmer <daniel@0x0f.com>
- Romain Perier <romain.perier@gmail.com>
allOf:
- $ref: rtc.yaml#
properties:
compatible:
enum:
- mstar,ssd202d-rtc
reg:
maxItems: 1
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
rtc@6800 {
compatible = "mstar,ssd202d-rtc";
reg = <0x6800 0x200>;
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/nxp,pcf2123.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP PCF2123 SPI Real Time Clock
maintainers:
- Javier Carrasco <javier.carrasco.cruz@gmail.com>
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
- $ref: rtc.yaml#
properties:
compatible:
enum:
- nxp,pcf2123
reg:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
rtc@3 {
compatible = "nxp,pcf2123";
reg = <3>;
interrupts = <GIC_SPI 130 IRQ_TYPE_LEVEL_LOW>;
spi-cs-high;
};
};
...
* NXP PCF8523 Real Time Clock
Required properties:
- compatible: Should contain "nxp,pcf8523".
- reg: I2C address for chip.
Optional property:
- quartz-load-femtofarads: The capacitive load of the quartz(x-tal),
expressed in femto Farad (fF). Valid values are 7000 and 12500.
Default value (if no value is specified) is 12500fF.
Example:
pcf8523: rtc@68 {
compatible = "nxp,pcf8523";
reg = <0x68>;
quartz-load-femtofarads = <7000>;
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/nxp,pcf8523.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP PCF8523 Real Time Clock
maintainers:
- Sam Ravnborg <sam@ravnborg.org>
allOf:
- $ref: rtc.yaml#
properties:
compatible:
const: nxp,pcf8523
reg:
maxItems: 1
quartz-load-femtofarads:
description:
The capacitive load of the crystal, expressed in femto Farad (fF).
enum: [ 7000, 12500 ]
default: 12500
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
rtc@68 {
compatible = "nxp,pcf8523";
reg = <0x68>;
quartz-load-femtofarads = <7000>;
};
};
NXP PCF2123 SPI Real Time Clock
Required properties:
- compatible: should be: "nxp,pcf2123"
or "microcrystal,rv2123"
- reg: should be the SPI slave chipselect address
Optional properties:
- spi-cs-high: PCF2123 needs chipselect high
Example:
pcf2123: rtc@3 {
compatible = "nxp,pcf2123"
reg = <3>
spi-cs-high;
};
......@@ -45,6 +45,8 @@ properties:
- isil,isl1208
# Intersil ISL1218 Low Power RTC with Battery Backed SRAM
- isil,isl1218
# SPI-BUS INTERFACE REAL TIME CLOCK MODULE
- maxim,mcp795
# Real Time Clock Module with I2C-Bus
- microcrystal,rv3029
# Real Time Clock
......
......@@ -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
......@@ -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
......
......@@ -558,7 +558,7 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
/*
* 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",
......
// 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" },
......
......@@ -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? */
......
......@@ -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,
};
......
......@@ -830,7 +830,7 @@ static int __init dryice_rtc_probe(struct platform_device *pdev)
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);
......
......@@ -282,7 +282,7 @@ static int __init mv_rtc_probe(struct platform_device *pdev)
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),
......
......@@ -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 {
......
......@@ -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",
},
......
......@@ -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) {
......
......@@ -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),
......
......@@ -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);
......
......@@ -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 @@ static int __init sh_rtc_probe(struct platform_device *pdev)
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);
......
// 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");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment