2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-16 01:04:08 +08:00

RTC for 5.17

New driver:
  - Sunplus SP7021 RTC
  - Nintendo GameCube, Wii and Wii U RTC
 
 Drivers:
  - cmos: refactor UIP handling and presence check, fix century
  - rs5c372: offset correction support, report low voltage
  - rv8803: Epson RX8804 support
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmHp+jkACgkQY6TcMGxw
 OjJS+hAArVbXHT0ceVHY5mzqkBXCyVxhXdyLUw/VmedO9NvCTk1+O9VhBwKTRdQX
 FLuvPkyjZlkAk3CEfaHBp4u5AW3Xumdkuh9XwG8b3qRs3UGYBgKiNfXQFtOWHs7w
 VO+lD7+f2GNNTYbYH2dLPs6qZwVIN0rL7JTwUH7Po2mqBVztppSOYhmo3vmOqqux
 FI5jFVDoE8u+h359bIKcaax3/dne9m1uyD1WWxOJFRtY+apbECpUBfo1tmauAEXP
 gg+v7On+gboDqVe9/lwqB+xfKzWFJKwYIu6CM8+Mf/dxhtRNRXCgszoiCSFZHUwG
 GghD7Kb96xWuGX1pRe6xx5/7wixes1wCkIVGa5JQLb5E/GQ3O9y8D+Tdk6lyAP5m
 XUVPWGC/qByj6z9pBHtbHmq9lhd1vPHp3SU0ske1xlCQd3WnPq7bQ3MEw345gf4Z
 RGrtpnUPIfKzHWID+2zCzTj6TluW9FnnOjcm2U8jF/kwB+d1/H2O5xeWR7eQYbUJ
 BY5gjDRVIaM3aQC9QiiFMUorqv8Q3oE9FtjCSuLhUx6WEg4S0mtYsXtRUaLT7pRw
 RRsoeCJd8Abnf1Nl/imZ2IrRCcbNhzLAq2Aw8cHbiroAk8lSFAH2NzvcS8213JfG
 muOrIB2Q3XBu+6hq452ZDE1155FGWFLwKnAjvSRy5yRbsN79/YA=
 =nwD6
 -----END PGP SIGNATURE-----

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

Pull RTC updates from Alexandre Belloni:
 "Two new drivers this cycle and a significant rework of the CMOS driver
  make the bulk of the changes.

  I also carry powerpc changes with the agreement of Michael.

  New drivers:
   - Sunplus SP7021 RTC
   - Nintendo GameCube, Wii and Wii U RTC

  Driver updates:
   - cmos: refactor UIP handling and presence check, fix century
   - rs5c372: offset correction support, report low voltage
   - rv8803: Epson RX8804 support"

* tag 'rtc-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (33 commits)
  rtc: sunplus: fix return value in sp_rtc_probe()
  rtc: cmos: Evaluate century appropriate
  rtc: gamecube: Fix an IS_ERR() vs NULL check
  rtc: mc146818-lib: fix signedness bug in mc146818_get_time()
  dt-bindings: rtc: qcom-pm8xxx-rtc: update register numbers
  rtc: pxa: fix null pointer dereference
  rtc: ftrtc010: Use platform_get_irq() to get the interrupt
  rtc: Move variable into switch case statement
  rtc: pcf2127: Fix typo in comment
  dt-bindings: rtc: Add Sunplus RTC json-schema
  rtc: Add driver for RTC in Sunplus SP7021
  rtc: rs5c372: fix incorrect oscillation value on r2221tl
  rtc: rs5c372: add offset correction support
  rtc: cmos: avoid UIP when writing alarm time
  rtc: cmos: avoid UIP when reading alarm time
  rtc: mc146818-lib: refactor mc146818_does_rtc_work
  rtc: mc146818-lib: refactor mc146818_get_time
  rtc: mc146818-lib: extract mc146818_avoid_UIP
  rtc: mc146818-lib: fix RTC presence check
  rtc: Check return value from mc146818_get_time()
  ...
This commit is contained in:
Linus Torvalds 2022-01-21 13:13:35 +02:00
commit 75242f31db
25 changed files with 1396 additions and 197 deletions

View File

@ -15,6 +15,7 @@ allOf:
properties:
compatible:
enum:
- epson,rx8804
- epson,rx8900
- microcrystal,rv8803

View File

@ -19,7 +19,14 @@ properties:
- qcom,pmk8350-rtc
reg:
maxItems: 1
minItems: 1
maxItems: 2
reg-names:
minItems: 1
items:
- const: rtc
- const: alarm
interrupts:
maxItems: 1

View File

@ -0,0 +1,56 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (C) Sunplus Co., Ltd. 2021
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/sunplus,sp7021-rtc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Sunplus SP7021 Real Time Clock controller
maintainers:
- Vincent Shih <vincent.sunplus@gmail.com>
properties:
compatible:
const: sunplus,sp7021-rtc
reg:
maxItems: 1
reg-names:
items:
- const: rtc
clocks:
maxItems: 1
resets:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg
- reg-names
- clocks
- resets
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
rtc: serial@9c003a00 {
compatible = "sunplus,sp7021-rtc";
reg = <0x9c003a00 0x80>;
reg-names = "rtc";
clocks = <&clkc 0x12>;
resets = <&rstc 0x02>;
interrupt-parent = <&intc>;
interrupts = <163 IRQ_TYPE_EDGE_RISING>;
};
...

View File

@ -18491,6 +18491,13 @@ L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/ethernet/dlink/sundance.c
SUNPLUS RTC DRIVER
M: Vincent Shih <vincent.sunplus@gmail.com>
L: linux-rtc@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/rtc/sunplus,sp7021-rtc.yaml
F: drivers/rtc/rtc-sunplus.c
SUPERH
M: Yoshinori Sato <ysato@users.sourceforge.jp>
M: Rich Felker <dalias@libc.org>

View File

@ -80,7 +80,12 @@ init_rtc_epoch(void)
static int
alpha_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
mc146818_get_time(tm);
int ret = mc146818_get_time(tm);
if (ret < 0) {
dev_err_ratelimited(dev, "unable to read current time\n");
return ret;
}
/* Adjust for non-default epochs. It's easier to depend on the
generic __get_rtc_time and adjust the epoch here than create

View File

@ -168,6 +168,11 @@
interrupts = <14>;
};
srnprot@d800060 {
compatible = "nintendo,hollywood-srnprot";
reg = <0x0d800060 0x4>;
};
GPIO: gpio@d8000c0 {
#gpio-cells = <2>;
compatible = "nintendo,hollywood-gpio";

View File

@ -68,7 +68,7 @@ CONFIG_SND_SEQUENCER=y
CONFIG_SND_SEQUENCER_OSS=y
# CONFIG_USB_SUPPORT is not set
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_GENERIC=y
CONFIG_RTC_DRV_GAMECUBE=y
CONFIG_EXT2_FS=y
CONFIG_EXT4_FS=y
CONFIG_ISO9660_FS=y

View File

@ -98,7 +98,7 @@ CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_LEDS_TRIGGER_PANIC=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_GENERIC=y
CONFIG_RTC_DRV_GAMECUBE=y
CONFIG_NVMEM_NINTENDO_OTP=y
CONFIG_EXT2_FS=y
CONFIG_EXT4_FS=y

View File

@ -1435,8 +1435,12 @@ irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id)
hpet_rtc_timer_reinit();
memset(&curr_time, 0, sizeof(struct rtc_time));
if (hpet_rtc_flags & (RTC_UIE | RTC_AIE))
mc146818_get_time(&curr_time);
if (hpet_rtc_flags & (RTC_UIE | RTC_AIE)) {
if (unlikely(mc146818_get_time(&curr_time) < 0)) {
pr_err_ratelimited("unable to read current time from RTC\n");
return IRQ_HANDLED;
}
}
if (hpet_rtc_flags & RTC_UIE &&
curr_time.tm_sec != hpet_prev_update_sec) {

View File

@ -120,7 +120,11 @@ static unsigned int read_magic_time(void)
struct rtc_time time;
unsigned int val;
mc146818_get_time(&time);
if (mc146818_get_time(&time) < 0) {
pr_err("Unable to read current time from RTC\n");
return 0;
}
pr_info("RTC time: %ptRt, date: %ptRd\n", &time, &time);
val = time.tm_year; /* 100 years */
if (val > 100)

View File

@ -1216,6 +1216,17 @@ config RTC_DRV_V3020
This driver can also be built as a module. If so, the module
will be called rtc-v3020.
config RTC_DRV_GAMECUBE
tristate "Nintendo GameCube, Wii and Wii U RTC"
depends on GAMECUBE || WII || COMPILE_TEST
select REGMAP
help
If you say yes here you will get support for the RTC subsystem
of the Nintendo GameCube, Wii and Wii U.
This driver can also be built as a module. If so, the module
will be called "rtc-gamecube".
config RTC_DRV_WM831X
tristate "Wolfson Microelectronics WM831x RTC"
depends on MFD_WM831X
@ -1444,6 +1455,19 @@ config RTC_DRV_SH
To compile this driver as a module, choose M here: the
module will be called rtc-sh.
config RTC_DRV_SUNPLUS
tristate "Sunplus SP7021 RTC"
depends on SOC_SP7021
help
Say 'yes' to get support for the real-time clock present in
Sunplus SP7021 - a SoC for industrial applications. It provides
RTC status check, timer/alarm functionalities, user data
reservation with the battery over 2.5V, RTC power status check
and battery charge.
This driver can also be built as a module. If so, the module
will be called rtc-sunplus.
config RTC_DRV_VR41XX
tristate "NEC VR41XX"
depends on CPU_VR41XX || COMPILE_TEST

View File

@ -111,6 +111,7 @@ obj-$(CONFIG_RTC_DRV_MT7622) += rtc-mt7622.o
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o
obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o
obj-$(CONFIG_RTC_DRV_GAMECUBE) += rtc-gamecube.o
obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
@ -165,6 +166,7 @@ obj-$(CONFIG_RTC_DRV_STM32) += rtc-stm32.o
obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
obj-$(CONFIG_RTC_DRV_SUN6I) += rtc-sun6i.o
obj-$(CONFIG_RTC_DRV_SUNPLUS) += rtc-sunplus.o
obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o
obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o

View File

@ -391,14 +391,14 @@ static long rtc_dev_ioctl(struct file *file,
}
switch(param.param) {
long offset;
case RTC_PARAM_FEATURES:
if (param.index != 0)
err = -EINVAL;
param.uvalue = rtc->features[0];
break;
case RTC_PARAM_CORRECTION:
case RTC_PARAM_CORRECTION: {
long offset;
mutex_unlock(&rtc->ops_lock);
if (param.index != 0)
return -EINVAL;
@ -407,7 +407,7 @@ static long rtc_dev_ioctl(struct file *file,
if (err == 0)
param.svalue = offset;
break;
}
default:
if (rtc->ops->param_get)
err = rtc->ops->param_get(rtc->dev.parent, &param);

View File

@ -222,6 +222,8 @@ static inline void cmos_write_bank2(unsigned char val, unsigned char addr)
static int cmos_read_time(struct device *dev, struct rtc_time *t)
{
int ret;
/*
* If pm_trace abused the RTC for storage, set the timespec to 0,
* which tells the caller that this RTC value is unusable.
@ -229,7 +231,12 @@ static int cmos_read_time(struct device *dev, struct rtc_time *t)
if (!pm_trace_rtc_valid())
return -EIO;
mc146818_get_time(t);
ret = mc146818_get_time(t);
if (ret < 0) {
dev_err_ratelimited(dev, "unable to read current time\n");
return ret;
}
return 0;
}
@ -242,10 +249,46 @@ static int cmos_set_time(struct device *dev, struct rtc_time *t)
return mc146818_set_time(t);
}
struct cmos_read_alarm_callback_param {
struct cmos_rtc *cmos;
struct rtc_time *time;
unsigned char rtc_control;
};
static void cmos_read_alarm_callback(unsigned char __always_unused seconds,
void *param_in)
{
struct cmos_read_alarm_callback_param *p =
(struct cmos_read_alarm_callback_param *)param_in;
struct rtc_time *time = p->time;
time->tm_sec = CMOS_READ(RTC_SECONDS_ALARM);
time->tm_min = CMOS_READ(RTC_MINUTES_ALARM);
time->tm_hour = CMOS_READ(RTC_HOURS_ALARM);
if (p->cmos->day_alrm) {
/* ignore upper bits on readback per ACPI spec */
time->tm_mday = CMOS_READ(p->cmos->day_alrm) & 0x3f;
if (!time->tm_mday)
time->tm_mday = -1;
if (p->cmos->mon_alrm) {
time->tm_mon = CMOS_READ(p->cmos->mon_alrm);
if (!time->tm_mon)
time->tm_mon = -1;
}
}
p->rtc_control = CMOS_READ(RTC_CONTROL);
}
static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned char rtc_control;
struct cmos_read_alarm_callback_param p = {
.cmos = cmos,
.time = &t->time,
};
/* This not only a rtc_op, but also called directly */
if (!is_valid_irq(cmos->irq))
@ -256,28 +299,18 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
* the future.
*/
spin_lock_irq(&rtc_lock);
t->time.tm_sec = CMOS_READ(RTC_SECONDS_ALARM);
t->time.tm_min = CMOS_READ(RTC_MINUTES_ALARM);
t->time.tm_hour = CMOS_READ(RTC_HOURS_ALARM);
/* Some Intel chipsets disconnect the alarm registers when the clock
* update is in progress - during this time reads return bogus values
* and writes may fail silently. See for example "7th Generation Intel®
* Processor Family I/O for U/Y Platforms [...] Datasheet", section
* 27.7.1
*
* Use the mc146818_avoid_UIP() function to avoid this.
*/
if (!mc146818_avoid_UIP(cmos_read_alarm_callback, &p))
return -EIO;
if (cmos->day_alrm) {
/* ignore upper bits on readback per ACPI spec */
t->time.tm_mday = CMOS_READ(cmos->day_alrm) & 0x3f;
if (!t->time.tm_mday)
t->time.tm_mday = -1;
if (cmos->mon_alrm) {
t->time.tm_mon = CMOS_READ(cmos->mon_alrm);
if (!t->time.tm_mon)
t->time.tm_mon = -1;
}
}
rtc_control = CMOS_READ(RTC_CONTROL);
spin_unlock_irq(&rtc_lock);
if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
if (!(p.rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
if (((unsigned)t->time.tm_sec) < 0x60)
t->time.tm_sec = bcd2bin(t->time.tm_sec);
else
@ -306,7 +339,7 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
}
}
t->enabled = !!(rtc_control & RTC_AIE);
t->enabled = !!(p.rtc_control & RTC_AIE);
t->pending = 0;
return 0;
@ -437,10 +470,57 @@ static int cmos_validate_alarm(struct device *dev, struct rtc_wkalrm *t)
return 0;
}
struct cmos_set_alarm_callback_param {
struct cmos_rtc *cmos;
unsigned char mon, mday, hrs, min, sec;
struct rtc_wkalrm *t;
};
/* Note: this function may be executed by mc146818_avoid_UIP() more then
* once
*/
static void cmos_set_alarm_callback(unsigned char __always_unused seconds,
void *param_in)
{
struct cmos_set_alarm_callback_param *p =
(struct cmos_set_alarm_callback_param *)param_in;
/* next rtc irq must not be from previous alarm setting */
cmos_irq_disable(p->cmos, RTC_AIE);
/* update alarm */
CMOS_WRITE(p->hrs, RTC_HOURS_ALARM);
CMOS_WRITE(p->min, RTC_MINUTES_ALARM);
CMOS_WRITE(p->sec, RTC_SECONDS_ALARM);
/* the system may support an "enhanced" alarm */
if (p->cmos->day_alrm) {
CMOS_WRITE(p->mday, p->cmos->day_alrm);
if (p->cmos->mon_alrm)
CMOS_WRITE(p->mon, p->cmos->mon_alrm);
}
if (use_hpet_alarm()) {
/*
* FIXME the HPET alarm glue currently ignores day_alrm
* and mon_alrm ...
*/
hpet_set_alarm_time(p->t->time.tm_hour, p->t->time.tm_min,
p->t->time.tm_sec);
}
if (p->t->enabled)
cmos_irq_enable(p->cmos, RTC_AIE);
}
static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned char mon, mday, hrs, min, sec, rtc_control;
struct cmos_set_alarm_callback_param p = {
.cmos = cmos,
.t = t
};
unsigned char rtc_control;
int ret;
/* This not only a rtc_op, but also called directly */
@ -451,53 +531,34 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
if (ret < 0)
return ret;
mon = t->time.tm_mon + 1;
mday = t->time.tm_mday;
hrs = t->time.tm_hour;
min = t->time.tm_min;
sec = t->time.tm_sec;
rtc_control = CMOS_READ(RTC_CONTROL);
if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
/* Writing 0xff means "don't care" or "match all". */
mon = (mon <= 12) ? bin2bcd(mon) : 0xff;
mday = (mday >= 1 && mday <= 31) ? bin2bcd(mday) : 0xff;
hrs = (hrs < 24) ? bin2bcd(hrs) : 0xff;
min = (min < 60) ? bin2bcd(min) : 0xff;
sec = (sec < 60) ? bin2bcd(sec) : 0xff;
}
p.mon = t->time.tm_mon + 1;
p.mday = t->time.tm_mday;
p.hrs = t->time.tm_hour;
p.min = t->time.tm_min;
p.sec = t->time.tm_sec;
spin_lock_irq(&rtc_lock);
/* next rtc irq must not be from previous alarm setting */
cmos_irq_disable(cmos, RTC_AIE);
/* update alarm */
CMOS_WRITE(hrs, RTC_HOURS_ALARM);
CMOS_WRITE(min, RTC_MINUTES_ALARM);
CMOS_WRITE(sec, RTC_SECONDS_ALARM);
/* the system may support an "enhanced" alarm */
if (cmos->day_alrm) {
CMOS_WRITE(mday, cmos->day_alrm);
if (cmos->mon_alrm)
CMOS_WRITE(mon, cmos->mon_alrm);
}
if (use_hpet_alarm()) {
/*
* FIXME the HPET alarm glue currently ignores day_alrm
* and mon_alrm ...
*/
hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min,
t->time.tm_sec);
}
if (t->enabled)
cmos_irq_enable(cmos, RTC_AIE);
rtc_control = CMOS_READ(RTC_CONTROL);
spin_unlock_irq(&rtc_lock);
if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
/* Writing 0xff means "don't care" or "match all". */
p.mon = (p.mon <= 12) ? bin2bcd(p.mon) : 0xff;
p.mday = (p.mday >= 1 && p.mday <= 31) ? bin2bcd(p.mday) : 0xff;
p.hrs = (p.hrs < 24) ? bin2bcd(p.hrs) : 0xff;
p.min = (p.min < 60) ? bin2bcd(p.min) : 0xff;
p.sec = (p.sec < 60) ? bin2bcd(p.sec) : 0xff;
}
/*
* Some Intel chipsets disconnect the alarm registers when the clock
* update is in progress - during this time writes fail silently.
*
* Use mc146818_avoid_UIP() to avoid this.
*/
if (!mc146818_avoid_UIP(cmos_set_alarm_callback, &p))
return -EIO;
cmos->alarm_expires = rtc_tm_to_time64(&t->time);
return 0;
@ -790,16 +851,14 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
rename_region(ports, dev_name(&cmos_rtc.rtc->dev));
spin_lock_irq(&rtc_lock);
/* Ensure that the RTC is accessible. Bit 6 must be 0! */
if ((CMOS_READ(RTC_VALID) & 0x40) != 0) {
spin_unlock_irq(&rtc_lock);
dev_warn(dev, "not accessible\n");
if (!mc146818_does_rtc_work()) {
dev_warn(dev, "broken or not accessible\n");
retval = -ENXIO;
goto cleanup1;
}
spin_lock_irq(&rtc_lock);
if (!(flags & CMOS_RTC_FLAGS_NOFREQ)) {
/* force periodic irq to CMOS reset default of 1024Hz;
*

View File

@ -475,12 +475,14 @@ static int da9063_rtc_probe(struct platform_device *pdev)
da9063_data_to_tm(data, &rtc->alarm_time, rtc);
rtc->rtc_sync = false;
/*
* TODO: some models have alarms on a minute boundary but still support
* real hardware interrupts. Add this once the core supports it.
*/
if (config->rtc_data_start != RTC_SEC)
rtc->rtc_dev->uie_unsupported = 1;
if (config->rtc_data_start != RTC_SEC) {
set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtc_dev->features);
/*
* TODO: some models have alarms on a minute boundary but still
* support real hardware interrupts.
*/
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtc_dev->features);
}
irq_alarm = platform_get_irq_byname(pdev, "ALARM");
if (irq_alarm < 0)
@ -494,6 +496,8 @@ static int da9063_rtc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Failed to request ALARM IRQ %d: %d\n",
irq_alarm, ret);
device_init_wakeup(&pdev->dev, true);
return devm_rtc_register_device(rtc->rtc_dev);
}

View File

@ -141,11 +141,9 @@ static int ftrtc010_rtc_probe(struct platform_device *pdev)
}
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res)
return -ENODEV;
rtc->rtc_irq = res->start;
rtc->rtc_irq = platform_get_irq(pdev, 0);
if (rtc->rtc_irq < 0)
return rtc->rtc_irq;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)

377
drivers/rtc/rtc-gamecube.c Normal file
View File

@ -0,0 +1,377 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Nintendo GameCube, Wii and Wii U RTC driver
*
* This driver is for the MX23L4005, more specifically its real-time clock and
* SRAM storage. The value returned by the RTC counter must be added with the
* offset stored in a bias register in SRAM (on the GameCube and Wii) or in
* /config/rtc.xml (on the Wii U). The latter being very impractical to access
* from Linux, this driver assumes the bootloader has read it and stored it in
* SRAM like for the other two consoles.
*
* This device sits on a bus named EXI (which is similar to SPI), channel 0,
* device 1. This driver assumes no other user of the EXI bus, which is
* currently the case but would have to be reworked to add support for other
* GameCube hardware exposed on this bus.
*
* References:
* - https://wiiubrew.org/wiki/Hardware/RTC
* - https://wiibrew.org/wiki/MX23L4005
*
* Copyright (C) 2018 rw-r-r-0644
* Copyright (C) 2021 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
*
* Based on rtc-gcn.c
* Copyright (C) 2004-2009 The GameCube Linux Team
* Copyright (C) 2005,2008,2009 Albert Herranz
* Based on gamecube_time.c from Torben Nielsen.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/rtc.h>
#include <linux/time.h>
/* EXI registers */
#define EXICSR 0
#define EXICR 12
#define EXIDATA 16
/* EXI register values */
#define EXICSR_DEV 0x380
#define EXICSR_DEV1 0x100
#define EXICSR_CLK 0x070
#define EXICSR_CLK_1MHZ 0x000
#define EXICSR_CLK_2MHZ 0x010
#define EXICSR_CLK_4MHZ 0x020
#define EXICSR_CLK_8MHZ 0x030
#define EXICSR_CLK_16MHZ 0x040
#define EXICSR_CLK_32MHZ 0x050
#define EXICSR_INT 0x008
#define EXICSR_INTSET 0x008
#define EXICR_TSTART 0x001
#define EXICR_TRSMODE 0x002
#define EXICR_TRSMODE_IMM 0x000
#define EXICR_TRSTYPE 0x00C
#define EXICR_TRSTYPE_R 0x000
#define EXICR_TRSTYPE_W 0x004
#define EXICR_TLEN 0x030
#define EXICR_TLEN32 0x030
/* EXI registers values to access the RTC */
#define RTC_EXICSR (EXICSR_DEV1 | EXICSR_CLK_8MHZ | EXICSR_INTSET)
#define RTC_EXICR_W (EXICR_TSTART | EXICR_TRSMODE_IMM | EXICR_TRSTYPE_W | EXICR_TLEN32)
#define RTC_EXICR_R (EXICR_TSTART | EXICR_TRSMODE_IMM | EXICR_TRSTYPE_R | EXICR_TLEN32)
#define RTC_EXIDATA_W 0x80000000
/* RTC registers */
#define RTC_COUNTER 0x200000
#define RTC_SRAM 0x200001
#define RTC_SRAM_BIAS 0x200004
#define RTC_SNAPSHOT 0x204000
#define RTC_ONTMR 0x210000
#define RTC_OFFTMR 0x210001
#define RTC_TEST0 0x210004
#define RTC_TEST1 0x210005
#define RTC_TEST2 0x210006
#define RTC_TEST3 0x210007
#define RTC_CONTROL0 0x21000c
#define RTC_CONTROL1 0x21000d
/* RTC flags */
#define RTC_CONTROL0_UNSTABLE_POWER 0x00000800
#define RTC_CONTROL0_LOW_BATTERY 0x00000200
struct priv {
struct regmap *regmap;
void __iomem *iob;
u32 rtc_bias;
};
static int exi_read(void *context, u32 reg, u32 *data)
{
struct priv *d = (struct priv *)context;
void __iomem *iob = d->iob;
/* The spin loops here loop about 15~16 times each, so there is no need
* to use a more expensive sleep method.
*/
/* Write register offset */
iowrite32be(RTC_EXICSR, iob + EXICSR);
iowrite32be(reg << 8, iob + EXIDATA);
iowrite32be(RTC_EXICR_W, iob + EXICR);
while (!(ioread32be(iob + EXICSR) & EXICSR_INTSET))
cpu_relax();
/* Read data */
iowrite32be(RTC_EXICSR, iob + EXICSR);
iowrite32be(RTC_EXICR_R, iob + EXICR);
while (!(ioread32be(iob + EXICSR) & EXICSR_INTSET))
cpu_relax();
*data = ioread32be(iob + EXIDATA);
/* Clear channel parameters */
iowrite32be(0, iob + EXICSR);
return 0;
}
static int exi_write(void *context, u32 reg, u32 data)
{
struct priv *d = (struct priv *)context;
void __iomem *iob = d->iob;
/* The spin loops here loop about 15~16 times each, so there is no need
* to use a more expensive sleep method.
*/
/* Write register offset */
iowrite32be(RTC_EXICSR, iob + EXICSR);
iowrite32be(RTC_EXIDATA_W | (reg << 8), iob + EXIDATA);
iowrite32be(RTC_EXICR_W, iob + EXICR);
while (!(ioread32be(iob + EXICSR) & EXICSR_INTSET))
cpu_relax();
/* Write data */
iowrite32be(RTC_EXICSR, iob + EXICSR);
iowrite32be(data, iob + EXIDATA);
iowrite32be(RTC_EXICR_W, iob + EXICR);
while (!(ioread32be(iob + EXICSR) & EXICSR_INTSET))
cpu_relax();
/* Clear channel parameters */
iowrite32be(0, iob + EXICSR);
return 0;
}
static const struct regmap_bus exi_bus = {
/* TODO: is that true? Not that it matters here, but still. */
.fast_io = true,
.reg_read = exi_read,
.reg_write = exi_write,
};
static int gamecube_rtc_read_time(struct device *dev, struct rtc_time *t)
{
struct priv *d = dev_get_drvdata(dev);
int ret;
u32 counter;
time64_t timestamp;
ret = regmap_read(d->regmap, RTC_COUNTER, &counter);
if (ret)
return ret;
/* Add the counter and the bias to obtain the timestamp */
timestamp = (time64_t)d->rtc_bias + counter;
rtc_time64_to_tm(timestamp, t);
return 0;
}
static int gamecube_rtc_set_time(struct device *dev, struct rtc_time *t)
{
struct priv *d = dev_get_drvdata(dev);
time64_t timestamp;
/* Subtract the timestamp and the bias to obtain the counter value */
timestamp = rtc_tm_to_time64(t);
return regmap_write(d->regmap, RTC_COUNTER, timestamp - d->rtc_bias);
}
static int gamecube_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
struct priv *d = dev_get_drvdata(dev);
int value;
int control0;
int ret;
switch (cmd) {
case RTC_VL_READ:
ret = regmap_read(d->regmap, RTC_CONTROL0, &control0);
if (ret)
return ret;
value = 0;
if (control0 & RTC_CONTROL0_UNSTABLE_POWER)
value |= RTC_VL_DATA_INVALID;
if (control0 & RTC_CONTROL0_LOW_BATTERY)
value |= RTC_VL_BACKUP_LOW;
return put_user(value, (unsigned int __user *)arg);
default:
return -ENOIOCTLCMD;
}
}
static const struct rtc_class_ops gamecube_rtc_ops = {
.read_time = gamecube_rtc_read_time,
.set_time = gamecube_rtc_set_time,
.ioctl = gamecube_rtc_ioctl,
};
static int gamecube_rtc_read_offset_from_sram(struct priv *d)
{
struct device_node *np;
int ret;
struct resource res;
void __iomem *hw_srnprot;
u32 old;
np = of_find_compatible_node(NULL, NULL, "nintendo,latte-srnprot");
if (!np)
np = of_find_compatible_node(NULL, NULL,
"nintendo,hollywood-srnprot");
if (!np) {
pr_info("HW_SRNPROT not found, assuming a GameCube\n");
return regmap_read(d->regmap, RTC_SRAM_BIAS, &d->rtc_bias);
}
ret = of_address_to_resource(np, 0, &res);
if (ret) {
pr_err("no io memory range found\n");
return -1;
}
hw_srnprot = ioremap(res.start, resource_size(&res));
old = ioread32be(hw_srnprot);
/* TODO: figure out why we use this magic constant. I obtained it by
* reading the leftover value after boot, after IOSU already ran.
*
* On my Wii U, setting this register to 1 prevents the console from
* rebooting properly, so wiiubrew.org must be missing something.
*
* See https://wiiubrew.org/wiki/Hardware/Latte_registers
*/
if (old != 0x7bf)
iowrite32be(0x7bf, hw_srnprot);
/* Get the offset from RTC SRAM.
*
* Its default location on the GameCube and on the Wii is in the SRAM,
* while on the Wii U the bootloader needs to fill it with the contents
* of /config/rtc.xml on the SLC (the eMMC). We dont do that from
* Linux since it requires implementing a proprietary filesystem and do
* file decryption, instead we require the bootloader to fill the same
* SRAM address as on previous consoles.
*/
ret = regmap_read(d->regmap, RTC_SRAM_BIAS, &d->rtc_bias);
if (ret) {
pr_err("failed to get the RTC bias\n");
return -1;
}
/* Reset SRAM access to how it was before, our job here is done. */
if (old != 0x7bf)
iowrite32be(old, hw_srnprot);
iounmap(hw_srnprot);
return 0;
}
static const struct regmap_range rtc_rd_ranges[] = {
regmap_reg_range(0x200000, 0x200010),
regmap_reg_range(0x204000, 0x204000),
regmap_reg_range(0x210000, 0x210001),
regmap_reg_range(0x210004, 0x210007),
regmap_reg_range(0x21000c, 0x21000d),
};
static const struct regmap_access_table rtc_rd_regs = {
.yes_ranges = rtc_rd_ranges,
.n_yes_ranges = ARRAY_SIZE(rtc_rd_ranges),
};
static const struct regmap_range rtc_wr_ranges[] = {
regmap_reg_range(0x200000, 0x200010),
regmap_reg_range(0x204000, 0x204000),
regmap_reg_range(0x210000, 0x210001),
regmap_reg_range(0x21000d, 0x21000d),
};
static const struct regmap_access_table rtc_wr_regs = {
.yes_ranges = rtc_wr_ranges,
.n_yes_ranges = ARRAY_SIZE(rtc_wr_ranges),
};
static const struct regmap_config gamecube_rtc_regmap_config = {
.reg_bits = 24,
.val_bits = 32,
.rd_table = &rtc_rd_regs,
.wr_table = &rtc_wr_regs,
.max_register = 0x21000d,
.name = "gamecube-rtc",
};
static int gamecube_rtc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rtc_device *rtc;
struct priv *d;
int ret;
d = devm_kzalloc(dev, sizeof(struct priv), GFP_KERNEL);
if (!d)
return -ENOMEM;
d->iob = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(d->iob))
return PTR_ERR(d->iob);
d->regmap = devm_regmap_init(dev, &exi_bus, d,
&gamecube_rtc_regmap_config);
if (IS_ERR(d->regmap))
return PTR_ERR(d->regmap);
ret = gamecube_rtc_read_offset_from_sram(d);
if (ret)
return ret;
dev_dbg(dev, "SRAM bias: 0x%x", d->rtc_bias);
dev_set_drvdata(dev, d);
rtc = devm_rtc_allocate_device(dev);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
/* We can represent further than that, but it depends on the stored
* bias and we cant modify it persistently on all supported consoles,
* so here we pretend to be limited to 2106.
*/
rtc->range_min = 0;
rtc->range_max = U32_MAX;
rtc->ops = &gamecube_rtc_ops;
devm_rtc_register_device(rtc);
return 0;
}
static const struct of_device_id gamecube_rtc_of_match[] = {
{.compatible = "nintendo,latte-exi" },
{.compatible = "nintendo,hollywood-exi" },
{.compatible = "nintendo,flipper-exi" },
{ }
};
MODULE_DEVICE_TABLE(of, gamecube_rtc_of_match);
static struct platform_driver gamecube_rtc_driver = {
.probe = gamecube_rtc_probe,
.driver = {
.name = "rtc-gamecube",
.of_match_table = gamecube_rtc_of_match,
},
};
module_platform_driver(gamecube_rtc_driver);
MODULE_AUTHOR("Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>");
MODULE_DESCRIPTION("Nintendo GameCube, Wii and Wii U RTC driver");
MODULE_LICENSE("GPL");

View File

@ -8,48 +8,100 @@
#include <linux/acpi.h>
#endif
unsigned int mc146818_get_time(struct rtc_time *time)
/*
* Execute a function while the UIP (Update-in-progress) bit of the RTC is
* unset.
*
* Warning: callback may be executed more then once.
*/
bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param),
void *param)
{
unsigned char ctrl;
int i;
unsigned long flags;
unsigned char century = 0;
bool retry;
unsigned char seconds;
for (i = 0; i < 10; i++) {
spin_lock_irqsave(&rtc_lock, flags);
/*
* Check whether there is an update in progress during which the
* readout is unspecified. The maximum update time is ~2ms. Poll
* every msec for completion.
*
* Store the second value before checking UIP so a long lasting
* NMI which happens to hit after the UIP check cannot make
* an update cycle invisible.
*/
seconds = CMOS_READ(RTC_SECONDS);
if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) {
spin_unlock_irqrestore(&rtc_lock, flags);
mdelay(1);
continue;
}
/* Revalidate the above readout */
if (seconds != CMOS_READ(RTC_SECONDS)) {
spin_unlock_irqrestore(&rtc_lock, flags);
continue;
}
if (callback)
callback(seconds, param);
/*
* Check for the UIP bit again. If it is set now then
* the above values may contain garbage.
*/
if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) {
spin_unlock_irqrestore(&rtc_lock, flags);
mdelay(1);
continue;
}
/*
* A NMI might have interrupted the above sequence so check
* whether the seconds value has changed which indicates that
* the NMI took longer than the UIP bit was set. Unlikely, but
* possible and there is also virt...
*/
if (seconds != CMOS_READ(RTC_SECONDS)) {
spin_unlock_irqrestore(&rtc_lock, flags);
continue;
}
spin_unlock_irqrestore(&rtc_lock, flags);
return true;
}
return false;
}
EXPORT_SYMBOL_GPL(mc146818_avoid_UIP);
/*
* If the UIP (Update-in-progress) bit of the RTC is set for more then
* 10ms, the RTC is apparently broken or not present.
*/
bool mc146818_does_rtc_work(void)
{
return mc146818_avoid_UIP(NULL, NULL);
}
EXPORT_SYMBOL_GPL(mc146818_does_rtc_work);
struct mc146818_get_time_callback_param {
struct rtc_time *time;
unsigned char ctrl;
#ifdef CONFIG_ACPI
unsigned char century;
#endif
#ifdef CONFIG_MACH_DECSTATION
unsigned int real_year;
#endif
};
again:
spin_lock_irqsave(&rtc_lock, flags);
/* Ensure that the RTC is accessible. Bit 6 must be 0! */
if (WARN_ON_ONCE((CMOS_READ(RTC_VALID) & 0x40) != 0)) {
spin_unlock_irqrestore(&rtc_lock, flags);
memset(time, 0xff, sizeof(*time));
return 0;
}
/*
* Check whether there is an update in progress during which the
* readout is unspecified. The maximum update time is ~2ms. Poll
* every msec for completion.
*
* Store the second value before checking UIP so a long lasting NMI
* which happens to hit after the UIP check cannot make an update
* cycle invisible.
*/
time->tm_sec = CMOS_READ(RTC_SECONDS);
if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) {
spin_unlock_irqrestore(&rtc_lock, flags);
mdelay(1);
goto again;
}
/* Revalidate the above readout */
if (time->tm_sec != CMOS_READ(RTC_SECONDS)) {
spin_unlock_irqrestore(&rtc_lock, flags);
goto again;
}
static void mc146818_get_time_callback(unsigned char seconds, void *param_in)
{
struct mc146818_get_time_callback_param *p = param_in;
/*
* Only the values that we read from the RTC are set. We leave
@ -57,39 +109,39 @@ again:
* RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
* by the RTC when initially set to a non-zero value.
*/
time->tm_min = CMOS_READ(RTC_MINUTES);
time->tm_hour = CMOS_READ(RTC_HOURS);
time->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
time->tm_mon = CMOS_READ(RTC_MONTH);
time->tm_year = CMOS_READ(RTC_YEAR);
p->time->tm_sec = seconds;
p->time->tm_min = CMOS_READ(RTC_MINUTES);
p->time->tm_hour = CMOS_READ(RTC_HOURS);
p->time->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
p->time->tm_mon = CMOS_READ(RTC_MONTH);
p->time->tm_year = CMOS_READ(RTC_YEAR);
#ifdef CONFIG_MACH_DECSTATION
real_year = CMOS_READ(RTC_DEC_YEAR);
p->real_year = CMOS_READ(RTC_DEC_YEAR);
#endif
#ifdef CONFIG_ACPI
if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
acpi_gbl_FADT.century)
century = CMOS_READ(acpi_gbl_FADT.century);
acpi_gbl_FADT.century) {
p->century = CMOS_READ(acpi_gbl_FADT.century);
} else {
p->century = 0;
}
#endif
ctrl = CMOS_READ(RTC_CONTROL);
/*
* Check for the UIP bit again. If it is set now then
* the above values may contain garbage.
*/
retry = CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP;
/*
* A NMI might have interrupted the above sequence so check whether
* the seconds value has changed which indicates that the NMI took
* longer than the UIP bit was set. Unlikely, but possible and
* there is also virt...
*/
retry |= time->tm_sec != CMOS_READ(RTC_SECONDS);
spin_unlock_irqrestore(&rtc_lock, flags);
p->ctrl = CMOS_READ(RTC_CONTROL);
}
if (retry)
goto again;
int mc146818_get_time(struct rtc_time *time)
{
struct mc146818_get_time_callback_param p = {
.time = time
};
if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
if (!mc146818_avoid_UIP(mc146818_get_time_callback, &p)) {
memset(time, 0, sizeof(*time));
return -EIO;
}
if (!(p.ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
{
time->tm_sec = bcd2bin(time->tm_sec);
time->tm_min = bcd2bin(time->tm_min);
@ -97,15 +149,19 @@ again:
time->tm_mday = bcd2bin(time->tm_mday);
time->tm_mon = bcd2bin(time->tm_mon);
time->tm_year = bcd2bin(time->tm_year);
century = bcd2bin(century);
#ifdef CONFIG_ACPI
p.century = bcd2bin(p.century);
#endif
}
#ifdef CONFIG_MACH_DECSTATION
time->tm_year += real_year - 72;
time->tm_year += p.real_year - 72;
#endif
if (century > 20)
time->tm_year += (century - 19) * 100;
#ifdef CONFIG_ACPI
if (p.century > 19)
time->tm_year += (p.century - 19) * 100;
#endif
/*
* Account for differences between how the RTC uses the values
@ -116,7 +172,7 @@ again:
time->tm_mon--;
return RTC_24H;
return 0;
}
EXPORT_SYMBOL_GPL(mc146818_get_time);

View File

@ -748,7 +748,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
/*
* Enable timestamp function and store timestamp of first trigger
* event until TSF1 and TFS2 interrupt flags are cleared.
* event until TSF1 and TSF2 interrupt flags are cleared.
*/
ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_TS_CTRL,
PCF2127_BIT_TS_CTRL_TSOFF |

View File

@ -514,21 +514,56 @@ static struct clk *pcf85063_clkout_register_clk(struct pcf85063 *pcf85063)
}
#endif
static const struct pcf85063_config pcf85063tp_config = {
.regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x0a,
enum pcf85063_type {
PCF85063,
PCF85063TP,
PCF85063A,
RV8263,
PCF85063_LAST_ID
};
static struct pcf85063_config pcf85063_cfg[] = {
[PCF85063] = {
.regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x0a,
},
},
[PCF85063TP] = {
.regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x0a,
},
},
[PCF85063A] = {
.regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x11,
},
.has_alarms = 1,
},
[RV8263] = {
.regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x11,
},
.has_alarms = 1,
.force_cap_7000 = 1,
},
};
static const struct i2c_device_id pcf85063_ids[];
static int pcf85063_probe(struct i2c_client *client)
{
struct pcf85063 *pcf85063;
unsigned int tmp;
int err;
const struct pcf85063_config *config = &pcf85063tp_config;
const void *data = of_device_get_match_data(&client->dev);
const struct pcf85063_config *config;
struct nvmem_config nvmem_cfg = {
.name = "pcf85063_nvram",
.reg_read = pcf85063_nvmem_read,
@ -544,8 +579,17 @@ static int pcf85063_probe(struct i2c_client *client)
if (!pcf85063)
return -ENOMEM;
if (data)
config = data;
if (client->dev.of_node) {
config = of_device_get_match_data(&client->dev);
if (!config)
return -ENODEV;
} else {
enum pcf85063_type type =
i2c_match_id(pcf85063_ids, client)->driver_data;
if (type >= PCF85063_LAST_ID)
return -ENODEV;
config = &pcf85063_cfg[type];
}
pcf85063->regmap = devm_regmap_init_i2c(client, &config->regmap);
if (IS_ERR(pcf85063->regmap))
@ -604,31 +648,21 @@ static int pcf85063_probe(struct i2c_client *client)
return devm_rtc_register_device(pcf85063->rtc);
}
static const struct i2c_device_id pcf85063_ids[] = {
{ "pcf85063", PCF85063 },
{ "pcf85063tp", PCF85063TP },
{ "pcf85063a", PCF85063A },
{ "rv8263", RV8263 },
{}
};
MODULE_DEVICE_TABLE(i2c, pcf85063_ids);
#ifdef CONFIG_OF
static const struct pcf85063_config pcf85063a_config = {
.regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x11,
},
.has_alarms = 1,
};
static const struct pcf85063_config rv8263_config = {
.regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x11,
},
.has_alarms = 1,
.force_cap_7000 = 1,
};
static const struct of_device_id pcf85063_of_match[] = {
{ .compatible = "nxp,pcf85063", .data = &pcf85063tp_config },
{ .compatible = "nxp,pcf85063tp", .data = &pcf85063tp_config },
{ .compatible = "nxp,pcf85063a", .data = &pcf85063a_config },
{ .compatible = "microcrystal,rv8263", .data = &rv8263_config },
{ .compatible = "nxp,pcf85063", .data = &pcf85063_cfg[PCF85063] },
{ .compatible = "nxp,pcf85063tp", .data = &pcf85063_cfg[PCF85063TP] },
{ .compatible = "nxp,pcf85063a", .data = &pcf85063_cfg[PCF85063A] },
{ .compatible = "microcrystal,rv8263", .data = &pcf85063_cfg[RV8263] },
{}
};
MODULE_DEVICE_TABLE(of, pcf85063_of_match);
@ -640,6 +674,7 @@ static struct i2c_driver pcf85063_driver = {
.of_match_table = of_match_ptr(pcf85063_of_match),
},
.probe_new = pcf85063_probe,
.id_table = pcf85063_ids,
};
module_i2c_driver(pcf85063_driver);

View File

@ -330,6 +330,10 @@ static int __init pxa_rtc_probe(struct platform_device *pdev)
if (sa1100_rtc->irq_alarm < 0)
return -ENXIO;
sa1100_rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(sa1100_rtc->rtc))
return PTR_ERR(sa1100_rtc->rtc);
pxa_rtc->base = devm_ioremap(dev, pxa_rtc->ress->start,
resource_size(pxa_rtc->ress));
if (!pxa_rtc->base) {

View File

@ -28,8 +28,10 @@
#define RS5C372_REG_MONTH 5
#define RS5C372_REG_YEAR 6
#define RS5C372_REG_TRIM 7
# define RS5C372_TRIM_XSL 0x80
# define RS5C372_TRIM_XSL 0x80 /* only if RS5C372[a|b] */
# define RS5C372_TRIM_MASK 0x7F
# define R2221TL_TRIM_DEV (1 << 7) /* only if R2221TL */
# define RS5C372_TRIM_DECR (1 << 6)
#define RS5C_REG_ALARM_A_MIN 8 /* or ALARM_W */
#define RS5C_REG_ALARM_A_HOURS 9
@ -324,8 +326,12 @@ static int rs5c372_get_trim(struct i2c_client *client, int *osc, int *trim)
struct rs5c372 *rs5c372 = i2c_get_clientdata(client);
u8 tmp = rs5c372->regs[RS5C372_REG_TRIM];
if (osc)
*osc = (tmp & RS5C372_TRIM_XSL) ? 32000 : 32768;
if (osc) {
if (rs5c372->type == rtc_rs5c372a || rs5c372->type == rtc_rs5c372b)
*osc = (tmp & RS5C372_TRIM_XSL) ? 32000 : 32768;
else
*osc = 32768;
}
if (trim) {
dev_dbg(&client->dev, "%s: raw trim=%x\n", __func__, tmp);
@ -485,6 +491,176 @@ static int rs5c372_rtc_proc(struct device *dev, struct seq_file *seq)
#define rs5c372_rtc_proc NULL
#endif
#ifdef CONFIG_RTC_INTF_DEV
static int rs5c372_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
struct rs5c372 *rs5c = i2c_get_clientdata(to_i2c_client(dev));
unsigned char ctrl2;
int addr;
unsigned int flags;
dev_dbg(dev, "%s: cmd=%x\n", __func__, cmd);
addr = RS5C_ADDR(RS5C_REG_CTRL2);
ctrl2 = i2c_smbus_read_byte_data(rs5c->client, addr);
switch (cmd) {
case RTC_VL_READ:
flags = 0;
switch (rs5c->type) {
case rtc_r2025sd:
case rtc_r2221tl:
if ((rs5c->type == rtc_r2025sd && !(ctrl2 & R2x2x_CTRL2_XSTP)) ||
(rs5c->type == rtc_r2221tl && (ctrl2 & R2x2x_CTRL2_XSTP))) {
flags |= RTC_VL_DATA_INVALID;
}
if (ctrl2 & R2x2x_CTRL2_VDET)
flags |= RTC_VL_BACKUP_LOW;
break;
default:
if (ctrl2 & RS5C_CTRL2_XSTP)
flags |= RTC_VL_DATA_INVALID;
break;
}
return put_user(flags, (unsigned int __user *)arg);
case RTC_VL_CLR:
/* clear VDET bit */
if (rs5c->type == rtc_r2025sd || rs5c->type == rtc_r2221tl) {
ctrl2 &= ~R2x2x_CTRL2_VDET;
if (i2c_smbus_write_byte_data(rs5c->client, addr, ctrl2) < 0) {
dev_dbg(&rs5c->client->dev, "%s: write error in line %i\n",
__func__, __LINE__);
return -EIO;
}
}
return 0;
default:
return -ENOIOCTLCMD;
}
return 0;
}
#else
#define rs5c372_ioctl NULL
#endif
static int rs5c372_read_offset(struct device *dev, long *offset)
{
struct rs5c372 *rs5c = i2c_get_clientdata(to_i2c_client(dev));
u8 val = rs5c->regs[RS5C372_REG_TRIM];
long ppb_per_step = 0;
bool decr = val & RS5C372_TRIM_DECR;
switch (rs5c->type) {
case rtc_r2221tl:
ppb_per_step = val & R2221TL_TRIM_DEV ? 1017 : 3051;
break;
case rtc_rs5c372a:
case rtc_rs5c372b:
ppb_per_step = val & RS5C372_TRIM_XSL ? 3125 : 3051;
break;
default:
ppb_per_step = 3051;
break;
}
/* Only bits[0:5] repsents the time counts */
val &= 0x3F;
/* If bits[1:5] are all 0, it means no increment or decrement */
if (!(val & 0x3E)) {
*offset = 0;
} else {
if (decr)
*offset = -(((~val) & 0x3F) + 1) * ppb_per_step;
else
*offset = (val - 1) * ppb_per_step;
}
return 0;
}
static int rs5c372_set_offset(struct device *dev, long offset)
{
struct rs5c372 *rs5c = i2c_get_clientdata(to_i2c_client(dev));
int addr = RS5C_ADDR(RS5C372_REG_TRIM);
u8 val = 0;
u8 tmp = 0;
long ppb_per_step = 3051;
long steps = LONG_MIN;
switch (rs5c->type) {
case rtc_rs5c372a:
case rtc_rs5c372b:
tmp = rs5c->regs[RS5C372_REG_TRIM];
if (tmp & RS5C372_TRIM_XSL) {
ppb_per_step = 3125;
val |= RS5C372_TRIM_XSL;
}
break;
case rtc_r2221tl:
/*
* Check if it is possible to use high resolution mode (DEV=1).
* In this mode, the minimum resolution is 2 / (32768 * 20 * 3),
* which is about 1017 ppb.
*/
steps = DIV_ROUND_CLOSEST(offset, 1017);
if (steps >= -0x3E && steps <= 0x3E) {
ppb_per_step = 1017;
val |= R2221TL_TRIM_DEV;
} else {
/*
* offset is out of the range of high resolution mode.
* Try to use low resolution mode (DEV=0). In this mode,
* the minimum resolution is 2 / (32768 * 20), which is
* about 3051 ppb.
*/
steps = LONG_MIN;
}
break;
default:
break;
}
if (steps == LONG_MIN) {
steps = DIV_ROUND_CLOSEST(offset, ppb_per_step);
if (steps > 0x3E || steps < -0x3E)
return -ERANGE;
}
if (steps > 0) {
val |= steps + 1;
} else {
val |= RS5C372_TRIM_DECR;
val |= (~(-steps - 1)) & 0x3F;
}
if (!steps || !(val & 0x3E)) {
/*
* if offset is too small, set oscillation adjustment register
* or time trimming register with its default value whic means
* no increment or decrement. But for rs5c372[a|b], the XSL bit
* should be kept unchanged.
*/
if (rs5c->type == rtc_rs5c372a || rs5c->type == rtc_rs5c372b)
val &= RS5C372_TRIM_XSL;
else
val = 0;
}
dev_dbg(&rs5c->client->dev, "write 0x%x for offset %ld\n", val, offset);
if (i2c_smbus_write_byte_data(rs5c->client, addr, val) < 0) {
dev_err(&rs5c->client->dev, "failed to write 0x%x to reg %d\n", val, addr);
return -EIO;
}
rs5c->regs[RS5C372_REG_TRIM] = val;
return 0;
}
static const struct rtc_class_ops rs5c372_rtc_ops = {
.proc = rs5c372_rtc_proc,
.read_time = rs5c372_rtc_read_time,
@ -492,6 +668,9 @@ static const struct rtc_class_ops rs5c372_rtc_ops = {
.read_alarm = rs5c_read_alarm,
.set_alarm = rs5c_set_alarm,
.alarm_irq_enable = rs5c_rtc_alarm_irq_enable,
.ioctl = rs5c372_ioctl,
.read_offset = rs5c372_read_offset,
.set_offset = rs5c372_set_offset,
};
#if IS_ENABLED(CONFIG_RTC_INTF_SYSFS)

View File

@ -55,6 +55,7 @@
enum rv8803_type {
rv_8803,
rx_8804,
rx_8900
};
@ -601,6 +602,7 @@ static int rv8803_probe(struct i2c_client *client,
static const struct i2c_device_id rv8803_id[] = {
{ "rv8803", rv_8803 },
{ "rv8804", rx_8804 },
{ "rx8803", rv_8803 },
{ "rx8900", rx_8900 },
{ }
@ -616,6 +618,10 @@ static const __maybe_unused struct of_device_id rv8803_of_match[] = {
.compatible = "epson,rx8803",
.data = (void *)rv_8803
},
{
.compatible = "epson,rx8804",
.data = (void *)rx_8804
},
{
.compatible = "epson,rx8900",
.data = (void *)rx_8900

362
drivers/rtc/rtc-sunplus.c Normal file
View File

@ -0,0 +1,362 @@
// SPDX-License-Identifier: GPL-2.0
/*
* The RTC driver for Sunplus SP7021
*
* Copyright (C) 2019 Sunplus Technology Inc., All rights reseerved.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/rtc.h>
#define RTC_REG_NAME "rtc"
#define RTC_CTRL 0x40
#define TIMER_FREEZE_MASK_BIT BIT(5 + 16)
#define TIMER_FREEZE BIT(5)
#define DIS_SYS_RST_RTC_MASK_BIT BIT(4 + 16)
#define DIS_SYS_RST_RTC BIT(4)
#define RTC32K_MODE_RESET_MASK_BIT BIT(3 + 16)
#define RTC32K_MODE_RESET BIT(3)
#define ALARM_EN_OVERDUE_MASK_BIT BIT(2 + 16)
#define ALARM_EN_OVERDUE BIT(2)
#define ALARM_EN_PMC_MASK_BIT BIT(1 + 16)
#define ALARM_EN_PMC BIT(1)
#define ALARM_EN_MASK_BIT BIT(0 + 16)
#define ALARM_EN BIT(0)
#define RTC_TIMER_OUT 0x44
#define RTC_DIVIDER 0x48
#define RTC_TIMER_SET 0x4c
#define RTC_ALARM_SET 0x50
#define RTC_USER_DATA 0x54
#define RTC_RESET_RECORD 0x58
#define RTC_BATT_CHARGE_CTRL 0x5c
#define BAT_CHARGE_RSEL_MASK_BIT GENMASK(3 + 16, 2 + 16)
#define BAT_CHARGE_RSEL_MASK GENMASK(3, 2)
#define BAT_CHARGE_RSEL_2K_OHM FIELD_PREP(BAT_CHARGE_RSEL_MASK, 0)
#define BAT_CHARGE_RSEL_250_OHM FIELD_PREP(BAT_CHARGE_RSEL_MASK, 1)
#define BAT_CHARGE_RSEL_50_OHM FIELD_PREP(BAT_CHARGE_RSEL_MASK, 2)
#define BAT_CHARGE_RSEL_0_OHM FIELD_PREP(BAT_CHARGE_RSEL_MASK, 3)
#define BAT_CHARGE_DSEL_MASK_BIT BIT(1 + 16)
#define BAT_CHARGE_DSEL_MASK GENMASK(1, 1)
#define BAT_CHARGE_DSEL_ON FIELD_PREP(BAT_CHARGE_DSEL_MASK, 0)
#define BAT_CHARGE_DSEL_OFF FIELD_PREP(BAT_CHARGE_DSEL_MASK, 1)
#define BAT_CHARGE_EN_MASK_BIT BIT(0 + 16)
#define BAT_CHARGE_EN BIT(0)
#define RTC_TRIM_CTRL 0x60
struct sunplus_rtc {
struct rtc_device *rtc;
struct resource *res;
struct clk *rtcclk;
struct reset_control *rstc;
void __iomem *reg_base;
int irq;
};
static void sp_get_seconds(struct device *dev, unsigned long *secs)
{
struct sunplus_rtc *sp_rtc = dev_get_drvdata(dev);
*secs = (unsigned long)readl(sp_rtc->reg_base + RTC_TIMER_OUT);
}
static void sp_set_seconds(struct device *dev, unsigned long secs)
{
struct sunplus_rtc *sp_rtc = dev_get_drvdata(dev);
writel((u32)secs, sp_rtc->reg_base + RTC_TIMER_SET);
}
static int sp_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
unsigned long secs;
sp_get_seconds(dev, &secs);
rtc_time64_to_tm(secs, tm);
return 0;
}
static int sp_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
unsigned long secs;
secs = rtc_tm_to_time64(tm);
dev_dbg(dev, "%s, secs = %lu\n", __func__, secs);
sp_set_seconds(dev, secs);
return 0;
}
static int sp_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct sunplus_rtc *sp_rtc = dev_get_drvdata(dev);
unsigned long alarm_time;
alarm_time = rtc_tm_to_time64(&alrm->time);
dev_dbg(dev, "%s, alarm_time: %u\n", __func__, (u32)(alarm_time));
writel((u32)alarm_time, sp_rtc->reg_base + RTC_ALARM_SET);
return 0;
}
static int sp_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct sunplus_rtc *sp_rtc = dev_get_drvdata(dev);
unsigned int alarm_time;
alarm_time = readl(sp_rtc->reg_base + RTC_ALARM_SET);
dev_dbg(dev, "%s, alarm_time: %u\n", __func__, alarm_time);
if (alarm_time == 0)
alrm->enabled = 0;
else
alrm->enabled = 1;
rtc_time64_to_tm((unsigned long)(alarm_time), &alrm->time);
return 0;
}
static int sp_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct sunplus_rtc *sp_rtc = dev_get_drvdata(dev);
if (enabled)
writel((TIMER_FREEZE_MASK_BIT | DIS_SYS_RST_RTC_MASK_BIT |
RTC32K_MODE_RESET_MASK_BIT | ALARM_EN_OVERDUE_MASK_BIT |
ALARM_EN_PMC_MASK_BIT | ALARM_EN_MASK_BIT) |
(DIS_SYS_RST_RTC | ALARM_EN_OVERDUE | ALARM_EN_PMC | ALARM_EN),
sp_rtc->reg_base + RTC_CTRL);
else
writel((ALARM_EN_OVERDUE_MASK_BIT | ALARM_EN_PMC_MASK_BIT | ALARM_EN_MASK_BIT) |
0x0, sp_rtc->reg_base + RTC_CTRL);
return 0;
}
static const struct rtc_class_ops sp_rtc_ops = {
.read_time = sp_rtc_read_time,
.set_time = sp_rtc_set_time,
.set_alarm = sp_rtc_set_alarm,
.read_alarm = sp_rtc_read_alarm,
.alarm_irq_enable = sp_rtc_alarm_irq_enable,
};
static irqreturn_t sp_rtc_irq_handler(int irq, void *dev_id)
{
struct platform_device *plat_dev = dev_id;
struct sunplus_rtc *sp_rtc = dev_get_drvdata(&plat_dev->dev);
rtc_update_irq(sp_rtc->rtc, 1, RTC_IRQF | RTC_AF);
dev_dbg(&plat_dev->dev, "[RTC] ALARM INT\n");
return IRQ_HANDLED;
}
/*
* -------------------------------------------------------------------------------------
* bat_charge_rsel bat_charge_dsel bat_charge_en Remarks
* x x 0 Disable
* 0 0 1 0.86mA (2K Ohm with diode)
* 1 0 1 1.81mA (250 Ohm with diode)
* 2 0 1 2.07mA (50 Ohm with diode)
* 3 0 1 16.0mA (0 Ohm with diode)
* 0 1 1 1.36mA (2K Ohm without diode)
* 1 1 1 3.99mA (250 Ohm without diode)
* 2 1 1 4.41mA (50 Ohm without diode)
* 3 1 1 16.0mA (0 Ohm without diode)
* -------------------------------------------------------------------------------------
*/
static void sp_rtc_set_trickle_charger(struct device dev)
{
struct sunplus_rtc *sp_rtc = dev_get_drvdata(&dev);
u32 ohms, rsel;
u32 chargeable;
if (of_property_read_u32(dev.of_node, "trickle-resistor-ohms", &ohms) ||
of_property_read_u32(dev.of_node, "aux-voltage-chargeable", &chargeable)) {
dev_warn(&dev, "battery charger disabled\n");
return;
}
switch (ohms) {
case 2000:
rsel = BAT_CHARGE_RSEL_2K_OHM;
break;
case 250:
rsel = BAT_CHARGE_RSEL_250_OHM;
break;
case 50:
rsel = BAT_CHARGE_RSEL_50_OHM;
break;
case 0:
rsel = BAT_CHARGE_RSEL_0_OHM;
break;
default:
dev_err(&dev, "invalid charger resistor value (%d)\n", ohms);
return;
}
writel(BAT_CHARGE_RSEL_MASK_BIT | rsel, sp_rtc->reg_base + RTC_BATT_CHARGE_CTRL);
switch (chargeable) {
case 0:
writel(BAT_CHARGE_DSEL_MASK_BIT | BAT_CHARGE_DSEL_OFF,
sp_rtc->reg_base + RTC_BATT_CHARGE_CTRL);
break;
case 1:
writel(BAT_CHARGE_DSEL_MASK_BIT | BAT_CHARGE_DSEL_ON,
sp_rtc->reg_base + RTC_BATT_CHARGE_CTRL);
break;
default:
dev_err(&dev, "invalid aux-voltage-chargeable value (%d)\n", chargeable);
return;
}
writel(BAT_CHARGE_EN_MASK_BIT | BAT_CHARGE_EN, sp_rtc->reg_base + RTC_BATT_CHARGE_CTRL);
}
static int sp_rtc_probe(struct platform_device *plat_dev)
{
struct sunplus_rtc *sp_rtc;
int ret;
sp_rtc = devm_kzalloc(&plat_dev->dev, sizeof(*sp_rtc), GFP_KERNEL);
if (!sp_rtc)
return -ENOMEM;
sp_rtc->res = platform_get_resource_byname(plat_dev, IORESOURCE_MEM, RTC_REG_NAME);
sp_rtc->reg_base = devm_ioremap_resource(&plat_dev->dev, sp_rtc->res);
if (IS_ERR(sp_rtc->reg_base))
return dev_err_probe(&plat_dev->dev, PTR_ERR(sp_rtc->reg_base),
"%s devm_ioremap_resource fail\n", RTC_REG_NAME);
dev_dbg(&plat_dev->dev, "res = 0x%x, reg_base = 0x%lx\n",
sp_rtc->res->start, (unsigned long)sp_rtc->reg_base);
sp_rtc->irq = platform_get_irq(plat_dev, 0);
if (sp_rtc->irq < 0)
return dev_err_probe(&plat_dev->dev, sp_rtc->irq, "platform_get_irq failed\n");
ret = devm_request_irq(&plat_dev->dev, sp_rtc->irq, sp_rtc_irq_handler,
IRQF_TRIGGER_RISING, "rtc irq", plat_dev);
if (ret)
return dev_err_probe(&plat_dev->dev, ret, "devm_request_irq failed:\n");
sp_rtc->rtcclk = devm_clk_get(&plat_dev->dev, NULL);
if (IS_ERR(sp_rtc->rtcclk))
return dev_err_probe(&plat_dev->dev, PTR_ERR(sp_rtc->rtcclk),
"devm_clk_get fail\n");
sp_rtc->rstc = devm_reset_control_get_exclusive(&plat_dev->dev, NULL);
if (IS_ERR(sp_rtc->rstc))
return dev_err_probe(&plat_dev->dev, PTR_ERR(sp_rtc->rstc),
"failed to retrieve reset controller\n");
ret = clk_prepare_enable(sp_rtc->rtcclk);
if (ret)
goto free_clk;
ret = reset_control_deassert(sp_rtc->rstc);
if (ret)
goto free_reset_assert;
device_init_wakeup(&plat_dev->dev, 1);
dev_set_drvdata(&plat_dev->dev, sp_rtc);
sp_rtc->rtc = devm_rtc_allocate_device(&plat_dev->dev);
if (IS_ERR(sp_rtc->rtc)) {
ret = PTR_ERR(sp_rtc->rtc);
goto free_reset_assert;
}
sp_rtc->rtc->range_max = U32_MAX;
sp_rtc->rtc->range_min = 0;
sp_rtc->rtc->ops = &sp_rtc_ops;
ret = devm_rtc_register_device(sp_rtc->rtc);
if (ret)
goto free_reset_assert;
/* Setup trickle charger */
if (plat_dev->dev.of_node)
sp_rtc_set_trickle_charger(plat_dev->dev);
/* Keep RTC from system reset */
writel(DIS_SYS_RST_RTC_MASK_BIT | DIS_SYS_RST_RTC, sp_rtc->reg_base + RTC_CTRL);
return 0;
free_reset_assert:
reset_control_assert(sp_rtc->rstc);
free_clk:
clk_disable_unprepare(sp_rtc->rtcclk);
return ret;
}
static int sp_rtc_remove(struct platform_device *plat_dev)
{
struct sunplus_rtc *sp_rtc = dev_get_drvdata(&plat_dev->dev);
device_init_wakeup(&plat_dev->dev, 0);
reset_control_assert(sp_rtc->rstc);
clk_disable_unprepare(sp_rtc->rtcclk);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int sp_rtc_suspend(struct device *dev)
{
struct sunplus_rtc *sp_rtc = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
enable_irq_wake(sp_rtc->irq);
return 0;
}
static int sp_rtc_resume(struct device *dev)
{
struct sunplus_rtc *sp_rtc = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
disable_irq_wake(sp_rtc->irq);
return 0;
}
#endif
static const struct of_device_id sp_rtc_of_match[] = {
{ .compatible = "sunplus,sp7021-rtc" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sp_rtc_of_match);
static SIMPLE_DEV_PM_OPS(sp_rtc_pm_ops, sp_rtc_suspend, sp_rtc_resume);
static struct platform_driver sp_rtc_driver = {
.probe = sp_rtc_probe,
.remove = sp_rtc_remove,
.driver = {
.name = "sp7021-rtc",
.of_match_table = sp_rtc_of_match,
.pm = &sp_rtc_pm_ops,
},
};
module_platform_driver(sp_rtc_driver);
MODULE_AUTHOR("Vincent Shih <vincent.sunplus@gmail.com>");
MODULE_DESCRIPTION("Sunplus RTC driver");
MODULE_LICENSE("GPL v2");

View File

@ -123,7 +123,11 @@ struct cmos_rtc_board_info {
#define RTC_IO_EXTENT_USED RTC_IO_EXTENT
#endif /* ARCH_RTC_LOCATION */
unsigned int mc146818_get_time(struct rtc_time *time);
bool mc146818_does_rtc_work(void);
int mc146818_get_time(struct rtc_time *time);
int mc146818_set_time(struct rtc_time *time);
bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param),
void *param);
#endif /* _MC146818RTC_H */