brintos

brintos / linux-shallow public Read only

0
0
Text · 7.2 KiB · 23edd11 Raw
285 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* NXP PCF50633 RTC Driver3 *4 * (C) 2006-2008 by Openmoko, Inc.5 * Author: Balaji Rao <balajirrao@openmoko.org>6 * All rights reserved.7 *8 * Broken down from monstrous PCF50633 driver mainly by9 * Harald Welte, Andy Green and Werner Almesberger10 */11 12#include <linux/kernel.h>13#include <linux/module.h>14#include <linux/init.h>15#include <linux/device.h>16#include <linux/slab.h>17#include <linux/platform_device.h>18#include <linux/rtc.h>19#include <linux/bcd.h>20#include <linux/err.h>21 22#include <linux/mfd/pcf50633/core.h>23 24#define PCF50633_REG_RTCSC	0x59 /* Second */25#define PCF50633_REG_RTCMN	0x5a /* Minute */26#define PCF50633_REG_RTCHR	0x5b /* Hour */27#define PCF50633_REG_RTCWD	0x5c /* Weekday */28#define PCF50633_REG_RTCDT	0x5d /* Day */29#define PCF50633_REG_RTCMT	0x5e /* Month */30#define PCF50633_REG_RTCYR	0x5f /* Year */31#define PCF50633_REG_RTCSCA	0x60 /* Alarm Second */32#define PCF50633_REG_RTCMNA	0x61 /* Alarm Minute */33#define PCF50633_REG_RTCHRA	0x62 /* Alarm Hour */34#define PCF50633_REG_RTCWDA	0x63 /* Alarm Weekday */35#define PCF50633_REG_RTCDTA	0x64 /* Alarm Day */36#define PCF50633_REG_RTCMTA	0x65 /* Alarm Month */37#define PCF50633_REG_RTCYRA	0x66 /* Alarm Year */38 39enum pcf50633_time_indexes {40	PCF50633_TI_SEC,41	PCF50633_TI_MIN,42	PCF50633_TI_HOUR,43	PCF50633_TI_WKDAY,44	PCF50633_TI_DAY,45	PCF50633_TI_MONTH,46	PCF50633_TI_YEAR,47	PCF50633_TI_EXTENT /* always last */48};49 50struct pcf50633_time {51	u_int8_t time[PCF50633_TI_EXTENT];52};53 54struct pcf50633_rtc {55	int alarm_enabled;56	int alarm_pending;57 58	struct pcf50633 *pcf;59	struct rtc_device *rtc_dev;60};61 62static void pcf2rtc_time(struct rtc_time *rtc, struct pcf50633_time *pcf)63{64	rtc->tm_sec = bcd2bin(pcf->time[PCF50633_TI_SEC]);65	rtc->tm_min = bcd2bin(pcf->time[PCF50633_TI_MIN]);66	rtc->tm_hour = bcd2bin(pcf->time[PCF50633_TI_HOUR]);67	rtc->tm_wday = bcd2bin(pcf->time[PCF50633_TI_WKDAY]);68	rtc->tm_mday = bcd2bin(pcf->time[PCF50633_TI_DAY]);69	rtc->tm_mon = bcd2bin(pcf->time[PCF50633_TI_MONTH]) - 1;70	rtc->tm_year = bcd2bin(pcf->time[PCF50633_TI_YEAR]) + 100;71}72 73static void rtc2pcf_time(struct pcf50633_time *pcf, struct rtc_time *rtc)74{75	pcf->time[PCF50633_TI_SEC] = bin2bcd(rtc->tm_sec);76	pcf->time[PCF50633_TI_MIN] = bin2bcd(rtc->tm_min);77	pcf->time[PCF50633_TI_HOUR] = bin2bcd(rtc->tm_hour);78	pcf->time[PCF50633_TI_WKDAY] = bin2bcd(rtc->tm_wday);79	pcf->time[PCF50633_TI_DAY] = bin2bcd(rtc->tm_mday);80	pcf->time[PCF50633_TI_MONTH] = bin2bcd(rtc->tm_mon + 1);81	pcf->time[PCF50633_TI_YEAR] = bin2bcd(rtc->tm_year % 100);82}83 84static int85pcf50633_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)86{87	struct pcf50633_rtc *rtc = dev_get_drvdata(dev);88	int err;89 90	if (enabled)91		err = pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);92	else93		err = pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);94 95	if (err < 0)96		return err;97 98	rtc->alarm_enabled = enabled;99 100	return 0;101}102 103static int pcf50633_rtc_read_time(struct device *dev, struct rtc_time *tm)104{105	struct pcf50633_rtc *rtc;106	struct pcf50633_time pcf_tm;107	int ret;108 109	rtc = dev_get_drvdata(dev);110 111	ret = pcf50633_read_block(rtc->pcf, PCF50633_REG_RTCSC,112					    PCF50633_TI_EXTENT,113					    &pcf_tm.time[0]);114	if (ret != PCF50633_TI_EXTENT) {115		dev_err(dev, "Failed to read time\n");116		return -EIO;117	}118 119	dev_dbg(dev, "PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",120		pcf_tm.time[PCF50633_TI_DAY],121		pcf_tm.time[PCF50633_TI_MONTH],122		pcf_tm.time[PCF50633_TI_YEAR],123		pcf_tm.time[PCF50633_TI_HOUR],124		pcf_tm.time[PCF50633_TI_MIN],125		pcf_tm.time[PCF50633_TI_SEC]);126 127	pcf2rtc_time(tm, &pcf_tm);128 129	dev_dbg(dev, "RTC_TIME: %ptRr\n", tm);130 131	return 0;132}133 134static int pcf50633_rtc_set_time(struct device *dev, struct rtc_time *tm)135{136	struct pcf50633_rtc *rtc;137	struct pcf50633_time pcf_tm;138	int alarm_masked, ret = 0;139 140	rtc = dev_get_drvdata(dev);141 142	dev_dbg(dev, "RTC_TIME: %ptRr\n", tm);143 144	rtc2pcf_time(&pcf_tm, tm);145 146	dev_dbg(dev, "PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",147		pcf_tm.time[PCF50633_TI_DAY],148		pcf_tm.time[PCF50633_TI_MONTH],149		pcf_tm.time[PCF50633_TI_YEAR],150		pcf_tm.time[PCF50633_TI_HOUR],151		pcf_tm.time[PCF50633_TI_MIN],152		pcf_tm.time[PCF50633_TI_SEC]);153 154 155	alarm_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_ALARM);156 157	if (!alarm_masked)158		pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);159 160	/* Returns 0 on success */161	ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSC,162					     PCF50633_TI_EXTENT,163					     &pcf_tm.time[0]);164 165	if (!alarm_masked)166		pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);167 168	return ret;169}170 171static int pcf50633_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)172{173	struct pcf50633_rtc *rtc;174	struct pcf50633_time pcf_tm;175	int ret = 0;176 177	rtc = dev_get_drvdata(dev);178 179	alrm->enabled = rtc->alarm_enabled;180	alrm->pending = rtc->alarm_pending;181 182	ret = pcf50633_read_block(rtc->pcf, PCF50633_REG_RTCSCA,183				PCF50633_TI_EXTENT, &pcf_tm.time[0]);184	if (ret != PCF50633_TI_EXTENT) {185		dev_err(dev, "Failed to read time\n");186		return -EIO;187	}188 189	pcf2rtc_time(&alrm->time, &pcf_tm);190 191	return rtc_valid_tm(&alrm->time);192}193 194static int pcf50633_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)195{196	struct pcf50633_rtc *rtc;197	struct pcf50633_time pcf_tm;198	int alarm_masked, ret = 0;199 200	rtc = dev_get_drvdata(dev);201 202	rtc2pcf_time(&pcf_tm, &alrm->time);203 204	/* do like mktime does and ignore tm_wday */205	pcf_tm.time[PCF50633_TI_WKDAY] = 7;206 207	alarm_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_ALARM);208 209	/* disable alarm interrupt */210	if (!alarm_masked)211		pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);212 213	/* Returns 0 on success */214	ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSCA,215				PCF50633_TI_EXTENT, &pcf_tm.time[0]);216	if (!alrm->enabled)217		rtc->alarm_pending = 0;218 219	if (!alarm_masked || alrm->enabled)220		pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);221	rtc->alarm_enabled = alrm->enabled;222 223	return ret;224}225 226static const struct rtc_class_ops pcf50633_rtc_ops = {227	.read_time		= pcf50633_rtc_read_time,228	.set_time		= pcf50633_rtc_set_time,229	.read_alarm		= pcf50633_rtc_read_alarm,230	.set_alarm		= pcf50633_rtc_set_alarm,231	.alarm_irq_enable	= pcf50633_rtc_alarm_irq_enable,232};233 234static void pcf50633_rtc_irq(int irq, void *data)235{236	struct pcf50633_rtc *rtc = data;237 238	rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);239	rtc->alarm_pending = 1;240}241 242static int pcf50633_rtc_probe(struct platform_device *pdev)243{244	struct pcf50633_rtc *rtc;245 246	rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);247	if (!rtc)248		return -ENOMEM;249 250	rtc->pcf = dev_to_pcf50633(pdev->dev.parent);251	platform_set_drvdata(pdev, rtc);252	rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, "pcf50633-rtc",253				&pcf50633_rtc_ops, THIS_MODULE);254 255	if (IS_ERR(rtc->rtc_dev))256		return PTR_ERR(rtc->rtc_dev);257 258	pcf50633_register_irq(rtc->pcf, PCF50633_IRQ_ALARM,259					pcf50633_rtc_irq, rtc);260	return 0;261}262 263static void pcf50633_rtc_remove(struct platform_device *pdev)264{265	struct pcf50633_rtc *rtc;266 267	rtc = platform_get_drvdata(pdev);268	pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_ALARM);269}270 271static struct platform_driver pcf50633_rtc_driver = {272	.driver = {273		.name = "pcf50633-rtc",274	},275	.probe = pcf50633_rtc_probe,276	.remove_new = pcf50633_rtc_remove,277};278 279module_platform_driver(pcf50633_rtc_driver);280 281MODULE_DESCRIPTION("PCF50633 RTC driver");282MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");283MODULE_LICENSE("GPL");284 285