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-opal.c (7031B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * IBM OPAL RTC driver
      4 * Copyright (C) 2014 IBM
      5 */
      6
      7#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
      8
      9#define DRVNAME		"rtc-opal"
     10
     11#include <linux/module.h>
     12#include <linux/err.h>
     13#include <linux/rtc.h>
     14#include <linux/delay.h>
     15#include <linux/bcd.h>
     16#include <linux/platform_device.h>
     17#include <linux/of.h>
     18#include <asm/opal.h>
     19#include <asm/firmware.h>
     20
     21static void opal_to_tm(u32 y_m_d, u64 h_m_s_ms, struct rtc_time *tm)
     22{
     23	tm->tm_year = ((bcd2bin(y_m_d >> 24) * 100) +
     24		       bcd2bin((y_m_d >> 16) & 0xff)) - 1900;
     25	tm->tm_mon  = bcd2bin((y_m_d >> 8) & 0xff) - 1;
     26	tm->tm_mday = bcd2bin(y_m_d & 0xff);
     27	tm->tm_hour = bcd2bin((h_m_s_ms >> 56) & 0xff);
     28	tm->tm_min  = bcd2bin((h_m_s_ms >> 48) & 0xff);
     29	tm->tm_sec  = bcd2bin((h_m_s_ms >> 40) & 0xff);
     30
     31	tm->tm_wday = -1;
     32}
     33
     34static void tm_to_opal(struct rtc_time *tm, u32 *y_m_d, u64 *h_m_s_ms)
     35{
     36	*y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) / 100)) << 24;
     37	*y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) % 100)) << 16;
     38	*y_m_d |= ((u32)bin2bcd((tm->tm_mon + 1))) << 8;
     39	*y_m_d |= ((u32)bin2bcd(tm->tm_mday));
     40
     41	*h_m_s_ms |= ((u64)bin2bcd(tm->tm_hour)) << 56;
     42	*h_m_s_ms |= ((u64)bin2bcd(tm->tm_min)) << 48;
     43	*h_m_s_ms |= ((u64)bin2bcd(tm->tm_sec)) << 40;
     44}
     45
     46static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm)
     47{
     48	s64 rc = OPAL_BUSY;
     49	int retries = 10;
     50	u32 y_m_d;
     51	u64 h_m_s_ms;
     52	__be32 __y_m_d;
     53	__be64 __h_m_s_ms;
     54
     55	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
     56		rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms);
     57		if (rc == OPAL_BUSY_EVENT) {
     58			msleep(OPAL_BUSY_DELAY_MS);
     59			opal_poll_events(NULL);
     60		} else if (rc == OPAL_BUSY) {
     61			msleep(OPAL_BUSY_DELAY_MS);
     62		} else if (rc == OPAL_HARDWARE || rc == OPAL_INTERNAL_ERROR) {
     63			if (retries--) {
     64				msleep(10); /* Wait 10ms before retry */
     65				rc = OPAL_BUSY; /* go around again */
     66			}
     67		}
     68	}
     69
     70	if (rc != OPAL_SUCCESS)
     71		return -EIO;
     72
     73	y_m_d = be32_to_cpu(__y_m_d);
     74	h_m_s_ms = be64_to_cpu(__h_m_s_ms);
     75	opal_to_tm(y_m_d, h_m_s_ms, tm);
     76
     77	return 0;
     78}
     79
     80static int opal_set_rtc_time(struct device *dev, struct rtc_time *tm)
     81{
     82	s64 rc = OPAL_BUSY;
     83	int retries = 10;
     84	u32 y_m_d = 0;
     85	u64 h_m_s_ms = 0;
     86
     87	tm_to_opal(tm, &y_m_d, &h_m_s_ms);
     88
     89	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
     90		rc = opal_rtc_write(y_m_d, h_m_s_ms);
     91		if (rc == OPAL_BUSY_EVENT) {
     92			msleep(OPAL_BUSY_DELAY_MS);
     93			opal_poll_events(NULL);
     94		} else if (rc == OPAL_BUSY) {
     95			msleep(OPAL_BUSY_DELAY_MS);
     96		} else if (rc == OPAL_HARDWARE || rc == OPAL_INTERNAL_ERROR) {
     97			if (retries--) {
     98				msleep(10); /* Wait 10ms before retry */
     99				rc = OPAL_BUSY; /* go around again */
    100			}
    101		}
    102	}
    103
    104	return rc == OPAL_SUCCESS ? 0 : -EIO;
    105}
    106
    107/*
    108 * TPO	Timed Power-On
    109 *
    110 * TPO get/set OPAL calls care about the hour and min and to make it consistent
    111 * with the rtc utility time conversion functions, we use the 'u64' to store
    112 * its value and perform bit shift by 32 before use..
    113 */
    114static int opal_get_tpo_time(struct device *dev, struct rtc_wkalrm *alarm)
    115{
    116	__be32 __y_m_d, __h_m;
    117	struct opal_msg msg;
    118	int rc, token;
    119	u64 h_m_s_ms;
    120	u32 y_m_d;
    121
    122	token = opal_async_get_token_interruptible();
    123	if (token < 0) {
    124		if (token != -ERESTARTSYS)
    125			pr_err("Failed to get the async token\n");
    126
    127		return token;
    128	}
    129
    130	rc = opal_tpo_read(token, &__y_m_d, &__h_m);
    131	if (rc != OPAL_ASYNC_COMPLETION) {
    132		rc = -EIO;
    133		goto exit;
    134	}
    135
    136	rc = opal_async_wait_response(token, &msg);
    137	if (rc) {
    138		rc = -EIO;
    139		goto exit;
    140	}
    141
    142	rc = opal_get_async_rc(msg);
    143	if (rc != OPAL_SUCCESS) {
    144		rc = -EIO;
    145		goto exit;
    146	}
    147
    148	y_m_d = be32_to_cpu(__y_m_d);
    149	h_m_s_ms = ((u64)be32_to_cpu(__h_m) << 32);
    150
    151	/* check if no alarm is set */
    152	if (y_m_d == 0 && h_m_s_ms == 0) {
    153		pr_debug("No alarm is set\n");
    154		rc = -ENOENT;
    155		goto exit;
    156	} else {
    157		pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms);
    158	}
    159
    160	opal_to_tm(y_m_d, h_m_s_ms, &alarm->time);
    161
    162exit:
    163	opal_async_release_token(token);
    164	return rc;
    165}
    166
    167/* Set Timed Power-On */
    168static int opal_set_tpo_time(struct device *dev, struct rtc_wkalrm *alarm)
    169{
    170	u64 h_m_s_ms = 0;
    171	struct opal_msg msg;
    172	u32 y_m_d = 0;
    173	int token, rc;
    174
    175	/* if alarm is enabled */
    176	if (alarm->enabled) {
    177		tm_to_opal(&alarm->time, &y_m_d, &h_m_s_ms);
    178		pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms);
    179
    180	} else {
    181		pr_debug("Alarm getting disabled\n");
    182	}
    183
    184	token = opal_async_get_token_interruptible();
    185	if (token < 0) {
    186		if (token != -ERESTARTSYS)
    187			pr_err("Failed to get the async token\n");
    188
    189		return token;
    190	}
    191
    192	/* TPO, we care about hour and minute */
    193	rc = opal_tpo_write(token, y_m_d,
    194			    (u32)((h_m_s_ms >> 32) & 0xffff0000));
    195	if (rc != OPAL_ASYNC_COMPLETION) {
    196		rc = -EIO;
    197		goto exit;
    198	}
    199
    200	rc = opal_async_wait_response(token, &msg);
    201	if (rc) {
    202		rc = -EIO;
    203		goto exit;
    204	}
    205
    206	rc = opal_get_async_rc(msg);
    207	if (rc != OPAL_SUCCESS)
    208		rc = -EIO;
    209
    210exit:
    211	opal_async_release_token(token);
    212	return rc;
    213}
    214
    215static int opal_tpo_alarm_irq_enable(struct device *dev, unsigned int enabled)
    216{
    217	struct rtc_wkalrm alarm = { .enabled = 0 };
    218
    219	/*
    220	 * TPO is automatically enabled when opal_set_tpo_time() is called with
    221	 * non-zero rtc-time. We only handle disable case which needs to be
    222	 * explicitly told to opal.
    223	 */
    224	return enabled ? 0 : opal_set_tpo_time(dev, &alarm);
    225}
    226
    227static const struct rtc_class_ops opal_rtc_ops = {
    228	.read_time	= opal_get_rtc_time,
    229	.set_time	= opal_set_rtc_time,
    230	.read_alarm	= opal_get_tpo_time,
    231	.set_alarm	= opal_set_tpo_time,
    232	.alarm_irq_enable = opal_tpo_alarm_irq_enable,
    233};
    234
    235static int opal_rtc_probe(struct platform_device *pdev)
    236{
    237	struct rtc_device *rtc;
    238
    239	rtc = devm_rtc_allocate_device(&pdev->dev);
    240	if (IS_ERR(rtc))
    241		return PTR_ERR(rtc);
    242
    243	if (pdev->dev.of_node &&
    244	    (of_property_read_bool(pdev->dev.of_node, "wakeup-source") ||
    245	     of_property_read_bool(pdev->dev.of_node, "has-tpo")/* legacy */))
    246		device_set_wakeup_capable(&pdev->dev, true);
    247	else
    248		clear_bit(RTC_FEATURE_ALARM, rtc->features);
    249
    250	rtc->ops = &opal_rtc_ops;
    251	rtc->range_min = RTC_TIMESTAMP_BEGIN_0000;
    252	rtc->range_max = RTC_TIMESTAMP_END_9999;
    253	clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
    254
    255	return devm_rtc_register_device(rtc);
    256}
    257
    258static const struct of_device_id opal_rtc_match[] = {
    259	{
    260		.compatible	= "ibm,opal-rtc",
    261	},
    262	{ }
    263};
    264MODULE_DEVICE_TABLE(of, opal_rtc_match);
    265
    266static const struct platform_device_id opal_rtc_driver_ids[] = {
    267	{
    268		.name		= "opal-rtc",
    269	},
    270	{ }
    271};
    272MODULE_DEVICE_TABLE(platform, opal_rtc_driver_ids);
    273
    274static struct platform_driver opal_rtc_driver = {
    275	.probe		= opal_rtc_probe,
    276	.id_table	= opal_rtc_driver_ids,
    277	.driver		= {
    278		.name		= DRVNAME,
    279		.of_match_table	= opal_rtc_match,
    280	},
    281};
    282
    283static int __init opal_rtc_init(void)
    284{
    285	if (!firmware_has_feature(FW_FEATURE_OPAL))
    286		return -ENODEV;
    287
    288	return platform_driver_register(&opal_rtc_driver);
    289}
    290
    291static void __exit opal_rtc_exit(void)
    292{
    293	platform_driver_unregister(&opal_rtc_driver);
    294}
    295
    296MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");
    297MODULE_DESCRIPTION("IBM OPAL RTC driver");
    298MODULE_LICENSE("GPL");
    299
    300module_init(opal_rtc_init);
    301module_exit(opal_rtc_exit);