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-max8925.c (8207B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * RTC driver for Maxim MAX8925
      4 *
      5 * Copyright (C) 2009-2010 Marvell International Ltd.
      6 *	Haojian Zhuang <haojian.zhuang@marvell.com>
      7 */
      8
      9#include <linux/module.h>
     10#include <linux/i2c.h>
     11#include <linux/slab.h>
     12#include <linux/rtc.h>
     13#include <linux/platform_device.h>
     14#include <linux/mfd/max8925.h>
     15
     16enum {
     17	RTC_SEC = 0,
     18	RTC_MIN,
     19	RTC_HOUR,
     20	RTC_WEEKDAY,
     21	RTC_DATE,
     22	RTC_MONTH,
     23	RTC_YEAR1,
     24	RTC_YEAR2,
     25};
     26
     27#define MAX8925_RTC_SEC			0x00
     28#define MAX8925_RTC_MIN			0x01
     29#define MAX8925_RTC_HOUR		0x02
     30#define MAX8925_RTC_WEEKDAY		0x03
     31#define MAX8925_RTC_DATE		0x04
     32#define MAX8925_RTC_MONTH		0x05
     33#define MAX8925_RTC_YEAR1		0x06
     34#define MAX8925_RTC_YEAR2		0x07
     35#define MAX8925_ALARM0_SEC		0x08
     36#define MAX8925_ALARM0_MIN		0x09
     37#define MAX8925_ALARM0_HOUR		0x0a
     38#define MAX8925_ALARM0_WEEKDAY		0x0b
     39#define MAX8925_ALARM0_DATE		0x0c
     40#define MAX8925_ALARM0_MON		0x0d
     41#define MAX8925_ALARM0_YEAR1		0x0e
     42#define MAX8925_ALARM0_YEAR2		0x0f
     43#define MAX8925_ALARM1_SEC		0x10
     44#define MAX8925_ALARM1_MIN		0x11
     45#define MAX8925_ALARM1_HOUR		0x12
     46#define MAX8925_ALARM1_WEEKDAY		0x13
     47#define MAX8925_ALARM1_DATE		0x14
     48#define MAX8925_ALARM1_MON		0x15
     49#define MAX8925_ALARM1_YEAR1		0x16
     50#define MAX8925_ALARM1_YEAR2		0x17
     51#define MAX8925_RTC_CNTL		0x1b
     52#define MAX8925_RTC_STATUS		0x20
     53
     54#define TIME_NUM			8
     55#define ALARM_1SEC			(1 << 7)
     56#define HOUR_12				(1 << 7)
     57#define HOUR_AM_PM			(1 << 5)
     58#define ALARM0_IRQ			(1 << 3)
     59#define ALARM1_IRQ			(1 << 2)
     60#define ALARM0_STATUS			(1 << 2)
     61#define ALARM1_STATUS			(1 << 1)
     62
     63
     64struct max8925_rtc_info {
     65	struct rtc_device	*rtc_dev;
     66	struct max8925_chip	*chip;
     67	struct i2c_client	*rtc;
     68	struct device		*dev;
     69	int			irq;
     70};
     71
     72static irqreturn_t rtc_update_handler(int irq, void *data)
     73{
     74	struct max8925_rtc_info *info = (struct max8925_rtc_info *)data;
     75
     76	/* disable ALARM0 except for 1SEC alarm */
     77	max8925_set_bits(info->rtc, MAX8925_ALARM0_CNTL, 0x7f, 0);
     78	rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
     79	return IRQ_HANDLED;
     80}
     81
     82static int tm_calc(struct rtc_time *tm, unsigned char *buf, int len)
     83{
     84	if (len < TIME_NUM)
     85		return -EINVAL;
     86	tm->tm_year = (buf[RTC_YEAR2] >> 4) * 1000
     87			+ (buf[RTC_YEAR2] & 0xf) * 100
     88			+ (buf[RTC_YEAR1] >> 4) * 10
     89			+ (buf[RTC_YEAR1] & 0xf);
     90	tm->tm_year -= 1900;
     91	tm->tm_mon = ((buf[RTC_MONTH] >> 4) & 0x01) * 10
     92			+ (buf[RTC_MONTH] & 0x0f);
     93	tm->tm_mday = ((buf[RTC_DATE] >> 4) & 0x03) * 10
     94			+ (buf[RTC_DATE] & 0x0f);
     95	tm->tm_wday = buf[RTC_WEEKDAY] & 0x07;
     96	if (buf[RTC_HOUR] & HOUR_12) {
     97		tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x1) * 10
     98				+ (buf[RTC_HOUR] & 0x0f);
     99		if (buf[RTC_HOUR] & HOUR_AM_PM)
    100			tm->tm_hour += 12;
    101	} else
    102		tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x03) * 10
    103				+ (buf[RTC_HOUR] & 0x0f);
    104	tm->tm_min = ((buf[RTC_MIN] >> 4) & 0x7) * 10
    105			+ (buf[RTC_MIN] & 0x0f);
    106	tm->tm_sec = ((buf[RTC_SEC] >> 4) & 0x7) * 10
    107			+ (buf[RTC_SEC] & 0x0f);
    108	return 0;
    109}
    110
    111static int data_calc(unsigned char *buf, struct rtc_time *tm, int len)
    112{
    113	unsigned char high, low;
    114
    115	if (len < TIME_NUM)
    116		return -EINVAL;
    117
    118	high = (tm->tm_year + 1900) / 1000;
    119	low = (tm->tm_year + 1900) / 100;
    120	low = low - high * 10;
    121	buf[RTC_YEAR2] = (high << 4) + low;
    122	high = (tm->tm_year + 1900) / 10;
    123	low = tm->tm_year + 1900;
    124	low = low - high * 10;
    125	high = high - (high / 10) * 10;
    126	buf[RTC_YEAR1] = (high << 4) + low;
    127	high = tm->tm_mon / 10;
    128	low = tm->tm_mon;
    129	low = low - high * 10;
    130	buf[RTC_MONTH] = (high << 4) + low;
    131	high = tm->tm_mday / 10;
    132	low = tm->tm_mday;
    133	low = low - high * 10;
    134	buf[RTC_DATE] = (high << 4) + low;
    135	buf[RTC_WEEKDAY] = tm->tm_wday;
    136	high = tm->tm_hour / 10;
    137	low = tm->tm_hour;
    138	low = low - high * 10;
    139	buf[RTC_HOUR] = (high << 4) + low;
    140	high = tm->tm_min / 10;
    141	low = tm->tm_min;
    142	low = low - high * 10;
    143	buf[RTC_MIN] = (high << 4) + low;
    144	high = tm->tm_sec / 10;
    145	low = tm->tm_sec;
    146	low = low - high * 10;
    147	buf[RTC_SEC] = (high << 4) + low;
    148	return 0;
    149}
    150
    151static int max8925_rtc_read_time(struct device *dev, struct rtc_time *tm)
    152{
    153	struct max8925_rtc_info *info = dev_get_drvdata(dev);
    154	unsigned char buf[TIME_NUM];
    155	int ret;
    156
    157	ret = max8925_bulk_read(info->rtc, MAX8925_RTC_SEC, TIME_NUM, buf);
    158	if (ret < 0)
    159		goto out;
    160	ret = tm_calc(tm, buf, TIME_NUM);
    161out:
    162	return ret;
    163}
    164
    165static int max8925_rtc_set_time(struct device *dev, struct rtc_time *tm)
    166{
    167	struct max8925_rtc_info *info = dev_get_drvdata(dev);
    168	unsigned char buf[TIME_NUM];
    169	int ret;
    170
    171	ret = data_calc(buf, tm, TIME_NUM);
    172	if (ret < 0)
    173		goto out;
    174	ret = max8925_bulk_write(info->rtc, MAX8925_RTC_SEC, TIME_NUM, buf);
    175out:
    176	return ret;
    177}
    178
    179static int max8925_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
    180{
    181	struct max8925_rtc_info *info = dev_get_drvdata(dev);
    182	unsigned char buf[TIME_NUM];
    183	int ret;
    184
    185	ret = max8925_bulk_read(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf);
    186	if (ret < 0)
    187		goto out;
    188	ret = tm_calc(&alrm->time, buf, TIME_NUM);
    189	if (ret < 0)
    190		goto out;
    191	ret = max8925_reg_read(info->rtc, MAX8925_RTC_IRQ_MASK);
    192	if (ret < 0)
    193		goto out;
    194	if (ret & ALARM0_IRQ) {
    195		alrm->enabled = 0;
    196	} else {
    197		ret = max8925_reg_read(info->rtc, MAX8925_ALARM0_CNTL);
    198		if (ret < 0)
    199			goto out;
    200		if (!ret)
    201			alrm->enabled = 0;
    202		else
    203			alrm->enabled = 1;
    204	}
    205	ret = max8925_reg_read(info->rtc, MAX8925_RTC_STATUS);
    206	if (ret < 0)
    207		goto out;
    208	if (ret & ALARM0_STATUS)
    209		alrm->pending = 1;
    210	else
    211		alrm->pending = 0;
    212	return 0;
    213out:
    214	return ret;
    215}
    216
    217static int max8925_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
    218{
    219	struct max8925_rtc_info *info = dev_get_drvdata(dev);
    220	unsigned char buf[TIME_NUM];
    221	int ret;
    222
    223	ret = data_calc(buf, &alrm->time, TIME_NUM);
    224	if (ret < 0)
    225		goto out;
    226	ret = max8925_bulk_write(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf);
    227	if (ret < 0)
    228		goto out;
    229	if (alrm->enabled)
    230		/* only enable alarm on year/month/day/hour/min/sec */
    231		ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x77);
    232	else
    233		ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x0);
    234out:
    235	return ret;
    236}
    237
    238static const struct rtc_class_ops max8925_rtc_ops = {
    239	.read_time	= max8925_rtc_read_time,
    240	.set_time	= max8925_rtc_set_time,
    241	.read_alarm	= max8925_rtc_read_alarm,
    242	.set_alarm	= max8925_rtc_set_alarm,
    243};
    244
    245static int max8925_rtc_probe(struct platform_device *pdev)
    246{
    247	struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
    248	struct max8925_rtc_info *info;
    249	int ret;
    250
    251	info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_rtc_info),
    252			    GFP_KERNEL);
    253	if (!info)
    254		return -ENOMEM;
    255	info->chip = chip;
    256	info->rtc = chip->rtc;
    257	info->dev = &pdev->dev;
    258	info->irq = platform_get_irq(pdev, 0);
    259
    260	ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
    261					rtc_update_handler, IRQF_ONESHOT,
    262					"rtc-alarm0", info);
    263	if (ret < 0) {
    264		dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
    265			info->irq, ret);
    266		return ret;
    267	}
    268
    269	dev_set_drvdata(&pdev->dev, info);
    270	/* XXX - isn't this redundant? */
    271	platform_set_drvdata(pdev, info);
    272
    273	device_init_wakeup(&pdev->dev, 1);
    274
    275	info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max8925-rtc",
    276					&max8925_rtc_ops, THIS_MODULE);
    277	ret = PTR_ERR(info->rtc_dev);
    278	if (IS_ERR(info->rtc_dev)) {
    279		dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
    280		return ret;
    281	}
    282
    283	return 0;
    284}
    285
    286#ifdef CONFIG_PM_SLEEP
    287static int max8925_rtc_suspend(struct device *dev)
    288{
    289	struct platform_device *pdev = to_platform_device(dev);
    290	struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
    291
    292	if (device_may_wakeup(dev))
    293		chip->wakeup_flag |= 1 << MAX8925_IRQ_RTC_ALARM0;
    294	return 0;
    295}
    296static int max8925_rtc_resume(struct device *dev)
    297{
    298	struct platform_device *pdev = to_platform_device(dev);
    299	struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
    300
    301	if (device_may_wakeup(dev))
    302		chip->wakeup_flag &= ~(1 << MAX8925_IRQ_RTC_ALARM0);
    303	return 0;
    304}
    305#endif
    306
    307static SIMPLE_DEV_PM_OPS(max8925_rtc_pm_ops, max8925_rtc_suspend, max8925_rtc_resume);
    308
    309static struct platform_driver max8925_rtc_driver = {
    310	.driver		= {
    311		.name	= "max8925-rtc",
    312		.pm     = &max8925_rtc_pm_ops,
    313	},
    314	.probe		= max8925_rtc_probe,
    315};
    316
    317module_platform_driver(max8925_rtc_driver);
    318
    319MODULE_DESCRIPTION("Maxim MAX8925 RTC driver");
    320MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
    321MODULE_LICENSE("GPL");
    322