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-rc5t583.c (8138B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * rtc-rc5t583.c -- RICOH RC5T583 Real Time Clock
      4 *
      5 * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
      6 * Author: Venu Byravarasu <vbyravarasu@nvidia.com>
      7 */
      8
      9#include <linux/kernel.h>
     10#include <linux/errno.h>
     11#include <linux/init.h>
     12#include <linux/module.h>
     13#include <linux/types.h>
     14#include <linux/rtc.h>
     15#include <linux/bcd.h>
     16#include <linux/platform_device.h>
     17#include <linux/interrupt.h>
     18#include <linux/mfd/rc5t583.h>
     19
     20struct rc5t583_rtc {
     21	struct rtc_device	*rtc;
     22	/* To store the list of enabled interrupts, during system suspend */
     23	u32 irqen;
     24};
     25
     26/* Total number of RTC registers needed to set time*/
     27#define NUM_TIME_REGS	(RC5T583_RTC_YEAR - RC5T583_RTC_SEC + 1)
     28
     29/* Total number of RTC registers needed to set Y-Alarm*/
     30#define NUM_YAL_REGS	(RC5T583_RTC_AY_YEAR - RC5T583_RTC_AY_MIN + 1)
     31
     32/* Set Y-Alarm interrupt */
     33#define SET_YAL BIT(5)
     34
     35/* Get Y-Alarm interrupt status*/
     36#define GET_YAL_STATUS BIT(3)
     37
     38static int rc5t583_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
     39{
     40	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
     41	u8 val;
     42
     43	/* Set Y-Alarm, based on 'enabled' */
     44	val = enabled ? SET_YAL : 0;
     45
     46	return regmap_update_bits(rc5t583->regmap, RC5T583_RTC_CTL1, SET_YAL,
     47		val);
     48}
     49
     50/*
     51 * Gets current rc5t583 RTC time and date parameters.
     52 *
     53 * The RTC's time/alarm representation is not what gmtime(3) requires
     54 * Linux to use:
     55 *
     56 *  - Months are 1..12 vs Linux 0-11
     57 *  - Years are 0..99 vs Linux 1900..N (we assume 21st century)
     58 */
     59static int rc5t583_rtc_read_time(struct device *dev, struct rtc_time *tm)
     60{
     61	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
     62	u8 rtc_data[NUM_TIME_REGS];
     63	int ret;
     64
     65	ret = regmap_bulk_read(rc5t583->regmap, RC5T583_RTC_SEC, rtc_data,
     66		NUM_TIME_REGS);
     67	if (ret < 0) {
     68		dev_err(dev, "RTC read time failed with err:%d\n", ret);
     69		return ret;
     70	}
     71
     72	tm->tm_sec = bcd2bin(rtc_data[0]);
     73	tm->tm_min = bcd2bin(rtc_data[1]);
     74	tm->tm_hour = bcd2bin(rtc_data[2]);
     75	tm->tm_wday = bcd2bin(rtc_data[3]);
     76	tm->tm_mday = bcd2bin(rtc_data[4]);
     77	tm->tm_mon = bcd2bin(rtc_data[5]) - 1;
     78	tm->tm_year = bcd2bin(rtc_data[6]) + 100;
     79
     80	return ret;
     81}
     82
     83static int rc5t583_rtc_set_time(struct device *dev, struct rtc_time *tm)
     84{
     85	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
     86	unsigned char rtc_data[NUM_TIME_REGS];
     87	int ret;
     88
     89	rtc_data[0] = bin2bcd(tm->tm_sec);
     90	rtc_data[1] = bin2bcd(tm->tm_min);
     91	rtc_data[2] = bin2bcd(tm->tm_hour);
     92	rtc_data[3] = bin2bcd(tm->tm_wday);
     93	rtc_data[4] = bin2bcd(tm->tm_mday);
     94	rtc_data[5] = bin2bcd(tm->tm_mon + 1);
     95	rtc_data[6] = bin2bcd(tm->tm_year - 100);
     96
     97	ret = regmap_bulk_write(rc5t583->regmap, RC5T583_RTC_SEC, rtc_data,
     98		NUM_TIME_REGS);
     99	if (ret < 0) {
    100		dev_err(dev, "RTC set time failed with error %d\n", ret);
    101		return ret;
    102	}
    103
    104	return ret;
    105}
    106
    107static int rc5t583_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
    108{
    109	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
    110	unsigned char alarm_data[NUM_YAL_REGS];
    111	u32 interrupt_enable;
    112	int ret;
    113
    114	ret = regmap_bulk_read(rc5t583->regmap, RC5T583_RTC_AY_MIN, alarm_data,
    115		NUM_YAL_REGS);
    116	if (ret < 0) {
    117		dev_err(dev, "rtc_read_alarm error %d\n", ret);
    118		return ret;
    119	}
    120
    121	alm->time.tm_sec = 0;
    122	alm->time.tm_min = bcd2bin(alarm_data[0]);
    123	alm->time.tm_hour = bcd2bin(alarm_data[1]);
    124	alm->time.tm_mday = bcd2bin(alarm_data[2]);
    125	alm->time.tm_mon = bcd2bin(alarm_data[3]) - 1;
    126	alm->time.tm_year = bcd2bin(alarm_data[4]) + 100;
    127
    128	ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL1, &interrupt_enable);
    129	if (ret < 0)
    130		return ret;
    131
    132	/* check if YALE is set */
    133	if (interrupt_enable & SET_YAL)
    134		alm->enabled = 1;
    135
    136	return ret;
    137}
    138
    139static int rc5t583_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
    140{
    141	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
    142	unsigned char alarm_data[NUM_YAL_REGS];
    143	int ret;
    144
    145	ret = rc5t583_rtc_alarm_irq_enable(dev, 0);
    146	if (ret)
    147		return ret;
    148
    149	alarm_data[0] = bin2bcd(alm->time.tm_min);
    150	alarm_data[1] = bin2bcd(alm->time.tm_hour);
    151	alarm_data[2] = bin2bcd(alm->time.tm_mday);
    152	alarm_data[3] = bin2bcd(alm->time.tm_mon + 1);
    153	alarm_data[4] = bin2bcd(alm->time.tm_year - 100);
    154
    155	ret = regmap_bulk_write(rc5t583->regmap, RC5T583_RTC_AY_MIN, alarm_data,
    156		NUM_YAL_REGS);
    157	if (ret) {
    158		dev_err(dev, "rtc_set_alarm error %d\n", ret);
    159		return ret;
    160	}
    161
    162	if (alm->enabled)
    163		ret = rc5t583_rtc_alarm_irq_enable(dev, 1);
    164
    165	return ret;
    166}
    167
    168static irqreturn_t rc5t583_rtc_interrupt(int irq, void *rtc)
    169{
    170	struct device *dev = rtc;
    171	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
    172	struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev);
    173	unsigned long events = 0;
    174	int ret;
    175	u32 rtc_reg;
    176
    177	ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL2, &rtc_reg);
    178	if (ret < 0)
    179		return IRQ_NONE;
    180
    181	if (rtc_reg & GET_YAL_STATUS) {
    182		events = RTC_IRQF | RTC_AF;
    183		/* clear pending Y-alarm interrupt bit */
    184		rtc_reg &= ~GET_YAL_STATUS;
    185	}
    186
    187	ret = regmap_write(rc5t583->regmap, RC5T583_RTC_CTL2, rtc_reg);
    188	if (ret)
    189		return IRQ_NONE;
    190
    191	/* Notify RTC core on event */
    192	rtc_update_irq(rc5t583_rtc->rtc, 1, events);
    193
    194	return IRQ_HANDLED;
    195}
    196
    197static const struct rtc_class_ops rc5t583_rtc_ops = {
    198	.read_time	= rc5t583_rtc_read_time,
    199	.set_time	= rc5t583_rtc_set_time,
    200	.read_alarm	= rc5t583_rtc_read_alarm,
    201	.set_alarm	= rc5t583_rtc_set_alarm,
    202	.alarm_irq_enable = rc5t583_rtc_alarm_irq_enable,
    203};
    204
    205static int rc5t583_rtc_probe(struct platform_device *pdev)
    206{
    207	struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent);
    208	struct rc5t583_rtc *ricoh_rtc;
    209	struct rc5t583_platform_data *pmic_plat_data;
    210	int ret;
    211	int irq;
    212
    213	ricoh_rtc = devm_kzalloc(&pdev->dev, sizeof(struct rc5t583_rtc),
    214			GFP_KERNEL);
    215	if (!ricoh_rtc)
    216		return -ENOMEM;
    217
    218	platform_set_drvdata(pdev, ricoh_rtc);
    219
    220	/* Clear pending interrupts */
    221	ret = regmap_write(rc5t583->regmap, RC5T583_RTC_CTL2, 0);
    222	if (ret < 0)
    223		return ret;
    224
    225	/* clear RTC Adjust register */
    226	ret = regmap_write(rc5t583->regmap, RC5T583_RTC_ADJ, 0);
    227	if (ret < 0) {
    228		dev_err(&pdev->dev, "unable to program rtc_adjust reg\n");
    229		return -EBUSY;
    230	}
    231
    232	pmic_plat_data = dev_get_platdata(rc5t583->dev);
    233	irq = pmic_plat_data->irq_base;
    234	if (irq <= 0) {
    235		dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n",
    236			irq);
    237		return ret;
    238	}
    239
    240	irq += RC5T583_IRQ_YALE;
    241	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
    242		rc5t583_rtc_interrupt, IRQF_TRIGGER_LOW,
    243		"rtc-rc5t583", &pdev->dev);
    244	if (ret < 0) {
    245		dev_err(&pdev->dev, "IRQ is not free.\n");
    246		return ret;
    247	}
    248	device_init_wakeup(&pdev->dev, 1);
    249
    250	ricoh_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
    251		&rc5t583_rtc_ops, THIS_MODULE);
    252	if (IS_ERR(ricoh_rtc->rtc)) {
    253		ret = PTR_ERR(ricoh_rtc->rtc);
    254		dev_err(&pdev->dev, "RTC device register: err %d\n", ret);
    255		return ret;
    256	}
    257
    258	return 0;
    259}
    260
    261/*
    262 * Disable rc5t583 RTC interrupts.
    263 * Sets status flag to free.
    264 */
    265static int rc5t583_rtc_remove(struct platform_device *pdev)
    266{
    267	struct rc5t583_rtc *rc5t583_rtc = platform_get_drvdata(pdev);
    268
    269	rc5t583_rtc_alarm_irq_enable(&rc5t583_rtc->rtc->dev, 0);
    270	return 0;
    271}
    272
    273#ifdef CONFIG_PM_SLEEP
    274static int rc5t583_rtc_suspend(struct device *dev)
    275{
    276	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
    277	struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev);
    278	int ret;
    279
    280	/* Store current list of enabled interrupts*/
    281	ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL1,
    282		&rc5t583_rtc->irqen);
    283	return ret;
    284}
    285
    286static int rc5t583_rtc_resume(struct device *dev)
    287{
    288	struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
    289	struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev);
    290
    291	/* Restore list of enabled interrupts before suspend */
    292	return regmap_write(rc5t583->regmap, RC5T583_RTC_CTL1,
    293		rc5t583_rtc->irqen);
    294}
    295#endif
    296
    297static SIMPLE_DEV_PM_OPS(rc5t583_rtc_pm_ops, rc5t583_rtc_suspend,
    298			rc5t583_rtc_resume);
    299
    300static struct platform_driver rc5t583_rtc_driver = {
    301	.probe		= rc5t583_rtc_probe,
    302	.remove		= rc5t583_rtc_remove,
    303	.driver		= {
    304		.name	= "rtc-rc5t583",
    305		.pm	= &rc5t583_rtc_pm_ops,
    306	},
    307};
    308
    309module_platform_driver(rc5t583_rtc_driver);
    310MODULE_ALIAS("platform:rtc-rc5t583");
    311MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>");
    312MODULE_LICENSE("GPL v2");