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-da9052.c (7863B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Real time clock driver for DA9052
      4 *
      5 * Copyright(c) 2012 Dialog Semiconductor Ltd.
      6 *
      7 * Author: Dajun Dajun Chen <dajun.chen@diasemi.com>
      8 */
      9
     10#include <linux/module.h>
     11#include <linux/platform_device.h>
     12#include <linux/rtc.h>
     13#include <linux/err.h>
     14#include <linux/delay.h>
     15
     16#include <linux/mfd/da9052/da9052.h>
     17#include <linux/mfd/da9052/reg.h>
     18
     19#define rtc_err(rtc, fmt, ...) \
     20		dev_err(rtc->da9052->dev, "%s: " fmt, __func__, ##__VA_ARGS__)
     21
     22#define DA9052_GET_TIME_RETRIES 5
     23
     24struct da9052_rtc {
     25	struct rtc_device *rtc;
     26	struct da9052 *da9052;
     27};
     28
     29static int da9052_rtc_enable_alarm(struct da9052_rtc *rtc, bool enable)
     30{
     31	int ret;
     32	if (enable) {
     33		ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG,
     34				DA9052_ALARM_Y_ALARM_ON|DA9052_ALARM_Y_TICK_ON,
     35				DA9052_ALARM_Y_ALARM_ON);
     36		if (ret != 0)
     37			rtc_err(rtc, "Failed to enable ALM: %d\n", ret);
     38	} else {
     39		ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG,
     40			DA9052_ALARM_Y_ALARM_ON|DA9052_ALARM_Y_TICK_ON, 0);
     41		if (ret != 0)
     42			rtc_err(rtc, "Write error: %d\n", ret);
     43	}
     44	return ret;
     45}
     46
     47static irqreturn_t da9052_rtc_irq(int irq, void *data)
     48{
     49	struct da9052_rtc *rtc = data;
     50
     51	rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
     52
     53	return IRQ_HANDLED;
     54}
     55
     56static int da9052_read_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm)
     57{
     58	int ret;
     59	uint8_t v[2][5];
     60	int idx = 1;
     61	int timeout = DA9052_GET_TIME_RETRIES;
     62
     63	ret = da9052_group_read(rtc->da9052, DA9052_ALARM_MI_REG, 5, &v[0][0]);
     64	if (ret) {
     65		rtc_err(rtc, "Failed to group read ALM: %d\n", ret);
     66		return ret;
     67	}
     68
     69	do {
     70		ret = da9052_group_read(rtc->da9052,
     71					DA9052_ALARM_MI_REG, 5, &v[idx][0]);
     72		if (ret) {
     73			rtc_err(rtc, "Failed to group read ALM: %d\n", ret);
     74			return ret;
     75		}
     76
     77		if (memcmp(&v[0][0], &v[1][0], 5) == 0) {
     78			rtc_tm->tm_year = (v[0][4] & DA9052_RTC_YEAR) + 100;
     79			rtc_tm->tm_mon  = (v[0][3] & DA9052_RTC_MONTH) - 1;
     80			rtc_tm->tm_mday = v[0][2] & DA9052_RTC_DAY;
     81			rtc_tm->tm_hour = v[0][1] & DA9052_RTC_HOUR;
     82			rtc_tm->tm_min  = v[0][0] & DA9052_RTC_MIN;
     83			rtc_tm->tm_sec = 0;
     84
     85			ret = rtc_valid_tm(rtc_tm);
     86			return ret;
     87		}
     88
     89		idx = (1-idx);
     90		msleep(20);
     91
     92	} while (timeout--);
     93
     94	rtc_err(rtc, "Timed out reading alarm time\n");
     95
     96	return -EIO;
     97}
     98
     99static int da9052_set_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm)
    100{
    101	struct da9052 *da9052 = rtc->da9052;
    102	unsigned long alm_time;
    103	int ret;
    104	uint8_t v[3];
    105
    106	alm_time = rtc_tm_to_time64(rtc_tm);
    107
    108	if (rtc_tm->tm_sec > 0) {
    109		alm_time += 60 - rtc_tm->tm_sec;
    110		rtc_time64_to_tm(alm_time, rtc_tm);
    111	}
    112	BUG_ON(rtc_tm->tm_sec); /* it will cause repeated irqs if not zero */
    113
    114	rtc_tm->tm_year -= 100;
    115	rtc_tm->tm_mon += 1;
    116
    117	ret = da9052_reg_update(da9052, DA9052_ALARM_MI_REG,
    118				DA9052_RTC_MIN, rtc_tm->tm_min);
    119	if (ret != 0) {
    120		rtc_err(rtc, "Failed to write ALRM MIN: %d\n", ret);
    121		return ret;
    122	}
    123
    124	v[0] = rtc_tm->tm_hour;
    125	v[1] = rtc_tm->tm_mday;
    126	v[2] = rtc_tm->tm_mon;
    127
    128	ret = da9052_group_write(da9052, DA9052_ALARM_H_REG, 3, v);
    129	if (ret < 0)
    130		return ret;
    131
    132	ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG,
    133				DA9052_RTC_YEAR, rtc_tm->tm_year);
    134	if (ret != 0)
    135		rtc_err(rtc, "Failed to write ALRM YEAR: %d\n", ret);
    136
    137	return ret;
    138}
    139
    140static int da9052_rtc_get_alarm_status(struct da9052_rtc *rtc)
    141{
    142	int ret;
    143
    144	ret = da9052_reg_read(rtc->da9052, DA9052_ALARM_Y_REG);
    145	if (ret < 0) {
    146		rtc_err(rtc, "Failed to read ALM: %d\n", ret);
    147		return ret;
    148	}
    149
    150	return !!(ret&DA9052_ALARM_Y_ALARM_ON);
    151}
    152
    153static int da9052_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm)
    154{
    155	struct da9052_rtc *rtc = dev_get_drvdata(dev);
    156	int ret;
    157	uint8_t v[2][6];
    158	int idx = 1;
    159	int timeout = DA9052_GET_TIME_RETRIES;
    160
    161	ret = da9052_group_read(rtc->da9052, DA9052_COUNT_S_REG, 6, &v[0][0]);
    162	if (ret) {
    163		rtc_err(rtc, "Failed to read RTC time : %d\n", ret);
    164		return ret;
    165	}
    166
    167	do {
    168		ret = da9052_group_read(rtc->da9052,
    169					DA9052_COUNT_S_REG, 6, &v[idx][0]);
    170		if (ret) {
    171			rtc_err(rtc, "Failed to read RTC time : %d\n", ret);
    172			return ret;
    173		}
    174
    175		if (memcmp(&v[0][0], &v[1][0], 6) == 0) {
    176			rtc_tm->tm_year = (v[0][5] & DA9052_RTC_YEAR) + 100;
    177			rtc_tm->tm_mon  = (v[0][4] & DA9052_RTC_MONTH) - 1;
    178			rtc_tm->tm_mday = v[0][3] & DA9052_RTC_DAY;
    179			rtc_tm->tm_hour = v[0][2] & DA9052_RTC_HOUR;
    180			rtc_tm->tm_min  = v[0][1] & DA9052_RTC_MIN;
    181			rtc_tm->tm_sec  = v[0][0] & DA9052_RTC_SEC;
    182
    183			return 0;
    184		}
    185
    186		idx = (1-idx);
    187		msleep(20);
    188
    189	} while (timeout--);
    190
    191	rtc_err(rtc, "Timed out reading time\n");
    192
    193	return -EIO;
    194}
    195
    196static int da9052_rtc_set_time(struct device *dev, struct rtc_time *tm)
    197{
    198	struct da9052_rtc *rtc;
    199	uint8_t v[6];
    200	int ret;
    201
    202	/* DA9052 only has 6 bits for year - to represent 2000-2063 */
    203	if ((tm->tm_year < 100) || (tm->tm_year > 163))
    204		return -EINVAL;
    205
    206	rtc = dev_get_drvdata(dev);
    207
    208	v[0] = tm->tm_sec;
    209	v[1] = tm->tm_min;
    210	v[2] = tm->tm_hour;
    211	v[3] = tm->tm_mday;
    212	v[4] = tm->tm_mon + 1;
    213	v[5] = tm->tm_year - 100;
    214
    215	ret = da9052_group_write(rtc->da9052, DA9052_COUNT_S_REG, 6, v);
    216	if (ret < 0)
    217		rtc_err(rtc, "failed to set RTC time: %d\n", ret);
    218	return ret;
    219}
    220
    221static int da9052_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
    222{
    223	int ret;
    224	struct rtc_time *tm = &alrm->time;
    225	struct da9052_rtc *rtc = dev_get_drvdata(dev);
    226
    227	ret = da9052_read_alarm(rtc, tm);
    228	if (ret < 0) {
    229		rtc_err(rtc, "failed to read RTC alarm: %d\n", ret);
    230		return ret;
    231	}
    232
    233	alrm->enabled = da9052_rtc_get_alarm_status(rtc);
    234	return 0;
    235}
    236
    237static int da9052_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
    238{
    239	int ret;
    240	struct rtc_time *tm = &alrm->time;
    241	struct da9052_rtc *rtc = dev_get_drvdata(dev);
    242
    243	/* DA9052 only has 6 bits for year - to represent 2000-2063 */
    244	if ((tm->tm_year < 100) || (tm->tm_year > 163))
    245		return -EINVAL;
    246
    247	ret = da9052_rtc_enable_alarm(rtc, 0);
    248	if (ret < 0)
    249		return ret;
    250
    251	ret = da9052_set_alarm(rtc, tm);
    252	if (ret < 0)
    253		return ret;
    254
    255	ret = da9052_rtc_enable_alarm(rtc, 1);
    256	return ret;
    257}
    258
    259static int da9052_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
    260{
    261	struct da9052_rtc *rtc = dev_get_drvdata(dev);
    262
    263	return da9052_rtc_enable_alarm(rtc, enabled);
    264}
    265
    266static const struct rtc_class_ops da9052_rtc_ops = {
    267	.read_time	= da9052_rtc_read_time,
    268	.set_time	= da9052_rtc_set_time,
    269	.read_alarm	= da9052_rtc_read_alarm,
    270	.set_alarm	= da9052_rtc_set_alarm,
    271	.alarm_irq_enable = da9052_rtc_alarm_irq_enable,
    272};
    273
    274static int da9052_rtc_probe(struct platform_device *pdev)
    275{
    276	struct da9052_rtc *rtc;
    277	int ret;
    278
    279	rtc = devm_kzalloc(&pdev->dev, sizeof(struct da9052_rtc), GFP_KERNEL);
    280	if (!rtc)
    281		return -ENOMEM;
    282
    283	rtc->da9052 = dev_get_drvdata(pdev->dev.parent);
    284	platform_set_drvdata(pdev, rtc);
    285
    286	ret = da9052_reg_write(rtc->da9052, DA9052_BBAT_CONT_REG, 0xFE);
    287	if (ret < 0) {
    288		rtc_err(rtc,
    289			"Failed to setup RTC battery charging: %d\n", ret);
    290		return ret;
    291	}
    292
    293	ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG,
    294				DA9052_ALARM_Y_TICK_ON, 0);
    295	if (ret != 0)
    296		rtc_err(rtc, "Failed to disable TICKS: %d\n", ret);
    297
    298	device_init_wakeup(&pdev->dev, true);
    299	rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
    300	if (IS_ERR(rtc->rtc))
    301		return PTR_ERR(rtc->rtc);
    302
    303	rtc->rtc->ops = &da9052_rtc_ops;
    304	rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
    305	rtc->rtc->range_max = RTC_TIMESTAMP_END_2063;
    306
    307	ret = devm_rtc_register_device(rtc->rtc);
    308	if (ret)
    309		return ret;
    310
    311	ret = da9052_request_irq(rtc->da9052, DA9052_IRQ_ALARM, "ALM",
    312				da9052_rtc_irq, rtc);
    313	if (ret != 0) {
    314		rtc_err(rtc, "irq registration failed: %d\n", ret);
    315		return ret;
    316	}
    317
    318	return 0;
    319}
    320
    321static struct platform_driver da9052_rtc_driver = {
    322	.probe	= da9052_rtc_probe,
    323	.driver = {
    324		.name	= "da9052-rtc",
    325	},
    326};
    327
    328module_platform_driver(da9052_rtc_driver);
    329
    330MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>");
    331MODULE_DESCRIPTION("RTC driver for Dialog DA9052 PMIC");
    332MODULE_LICENSE("GPL");
    333MODULE_ALIAS("platform:da9052-rtc");