brintos

brintos / linux-shallow public Read only

0
0
Text · 10.1 KiB · 71eafe4 Raw
409 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Real-time clock driver for MPC51214 *5 * Copyright 2007, Domen Puncer <domen.puncer@telargo.com>6 * Copyright 2008, Freescale Semiconductor, Inc. All rights reserved.7 * Copyright 2011, Dmitry Eremin-Solenikov8 */9 10#include <linux/init.h>11#include <linux/module.h>12#include <linux/rtc.h>13#include <linux/of.h>14#include <linux/of_irq.h>15#include <linux/platform_device.h>16#include <linux/io.h>17#include <linux/slab.h>18 19struct mpc5121_rtc_regs {20	u8 set_time;		/* RTC + 0x00 */21	u8 hour_set;		/* RTC + 0x01 */22	u8 minute_set;		/* RTC + 0x02 */23	u8 second_set;		/* RTC + 0x03 */24 25	u8 set_date;		/* RTC + 0x04 */26	u8 month_set;		/* RTC + 0x05 */27	u8 weekday_set;		/* RTC + 0x06 */28	u8 date_set;		/* RTC + 0x07 */29 30	u8 write_sw;		/* RTC + 0x08 */31	u8 sw_set;		/* RTC + 0x09 */32	u16 year_set;		/* RTC + 0x0a */33 34	u8 alm_enable;		/* RTC + 0x0c */35	u8 alm_hour_set;	/* RTC + 0x0d */36	u8 alm_min_set;		/* RTC + 0x0e */37	u8 int_enable;		/* RTC + 0x0f */38 39	u8 reserved1;40	u8 hour;		/* RTC + 0x11 */41	u8 minute;		/* RTC + 0x12 */42	u8 second;		/* RTC + 0x13 */43 44	u8 month;		/* RTC + 0x14 */45	u8 wday_mday;		/* RTC + 0x15 */46	u16 year;		/* RTC + 0x16 */47 48	u8 int_alm;		/* RTC + 0x18 */49	u8 int_sw;		/* RTC + 0x19 */50	u8 alm_status;		/* RTC + 0x1a */51	u8 sw_minute;		/* RTC + 0x1b */52 53	u8 bus_error_1;		/* RTC + 0x1c */54	u8 int_day;		/* RTC + 0x1d */55	u8 int_min;		/* RTC + 0x1e */56	u8 int_sec;		/* RTC + 0x1f */57 58	/*59	 * target_time:60	 *	intended to be used for hibernation but hibernation61	 *	does not work on silicon rev 1.5 so use it for non-volatile62	 *	storage of offset between the actual_time register and linux63	 *	time64	 */65	u32 target_time;	/* RTC + 0x20 */66	/*67	 * actual_time:68	 *	readonly time since VBAT_RTC was last connected69	 */70	u32 actual_time;	/* RTC + 0x24 */71	u32 keep_alive;		/* RTC + 0x28 */72};73 74struct mpc5121_rtc_data {75	unsigned irq;76	unsigned irq_periodic;77	struct mpc5121_rtc_regs __iomem *regs;78	struct rtc_device *rtc;79	struct rtc_wkalrm wkalarm;80};81 82/*83 * Update second/minute/hour registers.84 *85 * This is just so alarm will work.86 */87static void mpc5121_rtc_update_smh(struct mpc5121_rtc_regs __iomem *regs,88				   struct rtc_time *tm)89{90	out_8(&regs->second_set, tm->tm_sec);91	out_8(&regs->minute_set, tm->tm_min);92	out_8(&regs->hour_set, tm->tm_hour);93 94	/* set time sequence */95	out_8(&regs->set_time, 0x1);96	out_8(&regs->set_time, 0x3);97	out_8(&regs->set_time, 0x1);98	out_8(&regs->set_time, 0x0);99}100 101static int mpc5121_rtc_read_time(struct device *dev, struct rtc_time *tm)102{103	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);104	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;105	unsigned long now;106 107	/*108	 * linux time is actual_time plus the offset saved in target_time109	 */110	now = in_be32(&regs->actual_time) + in_be32(&regs->target_time);111 112	rtc_time64_to_tm(now, tm);113 114	/*115	 * update second minute hour registers116	 * so alarms will work117	 */118	mpc5121_rtc_update_smh(regs, tm);119 120	return 0;121}122 123static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm)124{125	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);126	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;127	unsigned long now;128 129	/*130	 * The actual_time register is read only so we write the offset131	 * between it and linux time to the target_time register.132	 */133	now = rtc_tm_to_time64(tm);134	out_be32(&regs->target_time, now - in_be32(&regs->actual_time));135 136	/*137	 * update second minute hour registers138	 * so alarms will work139	 */140	mpc5121_rtc_update_smh(regs, tm);141 142	return 0;143}144 145static int mpc5200_rtc_read_time(struct device *dev, struct rtc_time *tm)146{147	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);148	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;149	int tmp;150 151	tm->tm_sec = in_8(&regs->second);152	tm->tm_min = in_8(&regs->minute);153 154	/* 12 hour format? */155	if (in_8(&regs->hour) & 0x20)156		tm->tm_hour = (in_8(&regs->hour) >> 1) +157			(in_8(&regs->hour) & 1 ? 12 : 0);158	else159		tm->tm_hour = in_8(&regs->hour);160 161	tmp = in_8(&regs->wday_mday);162	tm->tm_mday = tmp & 0x1f;163	tm->tm_mon = in_8(&regs->month) - 1;164	tm->tm_year = in_be16(&regs->year) - 1900;165	tm->tm_wday = (tmp >> 5) % 7;166	tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);167	tm->tm_isdst = 0;168 169	return 0;170}171 172static int mpc5200_rtc_set_time(struct device *dev, struct rtc_time *tm)173{174	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);175	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;176 177	mpc5121_rtc_update_smh(regs, tm);178 179	/* date */180	out_8(&regs->month_set, tm->tm_mon + 1);181	out_8(&regs->weekday_set, tm->tm_wday ? tm->tm_wday : 7);182	out_8(&regs->date_set, tm->tm_mday);183	out_be16(&regs->year_set, tm->tm_year + 1900);184 185	/* set date sequence */186	out_8(&regs->set_date, 0x1);187	out_8(&regs->set_date, 0x3);188	out_8(&regs->set_date, 0x1);189	out_8(&regs->set_date, 0x0);190 191	return 0;192}193 194static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)195{196	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);197	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;198 199	*alarm = rtc->wkalarm;200 201	alarm->pending = in_8(&regs->alm_status);202 203	return 0;204}205 206static int mpc5121_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)207{208	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);209	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;210 211	alarm->time.tm_mday = -1;212	alarm->time.tm_mon = -1;213	alarm->time.tm_year = -1;214 215	out_8(&regs->alm_min_set, alarm->time.tm_min);216	out_8(&regs->alm_hour_set, alarm->time.tm_hour);217 218	out_8(&regs->alm_enable, alarm->enabled);219 220	rtc->wkalarm = *alarm;221	return 0;222}223 224static irqreturn_t mpc5121_rtc_handler(int irq, void *dev)225{226	struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);227	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;228 229	if (in_8(&regs->int_alm)) {230		/* acknowledge and clear status */231		out_8(&regs->int_alm, 1);232		out_8(&regs->alm_status, 1);233 234		rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);235		return IRQ_HANDLED;236	}237 238	return IRQ_NONE;239}240 241static irqreturn_t mpc5121_rtc_handler_upd(int irq, void *dev)242{243	struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);244	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;245 246	if (in_8(&regs->int_sec) && (in_8(&regs->int_enable) & 0x1)) {247		/* acknowledge */248		out_8(&regs->int_sec, 1);249 250		rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_UF);251		return IRQ_HANDLED;252	}253 254	return IRQ_NONE;255}256 257static int mpc5121_rtc_alarm_irq_enable(struct device *dev,258					unsigned int enabled)259{260	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);261	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;262	int val;263 264	if (enabled)265		val = 1;266	else267		val = 0;268 269	out_8(&regs->alm_enable, val);270	rtc->wkalarm.enabled = val;271 272	return 0;273}274 275static const struct rtc_class_ops mpc5121_rtc_ops = {276	.read_time = mpc5121_rtc_read_time,277	.set_time = mpc5121_rtc_set_time,278	.read_alarm = mpc5121_rtc_read_alarm,279	.set_alarm = mpc5121_rtc_set_alarm,280	.alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,281};282 283static const struct rtc_class_ops mpc5200_rtc_ops = {284	.read_time = mpc5200_rtc_read_time,285	.set_time = mpc5200_rtc_set_time,286	.read_alarm = mpc5121_rtc_read_alarm,287	.set_alarm = mpc5121_rtc_set_alarm,288	.alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,289};290 291static int mpc5121_rtc_probe(struct platform_device *op)292{293	struct mpc5121_rtc_data *rtc;294	int err = 0;295 296	rtc = devm_kzalloc(&op->dev, sizeof(*rtc), GFP_KERNEL);297	if (!rtc)298		return -ENOMEM;299 300	rtc->regs = devm_platform_ioremap_resource(op, 0);301	if (IS_ERR(rtc->regs)) {302		dev_err(&op->dev, "%s: couldn't map io space\n", __func__);303		return PTR_ERR(rtc->regs);304	}305 306	device_init_wakeup(&op->dev, 1);307 308	platform_set_drvdata(op, rtc);309 310	rtc->irq = irq_of_parse_and_map(op->dev.of_node, 1);311	err = devm_request_irq(&op->dev, rtc->irq, mpc5121_rtc_handler, 0,312			       "mpc5121-rtc", &op->dev);313	if (err) {314		dev_err(&op->dev, "%s: could not request irq: %i\n",315							__func__, rtc->irq);316		goto out_dispose;317	}318 319	rtc->irq_periodic = irq_of_parse_and_map(op->dev.of_node, 0);320	err = devm_request_irq(&op->dev, rtc->irq_periodic,321			       mpc5121_rtc_handler_upd, 0, "mpc5121-rtc_upd",322			       &op->dev);323	if (err) {324		dev_err(&op->dev, "%s: could not request irq: %i\n",325						__func__, rtc->irq_periodic);326		goto out_dispose2;327	}328 329	rtc->rtc = devm_rtc_allocate_device(&op->dev);330	if (IS_ERR(rtc->rtc)) {331		err = PTR_ERR(rtc->rtc);332		goto out_dispose2;333	}334 335	rtc->rtc->ops = &mpc5200_rtc_ops;336	set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtc->features);337	clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtc->features);338	rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_0000;339	rtc->rtc->range_max = 65733206399ULL; /* 4052-12-31 23:59:59 */340 341	if (of_device_is_compatible(op->dev.of_node, "fsl,mpc5121-rtc")) {342		u32 ka;343		ka = in_be32(&rtc->regs->keep_alive);344		if (ka & 0x02) {345			dev_warn(&op->dev,346				"mpc5121-rtc: Battery or oscillator failure!\n");347			out_be32(&rtc->regs->keep_alive, ka);348		}349		rtc->rtc->ops = &mpc5121_rtc_ops;350		/*351		 * This is a limitation of the driver that abuses the target352		 * time register, the actual maximum year for the mpc5121 is353		 * also 4052.354		 */355		rtc->rtc->range_min = 0;356		rtc->rtc->range_max = U32_MAX;357	}358 359	err = devm_rtc_register_device(rtc->rtc);360	if (err)361		goto out_dispose2;362 363	return 0;364 365out_dispose2:366	irq_dispose_mapping(rtc->irq_periodic);367out_dispose:368	irq_dispose_mapping(rtc->irq);369 370	return err;371}372 373static void mpc5121_rtc_remove(struct platform_device *op)374{375	struct mpc5121_rtc_data *rtc = platform_get_drvdata(op);376	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;377 378	/* disable interrupt, so there are no nasty surprises */379	out_8(&regs->alm_enable, 0);380	out_8(&regs->int_enable, in_8(&regs->int_enable) & ~0x1);381 382	irq_dispose_mapping(rtc->irq);383	irq_dispose_mapping(rtc->irq_periodic);384}385 386#ifdef CONFIG_OF387static const struct of_device_id mpc5121_rtc_match[] = {388	{ .compatible = "fsl,mpc5121-rtc", },389	{ .compatible = "fsl,mpc5200-rtc", },390	{},391};392MODULE_DEVICE_TABLE(of, mpc5121_rtc_match);393#endif394 395static struct platform_driver mpc5121_rtc_driver = {396	.driver = {397		.name = "mpc5121-rtc",398		.of_match_table = of_match_ptr(mpc5121_rtc_match),399	},400	.probe = mpc5121_rtc_probe,401	.remove_new = mpc5121_rtc_remove,402};403 404module_platform_driver(mpc5121_rtc_driver);405 406MODULE_DESCRIPTION("Freescale MPC5121 built-in RTC driver");407MODULE_LICENSE("GPL");408MODULE_AUTHOR("John Rigby <jcrigby@gmail.com>");409