Compare commits

...

10 Commits

Author SHA1 Message Date
Tom Rini
b8b7761e89 Merge patch series "rtc: pcf85036: improve spec coherence and extend features"
Alexander Feilke <alexander.feilke@ew.tq-group.com> says:

This series fixes some spec inconsistencies and oversimplifications in the initial
PCF85063 support, and adds some missing features from mainline linux v6.19.

[trini: Make a minor correction to the Kconfig help that Alexander
        Sverdlin noted in review]
Signed-off-by: Tom Rini <trini@konsulko.com>
Link: https://lore.kernel.org/r/20260522153929.2472552-1-Alexander.Feilke@ew.tq-group.com
2026-06-05 10:14:24 -06:00
Alexander Feilke
8294d86b1d drivers: Kconfig: rtc_pcf85063: note unsupported chip features
Signed-off-by: Alexander Feilke <alexander.feilke@ew.tq-group.com>
2026-06-05 10:14:24 -06:00
Alexander Feilke
059174b955 rtc: pcf85063: add power loss detection during probe
Retrofit from upstream linux to try resetting the device after power loss.

Reviewed-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
Signed-off-by: Alexander Feilke <alexander.feilke@ew.tq-group.com>
2026-06-05 10:14:24 -06:00
Alexander Feilke
d8f535cd8f rtc: pcf85063: support loading quartz-load capacitance from device tree
Use previously ignored quartz-load-femtofarads property from device tree
to set load capacitance. If missing, leave the device unconfigured
as a default might have been set. force_cap is left out for now but
can be retrofitted in the future as there may be different hardware
without the 12.500pF flag.

Reviewed-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
Signed-off-by: Alexander Feilke <alexander.feilke@ew.tq-group.com>
2026-06-05 10:14:24 -06:00
Alexander Feilke
2b6de25797 rtc: pcf85063: keep the divider chain in reset during set_time
Sync from upstream linux v6.19.

Reviewed-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
Signed-off-by: Alexander Feilke <alexander.feilke@ew.tq-group.com>
2026-06-05 10:14:24 -06:00
Alexander Feilke
49754d293e rtc: pcf85063: add missing register definitions
Sync definitions from upstream linux v6.19.

Reviewed-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
Signed-off-by: Alexander Feilke <alexander.feilke@ew.tq-group.com>
2026-06-05 10:14:24 -06:00
Alexander Feilke
1f04246641 rtc: pcf85063: add support for NXP PCF85063 family
Supported devices:

- generic PCF85063 / PCF85063TP (no alarm regs)
- PCF85063A / PCF85073A (alarm regs)

Tested with TQMa8MPxL SOM from TQ-Systems GmbH.

Also add missing .data field to rv8263 which represents the number
of available registers (= linux `pcf85063_config.max_register + 1`).

Signed-off-by: Markus Niebel <Markus.Niebel@ew.tq-group.com>
Reviewed-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
Signed-off-by: Alexander Feilke <alexander.feilke@ew.tq-group.com>
2026-06-05 10:14:24 -06:00
Alexander Feilke
8763c0169d rtc: pcf85063: adjust date format to adhere to the rtc_time spec
The rtc_time documentation in rtc_def.h notes a differences
to the common "struct time" that specifies tm_mon as 1 ... 12
and tm_year as year since 0. Also trim register values to valid bits.

Fixes: 1c2a2253f7 ("drivers: rtc: add PCF85063 support")

Reviewed-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
Signed-off-by: Alexander Feilke <alexander.feilke@ew.tq-group.com>
2026-06-05 10:14:24 -06:00
Markus Niebel
3bf0e5d90c cmd: date: validate date using rtc_month_days()
The old check accepted day 0 as well as Feb 29th in non-leap years.
With this change, both day and month 0 are rejected, and the local day
limit logic is now handled by rtc_month_days(), which correctly accounts
for month length and leap years.

In the 'MMDDhhmm' format case, tm_year is not initialized by mk_date().
The leap-year calculation in rtc_month_days() therefore depends on the
value provided by the caller, which do_date() does, via dm_rtc_get().
This is pre-existing behaviour, but is now made more explicit.

Signed-off-by: Markus Niebel <Markus.Niebel@ew.tq-group.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
Signed-off-by: Alexander Feilke <alexander.feilke@ew.tq-group.com>
2026-06-05 10:14:24 -06:00
Alexander Feilke
58545b0106 cmd_date: make mk_date() static
Use static as this function is not used externally.

Signed-off-by: Alexander Feilke <alexander.feilke@ew.tq-group.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
2026-06-05 10:14:24 -06:00
3 changed files with 135 additions and 13 deletions

View File

@@ -16,7 +16,7 @@ static const char * const weekdays[] = {
"Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur",
};
int mk_date (const char *, struct rtc_time *);
static int mk_date(const char *, struct rtc_time *);
static struct rtc_time default_tm = { 0, 0, 0, 1, 1, 2000, 6, 0, 0 };
@@ -117,7 +117,7 @@ static int cnvrt2 (const char *str, int *valp)
* Some basic checking for valid values is done, but this will not catch
* all possible error conditions.
*/
int mk_date (const char *datestr, struct rtc_time *tmp)
static int mk_date(const char *datestr, struct rtc_time *tmp)
{
int len, val;
char *ptr;
@@ -167,12 +167,13 @@ int mk_date (const char *datestr, struct rtc_time *tmp)
/* fall thru */
case 12: /* MMDDhhmmCCYY */
if (cnvrt2 (datestr+0, &val) ||
val > 12) {
val > 12 || val < 1) {
break;
}
tmp->tm_mon = val;
if (cnvrt2 (datestr+2, &val) ||
val > ((tmp->tm_mon==2) ? 29 : 31)) {
if (cnvrt2(datestr + 2, &val) ||
val < 1 ||
val > rtc_month_days(tmp->tm_mon - 1, tmp->tm_year)) {
break;
}
tmp->tm_mday = val;

View File

@@ -163,6 +163,9 @@ config RTC_PCF85063
help
If you say yes here you get support for the NXP PCF85063 RTC
and compatible chips.
Support for the following chip features is currently not implemented:
- NVMEM device for RAM register
- CLKOUT generation
config RTC_PCF8563
bool "Philips PCF8563"

View File

@@ -11,11 +11,33 @@
#include <dm/device_compat.h>
#define PCF85063_REG_CTRL1 0x00 /* status */
#define PCF85063_REG_CTRL1_SR 0x58
#define PCF85063_REG_CTRL1_CAP_SEL BIT(0)
#define PCF85063_REG_CTRL1_STOP BIT(5)
#define PCF85063_REG_CTRL1_EXT_TEST BIT(7)
#define PCF85063_REG_CTRL1_SWR 0x58 /* Software reset command */
#define PCF85063_REG_CTRL2 0x01
#define PCF85063_CTRL2_AF BIT(6)
#define PCF85063_CTRL2_AIE BIT(7)
#define PCF85063_REG_OFFSET 0x02
#define PCF85063_OFFSET_SIGN_BIT 6 /* 2's complement sign bit */
#define PCF85063_OFFSET_MODE BIT(7)
#define PCF85063_OFFSET_STEP0 4340
#define PCF85063_OFFSET_STEP1 4069
#define PCF85063_REG_CLKO_F_MASK 0x07 /* frequency mask */
#define PCF85063_REG_CLKO_F_32768HZ 0x00
#define PCF85063_REG_CLKO_F_OFF 0x07
#define PCF85063_REG_RAM 0x03
#define PCF85063_REG_SC 0x04 /* datetime */
#define PCF85063_REG_SC_OS 0x80
#define PCF85063_REG_ALM_S 0x0b
#define PCF85063_AEN BIT(7)
static int pcf85063_get_time(struct udevice *dev, struct rtc_time *tm)
{
u8 regs[7];
@@ -35,7 +57,9 @@ static int pcf85063_get_time(struct udevice *dev, struct rtc_time *tm)
tm->tm_hour = bcd2bin(regs[2] & 0x3f);
tm->tm_mday = bcd2bin(regs[3] & 0x3f);
tm->tm_wday = regs[4] & 0x07;
tm->tm_mon = bcd2bin(regs[5] & 0x1f) - 1;
/* rtc register and rtc_time spec uses 1 - 12 */
tm->tm_mon = bcd2bin(regs[5] & 0x1f);
/* adjust rtc_time (years since 0) to match register spec */
tm->tm_year = bcd2bin(regs[6]) + 2000;
return 0;
@@ -44,40 +68,102 @@ static int pcf85063_get_time(struct udevice *dev, struct rtc_time *tm)
static int pcf85063_set_time(struct udevice *dev, const struct rtc_time *tm)
{
u8 regs[7];
int rc;
if (tm->tm_year < 2000 || tm->tm_year > 2099) {
dev_err(dev, "Year must be between 2000 and 2099.\n");
return -EINVAL;
}
regs[0] = bin2bcd(tm->tm_sec);
/*
* to accurately set the time, reset the divider chain and keep it in
* reset state until all time/date registers are written
*/
rc = dm_i2c_reg_clrset(dev, PCF85063_REG_CTRL1,
PCF85063_REG_CTRL1_EXT_TEST |
PCF85063_REG_CTRL1_STOP,
PCF85063_REG_CTRL1_STOP);
if (rc)
return rc;
/* hours, minutes and seconds */
regs[0] = bin2bcd(tm->tm_sec) & (~PCF85063_REG_SC_OS);
regs[1] = bin2bcd(tm->tm_min);
regs[2] = bin2bcd(tm->tm_hour);
/* Day of month, 1 - 31 */
regs[3] = bin2bcd(tm->tm_mday);
regs[4] = tm->tm_wday;
regs[5] = bin2bcd(tm->tm_mon + 1);
/* Day of week 0 - 6 */
regs[4] = tm->tm_wday & 0x07;
/* rtc register and rtc_time spec uses 1 - 12 */
regs[5] = bin2bcd(tm->tm_mon);
/* adjust register to match rtc_time spec */
regs[6] = bin2bcd(tm->tm_year % 100);
return dm_i2c_write(dev, PCF85063_REG_SC, regs, sizeof(regs));
rc = dm_i2c_write(dev, PCF85063_REG_SC, regs, sizeof(regs));
if (rc)
return rc;
/*
* Write the control register as a separate action since the size of
* the register space is different between the PCF85063TP and
* PCF85063A devices. The rollover point can not be used.
*/
return dm_i2c_reg_clrset(dev, PCF85063_REG_CTRL1,
PCF85063_REG_CTRL1_STOP, 0);
}
static int pcf85063_reset(struct udevice *dev)
{
return dm_i2c_reg_write(dev, PCF85063_REG_CTRL1, PCF85063_REG_CTRL1_SR);
return dm_i2c_reg_write(dev, PCF85063_REG_CTRL1, PCF85063_REG_CTRL1_SWR);
}
static int pcf85063_read(struct udevice *dev, unsigned int offset, u8 *buf,
unsigned int len)
{
if (offset + len > dev->driver_data)
return -EINVAL;
return dm_i2c_read(dev, offset, buf, len);
}
static int pcf85063_write(struct udevice *dev, unsigned int offset,
const u8 *buf, unsigned int len)
{
if (offset + len > dev->driver_data)
return -EINVAL;
return dm_i2c_write(dev, offset, buf, len);
}
static int pcf85063_load_capacitance(struct udevice *dev)
{
u32 load = 7000;
u8 reg = 0;
if (ofnode_read_u32(dev_ofnode(dev), "quartz-load-femtofarads", &load))
return 0;
switch (load) {
default:
dev_warn(dev, "Unknown quartz-load-femtofarads value: %d. Assuming 7000",
load);
fallthrough;
case 7000:
break;
case 12500:
reg = PCF85063_REG_CTRL1_CAP_SEL;
break;
}
return dm_i2c_reg_clrset(dev, PCF85063_REG_CTRL1,
PCF85063_REG_CTRL1_CAP_SEL, reg);
}
static const struct rtc_ops pcf85063_rtc_ops = {
.get = pcf85063_get_time,
.set = pcf85063_set_time,
@@ -88,13 +174,45 @@ static const struct rtc_ops pcf85063_rtc_ops = {
static int pcf85063_probe(struct udevice *dev)
{
u8 tmp;
int err;
i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS | DM_I2C_CHIP_WR_ADDRESS);
err = dm_i2c_read(dev, PCF85063_REG_SC, &tmp, sizeof(tmp));
if (err) {
dev_err(dev, "RTC chip is not present\n");
return err;
}
/*
* If a Power loss is detected, SW reset the device.
* From PCF85063A datasheet:
* There is a low probability that some devices will have corruption
* of the registers after the automatic power-on reset...
*/
if (tmp & PCF85063_REG_SC_OS) {
dev_warn(dev, "POR issue detected, sending a SW reset\n");
err = dm_i2c_reg_clrset(dev, PCF85063_REG_CTRL1,
0xff, PCF85063_REG_CTRL1_SWR);
if (err < 0)
dev_warn(dev, "SW reset failed, trying to continue\n");
}
err = pcf85063_load_capacitance(dev);
if (err < 0)
dev_warn(dev, "failed to set xtal load capacitance: %d",
err);
return 0;
}
static const struct udevice_id pcf85063_of_id[] = {
{ .compatible = "microcrystal,rv8263" },
{ .compatible = "microcrystal,rv8263", .data = 0x12 },
{ .compatible = "nxp,pcf85063", .data = 0xb },
{ .compatible = "nxp,pcf85063a", .data = 0x12 },
{ .compatible = "nxp,pcf85063tp", .data = 0xb },
{ .compatible = "nxp,pcf85073a", .data = 0x12 },
{ }
};