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:
commit
75242f31db
@ -15,6 +15,7 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- epson,rx8804
|
||||
- epson,rx8900
|
||||
- microcrystal,rv8803
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>;
|
||||
};
|
||||
...
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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, ¶m);
|
||||
|
@ -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;
|
||||
*
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
377
drivers/rtc/rtc-gamecube.c
Normal 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 don’t 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 can’t 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");
|
@ -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);
|
||||
|
||||
|
@ -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 |
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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
362
drivers/rtc/rtc-sunplus.c
Normal 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");
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user