cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rtc-ntxec.c (3989B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * The Netronix embedded controller is a microcontroller found in some
      4 * e-book readers designed by the original design manufacturer Netronix, Inc.
      5 * It contains RTC, battery monitoring, system power management, and PWM
      6 * functionality.
      7 *
      8 * This driver implements access to the RTC time and date.
      9 *
     10 * Copyright 2020 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
     11 */
     12
     13#include <linux/mfd/ntxec.h>
     14#include <linux/module.h>
     15#include <linux/platform_device.h>
     16#include <linux/regmap.h>
     17#include <linux/rtc.h>
     18#include <linux/types.h>
     19
     20struct ntxec_rtc {
     21	struct device *dev;
     22	struct ntxec *ec;
     23};
     24
     25#define NTXEC_REG_WRITE_YEAR	0x10
     26#define NTXEC_REG_WRITE_MONTH	0x11
     27#define NTXEC_REG_WRITE_DAY	0x12
     28#define NTXEC_REG_WRITE_HOUR	0x13
     29#define NTXEC_REG_WRITE_MINUTE	0x14
     30#define NTXEC_REG_WRITE_SECOND	0x15
     31
     32#define NTXEC_REG_READ_YEAR_MONTH	0x20
     33#define NTXEC_REG_READ_MDAY_HOUR	0x21
     34#define NTXEC_REG_READ_MINUTE_SECOND	0x23
     35
     36static int ntxec_read_time(struct device *dev, struct rtc_time *tm)
     37{
     38	struct ntxec_rtc *rtc = dev_get_drvdata(dev);
     39	unsigned int value;
     40	int res;
     41
     42retry:
     43	res = regmap_read(rtc->ec->regmap, NTXEC_REG_READ_MINUTE_SECOND, &value);
     44	if (res < 0)
     45		return res;
     46
     47	tm->tm_min = value >> 8;
     48	tm->tm_sec = value & 0xff;
     49
     50	res = regmap_read(rtc->ec->regmap, NTXEC_REG_READ_MDAY_HOUR, &value);
     51	if (res < 0)
     52		return res;
     53
     54	tm->tm_mday = value >> 8;
     55	tm->tm_hour = value & 0xff;
     56
     57	res = regmap_read(rtc->ec->regmap, NTXEC_REG_READ_YEAR_MONTH, &value);
     58	if (res < 0)
     59		return res;
     60
     61	tm->tm_year = (value >> 8) + 100;
     62	tm->tm_mon = (value & 0xff) - 1;
     63
     64	/*
     65	 * Read the minutes/seconds field again. If it changed since the first
     66	 * read, we can't assume that the values read so far are consistent,
     67	 * and should start from the beginning.
     68	 */
     69	res = regmap_read(rtc->ec->regmap, NTXEC_REG_READ_MINUTE_SECOND, &value);
     70	if (res < 0)
     71		return res;
     72
     73	if (tm->tm_min != value >> 8 || tm->tm_sec != (value & 0xff))
     74		goto retry;
     75
     76	return 0;
     77}
     78
     79static int ntxec_set_time(struct device *dev, struct rtc_time *tm)
     80{
     81	struct ntxec_rtc *rtc = dev_get_drvdata(dev);
     82
     83	/*
     84	 * To avoid time overflows while we're writing the full date/time,
     85	 * set the seconds field to zero before doing anything else. For the
     86	 * next 59 seconds (plus however long it takes until the RTC's next
     87	 * update of the second field), the seconds field will not overflow
     88	 * into the other fields.
     89	 */
     90	struct reg_sequence regs[] = {
     91		{ NTXEC_REG_WRITE_SECOND, ntxec_reg8(0) },
     92		{ NTXEC_REG_WRITE_YEAR, ntxec_reg8(tm->tm_year - 100) },
     93		{ NTXEC_REG_WRITE_MONTH, ntxec_reg8(tm->tm_mon + 1) },
     94		{ NTXEC_REG_WRITE_DAY, ntxec_reg8(tm->tm_mday) },
     95		{ NTXEC_REG_WRITE_HOUR, ntxec_reg8(tm->tm_hour) },
     96		{ NTXEC_REG_WRITE_MINUTE, ntxec_reg8(tm->tm_min) },
     97		{ NTXEC_REG_WRITE_SECOND, ntxec_reg8(tm->tm_sec) },
     98	};
     99
    100	return regmap_multi_reg_write(rtc->ec->regmap, regs, ARRAY_SIZE(regs));
    101}
    102
    103static const struct rtc_class_ops ntxec_rtc_ops = {
    104	.read_time = ntxec_read_time,
    105	.set_time = ntxec_set_time,
    106};
    107
    108static int ntxec_rtc_probe(struct platform_device *pdev)
    109{
    110	struct rtc_device *dev;
    111	struct ntxec_rtc *rtc;
    112
    113	pdev->dev.of_node = pdev->dev.parent->of_node;
    114
    115	rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
    116	if (!rtc)
    117		return -ENOMEM;
    118
    119	rtc->dev = &pdev->dev;
    120	rtc->ec = dev_get_drvdata(pdev->dev.parent);
    121	platform_set_drvdata(pdev, rtc);
    122
    123	dev = devm_rtc_allocate_device(&pdev->dev);
    124	if (IS_ERR(dev))
    125		return PTR_ERR(dev);
    126
    127	dev->ops = &ntxec_rtc_ops;
    128	dev->range_min = RTC_TIMESTAMP_BEGIN_2000;
    129	dev->range_max = 9025257599LL; /* 2255-12-31 23:59:59 */
    130
    131	return devm_rtc_register_device(dev);
    132}
    133
    134static struct platform_driver ntxec_rtc_driver = {
    135	.driver = {
    136		.name = "ntxec-rtc",
    137	},
    138	.probe = ntxec_rtc_probe,
    139};
    140module_platform_driver(ntxec_rtc_driver);
    141
    142MODULE_AUTHOR("Jonathan Neuschäfer <j.neuschaefer@gmx.net>");
    143MODULE_DESCRIPTION("RTC driver for Netronix EC");
    144MODULE_LICENSE("GPL");
    145MODULE_ALIAS("platform:ntxec-rtc");