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-da9055.c (9307B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Real time clock driver for DA9055
      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
     14#include <linux/mfd/da9055/core.h>
     15#include <linux/mfd/da9055/reg.h>
     16#include <linux/mfd/da9055/pdata.h>
     17
     18struct da9055_rtc {
     19	struct rtc_device *rtc;
     20	struct da9055 *da9055;
     21	int alarm_enable;
     22};
     23
     24static int da9055_rtc_enable_alarm(struct da9055_rtc *rtc, bool enable)
     25{
     26	int ret;
     27	if (enable) {
     28		ret = da9055_reg_update(rtc->da9055, DA9055_REG_ALARM_Y,
     29					DA9055_RTC_ALM_EN,
     30					DA9055_RTC_ALM_EN);
     31		if (ret != 0)
     32			dev_err(rtc->da9055->dev, "Failed to enable ALM: %d\n",
     33				ret);
     34		rtc->alarm_enable = 1;
     35	} else {
     36		ret = da9055_reg_update(rtc->da9055, DA9055_REG_ALARM_Y,
     37					DA9055_RTC_ALM_EN, 0);
     38		if (ret != 0)
     39			dev_err(rtc->da9055->dev,
     40				"Failed to disable ALM: %d\n", ret);
     41		rtc->alarm_enable = 0;
     42	}
     43	return ret;
     44}
     45
     46static irqreturn_t da9055_rtc_alm_irq(int irq, void *data)
     47{
     48	struct da9055_rtc *rtc = data;
     49
     50	da9055_rtc_enable_alarm(rtc, 0);
     51	rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
     52
     53	return IRQ_HANDLED;
     54}
     55
     56static int da9055_read_alarm(struct da9055 *da9055, struct rtc_time *rtc_tm)
     57{
     58	int ret;
     59	uint8_t v[5];
     60
     61	ret = da9055_group_read(da9055, DA9055_REG_ALARM_MI, 5, v);
     62	if (ret != 0) {
     63		dev_err(da9055->dev, "Failed to group read ALM: %d\n", ret);
     64		return ret;
     65	}
     66
     67	rtc_tm->tm_year = (v[4] & DA9055_RTC_ALM_YEAR) + 100;
     68	rtc_tm->tm_mon  = (v[3] & DA9055_RTC_ALM_MONTH) - 1;
     69	rtc_tm->tm_mday = v[2] & DA9055_RTC_ALM_DAY;
     70	rtc_tm->tm_hour = v[1] & DA9055_RTC_ALM_HOUR;
     71	rtc_tm->tm_min  = v[0] & DA9055_RTC_ALM_MIN;
     72	rtc_tm->tm_sec = 0;
     73
     74	return rtc_valid_tm(rtc_tm);
     75}
     76
     77static int da9055_set_alarm(struct da9055 *da9055, struct rtc_time *rtc_tm)
     78{
     79	int ret;
     80	uint8_t v[2];
     81
     82	rtc_tm->tm_year -= 100;
     83	rtc_tm->tm_mon += 1;
     84
     85	ret = da9055_reg_update(da9055, DA9055_REG_ALARM_MI,
     86				DA9055_RTC_ALM_MIN, rtc_tm->tm_min);
     87	if (ret != 0) {
     88		dev_err(da9055->dev, "Failed to write ALRM MIN: %d\n", ret);
     89		return ret;
     90	}
     91
     92	v[0] = rtc_tm->tm_hour;
     93	v[1] = rtc_tm->tm_mday;
     94
     95	ret = da9055_group_write(da9055, DA9055_REG_ALARM_H, 2, v);
     96	if (ret < 0)
     97		return ret;
     98
     99	ret = da9055_reg_update(da9055, DA9055_REG_ALARM_MO,
    100				DA9055_RTC_ALM_MONTH, rtc_tm->tm_mon);
    101	if (ret < 0)
    102		dev_err(da9055->dev, "Failed to write ALM Month:%d\n", ret);
    103
    104	ret = da9055_reg_update(da9055, DA9055_REG_ALARM_Y,
    105				DA9055_RTC_ALM_YEAR, rtc_tm->tm_year);
    106	if (ret < 0)
    107		dev_err(da9055->dev, "Failed to write ALM Year:%d\n", ret);
    108
    109	return ret;
    110}
    111
    112static int da9055_rtc_get_alarm_status(struct da9055 *da9055)
    113{
    114	int ret;
    115
    116	ret = da9055_reg_read(da9055, DA9055_REG_ALARM_Y);
    117	if (ret < 0) {
    118		dev_err(da9055->dev, "Failed to read ALM: %d\n", ret);
    119		return ret;
    120	}
    121	ret &= DA9055_RTC_ALM_EN;
    122	return (ret > 0) ? 1 : 0;
    123}
    124
    125static int da9055_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm)
    126{
    127	struct da9055_rtc *rtc = dev_get_drvdata(dev);
    128	uint8_t v[6];
    129	int ret;
    130
    131	ret = da9055_reg_read(rtc->da9055, DA9055_REG_COUNT_S);
    132	if (ret < 0)
    133		return ret;
    134
    135	/*
    136	 * Registers are only valid when RTC_READ
    137	 * status bit is asserted
    138	 */
    139	if (!(ret & DA9055_RTC_READ))
    140		return -EBUSY;
    141
    142	ret = da9055_group_read(rtc->da9055, DA9055_REG_COUNT_S, 6, v);
    143	if (ret < 0) {
    144		dev_err(rtc->da9055->dev, "Failed to read RTC time : %d\n",
    145			ret);
    146		return ret;
    147	}
    148
    149	rtc_tm->tm_year = (v[5] & DA9055_RTC_YEAR) + 100;
    150	rtc_tm->tm_mon  = (v[4] & DA9055_RTC_MONTH) - 1;
    151	rtc_tm->tm_mday = v[3] & DA9055_RTC_DAY;
    152	rtc_tm->tm_hour = v[2] & DA9055_RTC_HOUR;
    153	rtc_tm->tm_min  = v[1] & DA9055_RTC_MIN;
    154	rtc_tm->tm_sec  = v[0] & DA9055_RTC_SEC;
    155
    156	return 0;
    157}
    158
    159static int da9055_rtc_set_time(struct device *dev, struct rtc_time *tm)
    160{
    161	struct da9055_rtc *rtc;
    162	uint8_t v[6];
    163
    164	rtc = dev_get_drvdata(dev);
    165
    166	v[0] = tm->tm_sec;
    167	v[1] = tm->tm_min;
    168	v[2] = tm->tm_hour;
    169	v[3] = tm->tm_mday;
    170	v[4] = tm->tm_mon + 1;
    171	v[5] = tm->tm_year - 100;
    172
    173	return da9055_group_write(rtc->da9055, DA9055_REG_COUNT_S, 6, v);
    174}
    175
    176static int da9055_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
    177{
    178	int ret;
    179	struct rtc_time *tm = &alrm->time;
    180	struct da9055_rtc *rtc = dev_get_drvdata(dev);
    181
    182	ret = da9055_read_alarm(rtc->da9055, tm);
    183
    184	if (ret)
    185		return ret;
    186
    187	alrm->enabled = da9055_rtc_get_alarm_status(rtc->da9055);
    188
    189	return 0;
    190}
    191
    192static int da9055_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
    193{
    194	int ret;
    195	struct rtc_time *tm = &alrm->time;
    196	struct da9055_rtc *rtc = dev_get_drvdata(dev);
    197
    198	ret = da9055_rtc_enable_alarm(rtc, 0);
    199	if (ret < 0)
    200		return ret;
    201
    202	ret = da9055_set_alarm(rtc->da9055, tm);
    203	if (ret)
    204		return ret;
    205
    206	ret = da9055_rtc_enable_alarm(rtc, 1);
    207
    208	return ret;
    209}
    210
    211static int da9055_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
    212{
    213	struct da9055_rtc *rtc = dev_get_drvdata(dev);
    214
    215	return da9055_rtc_enable_alarm(rtc, enabled);
    216}
    217
    218static const struct rtc_class_ops da9055_rtc_ops = {
    219	.read_time	= da9055_rtc_read_time,
    220	.set_time	= da9055_rtc_set_time,
    221	.read_alarm	= da9055_rtc_read_alarm,
    222	.set_alarm	= da9055_rtc_set_alarm,
    223	.alarm_irq_enable = da9055_rtc_alarm_irq_enable,
    224};
    225
    226static int da9055_rtc_device_init(struct da9055 *da9055,
    227					struct da9055_pdata *pdata)
    228{
    229	int ret;
    230
    231	/* Enable RTC and the internal Crystal */
    232	ret = da9055_reg_update(da9055, DA9055_REG_CONTROL_B,
    233				DA9055_RTC_EN, DA9055_RTC_EN);
    234	if (ret < 0)
    235		return ret;
    236	ret = da9055_reg_update(da9055, DA9055_REG_EN_32K,
    237				DA9055_CRYSTAL_EN, DA9055_CRYSTAL_EN);
    238	if (ret < 0)
    239		return ret;
    240
    241	/* Enable RTC in Power Down mode */
    242	ret = da9055_reg_update(da9055, DA9055_REG_CONTROL_B,
    243				DA9055_RTC_MODE_PD, DA9055_RTC_MODE_PD);
    244	if (ret < 0)
    245		return ret;
    246
    247	/* Enable RTC in Reset mode */
    248	if (pdata && pdata->reset_enable) {
    249		ret = da9055_reg_update(da9055, DA9055_REG_CONTROL_B,
    250					DA9055_RTC_MODE_SD,
    251					DA9055_RTC_MODE_SD <<
    252					DA9055_RTC_MODE_SD_SHIFT);
    253		if (ret < 0)
    254			return ret;
    255	}
    256
    257	/* Disable the RTC TICK ALM */
    258	ret = da9055_reg_update(da9055, DA9055_REG_ALARM_MO,
    259				DA9055_RTC_TICK_WAKE_MASK, 0);
    260	if (ret < 0)
    261		return ret;
    262
    263	return 0;
    264}
    265
    266static int da9055_rtc_probe(struct platform_device *pdev)
    267{
    268	struct da9055_rtc *rtc;
    269	struct da9055_pdata *pdata = NULL;
    270	int ret, alm_irq;
    271
    272	rtc = devm_kzalloc(&pdev->dev, sizeof(struct da9055_rtc), GFP_KERNEL);
    273	if (!rtc)
    274		return -ENOMEM;
    275
    276	rtc->da9055 = dev_get_drvdata(pdev->dev.parent);
    277	pdata = dev_get_platdata(rtc->da9055->dev);
    278	platform_set_drvdata(pdev, rtc);
    279
    280	ret = da9055_rtc_device_init(rtc->da9055, pdata);
    281	if (ret < 0)
    282		goto err_rtc;
    283
    284	ret = da9055_reg_read(rtc->da9055, DA9055_REG_ALARM_Y);
    285	if (ret < 0)
    286		goto err_rtc;
    287
    288	if (ret & DA9055_RTC_ALM_EN)
    289		rtc->alarm_enable = 1;
    290
    291	device_init_wakeup(&pdev->dev, 1);
    292
    293	rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
    294					&da9055_rtc_ops, THIS_MODULE);
    295	if (IS_ERR(rtc->rtc)) {
    296		ret = PTR_ERR(rtc->rtc);
    297		goto err_rtc;
    298	}
    299
    300	alm_irq = platform_get_irq_byname(pdev, "ALM");
    301	if (alm_irq < 0)
    302		return alm_irq;
    303
    304	ret = devm_request_threaded_irq(&pdev->dev, alm_irq, NULL,
    305					da9055_rtc_alm_irq,
    306					IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
    307					"ALM", rtc);
    308	if (ret != 0)
    309		dev_err(rtc->da9055->dev, "irq registration failed: %d\n", ret);
    310
    311err_rtc:
    312	return ret;
    313
    314}
    315
    316#ifdef CONFIG_PM
    317/* Turn off the alarm if it should not be a wake source. */
    318static int da9055_rtc_suspend(struct device *dev)
    319{
    320	struct platform_device *pdev = to_platform_device(dev);
    321	struct da9055_rtc *rtc = dev_get_drvdata(&pdev->dev);
    322	int ret;
    323
    324	if (!device_may_wakeup(&pdev->dev)) {
    325		/* Disable the ALM IRQ */
    326		ret = da9055_rtc_enable_alarm(rtc, 0);
    327		if (ret < 0)
    328			dev_err(&pdev->dev, "Failed to disable RTC ALM\n");
    329	}
    330
    331	return 0;
    332}
    333
    334/* Enable the alarm if it should be enabled (in case it was disabled to
    335 * prevent use as a wake source).
    336 */
    337static int da9055_rtc_resume(struct device *dev)
    338{
    339	struct platform_device *pdev = to_platform_device(dev);
    340	struct da9055_rtc *rtc = dev_get_drvdata(&pdev->dev);
    341	int ret;
    342
    343	if (!device_may_wakeup(&pdev->dev)) {
    344		if (rtc->alarm_enable) {
    345			ret = da9055_rtc_enable_alarm(rtc, 1);
    346			if (ret < 0)
    347				dev_err(&pdev->dev,
    348					"Failed to restart RTC ALM\n");
    349		}
    350	}
    351
    352	return 0;
    353}
    354
    355/* Unconditionally disable the alarm */
    356static int da9055_rtc_freeze(struct device *dev)
    357{
    358	struct platform_device *pdev = to_platform_device(dev);
    359	struct da9055_rtc *rtc = dev_get_drvdata(&pdev->dev);
    360	int ret;
    361
    362	ret = da9055_rtc_enable_alarm(rtc, 0);
    363	if (ret < 0)
    364		dev_err(&pdev->dev, "Failed to freeze RTC ALMs\n");
    365
    366	return 0;
    367
    368}
    369#else
    370#define da9055_rtc_suspend NULL
    371#define da9055_rtc_resume NULL
    372#define da9055_rtc_freeze NULL
    373#endif
    374
    375static const struct dev_pm_ops da9055_rtc_pm_ops = {
    376	.suspend = da9055_rtc_suspend,
    377	.resume = da9055_rtc_resume,
    378
    379	.freeze = da9055_rtc_freeze,
    380	.thaw = da9055_rtc_resume,
    381	.restore = da9055_rtc_resume,
    382
    383	.poweroff = da9055_rtc_suspend,
    384};
    385
    386static struct platform_driver da9055_rtc_driver = {
    387	.probe  = da9055_rtc_probe,
    388	.driver = {
    389		.name   = "da9055-rtc",
    390		.pm = &da9055_rtc_pm_ops,
    391	},
    392};
    393
    394module_platform_driver(da9055_rtc_driver);
    395
    396MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
    397MODULE_DESCRIPTION("RTC driver for Dialog DA9055 PMIC");
    398MODULE_LICENSE("GPL");
    399MODULE_ALIAS("platform:da9055-rtc");