brintos

brintos / linux-shallow public Read only

0
0
Text · 12.0 KiB · bad6a5d Raw
471 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * drivers/rtc/rtc-pl031.c4 *5 * Real Time Clock interface for ARM AMBA PrimeCell 031 RTC6 *7 * Author: Deepak Saxena <dsaxena@plexity.net>8 *9 * Copyright 2006 (c) MontaVista Software, Inc.10 *11 * Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>12 * Copyright 2010 (c) ST-Ericsson AB13 */14#include <linux/module.h>15#include <linux/rtc.h>16#include <linux/init.h>17#include <linux/interrupt.h>18#include <linux/amba/bus.h>19#include <linux/io.h>20#include <linux/bcd.h>21#include <linux/delay.h>22#include <linux/pm_wakeirq.h>23#include <linux/slab.h>24 25/*26 * Register definitions27 */28#define	RTC_DR		0x00	/* Data read register */29#define	RTC_MR		0x04	/* Match register */30#define	RTC_LR		0x08	/* Data load register */31#define	RTC_CR		0x0c	/* Control register */32#define	RTC_IMSC	0x10	/* Interrupt mask and set register */33#define	RTC_RIS		0x14	/* Raw interrupt status register */34#define	RTC_MIS		0x18	/* Masked interrupt status register */35#define	RTC_ICR		0x1c	/* Interrupt clear register */36/* ST variants have additional timer functionality */37#define RTC_TDR		0x20	/* Timer data read register */38#define RTC_TLR		0x24	/* Timer data load register */39#define RTC_TCR		0x28	/* Timer control register */40#define RTC_YDR		0x30	/* Year data read register */41#define RTC_YMR		0x34	/* Year match register */42#define RTC_YLR		0x38	/* Year data load register */43 44#define RTC_CR_EN	(1 << 0)	/* counter enable bit */45#define RTC_CR_CWEN	(1 << 26)	/* Clockwatch enable bit */46 47#define RTC_TCR_EN	(1 << 1) /* Periodic timer enable bit */48 49/* Common bit definitions for Interrupt status and control registers */50#define RTC_BIT_AI	(1 << 0) /* Alarm interrupt bit */51#define RTC_BIT_PI	(1 << 1) /* Periodic interrupt bit. ST variants only. */52 53/* Common bit definations for ST v2 for reading/writing time */54#define RTC_SEC_SHIFT 055#define RTC_SEC_MASK (0x3F << RTC_SEC_SHIFT) /* Second [0-59] */56#define RTC_MIN_SHIFT 657#define RTC_MIN_MASK (0x3F << RTC_MIN_SHIFT) /* Minute [0-59] */58#define RTC_HOUR_SHIFT 1259#define RTC_HOUR_MASK (0x1F << RTC_HOUR_SHIFT) /* Hour [0-23] */60#define RTC_WDAY_SHIFT 1761#define RTC_WDAY_MASK (0x7 << RTC_WDAY_SHIFT) /* Day of Week [1-7] 1=Sunday */62#define RTC_MDAY_SHIFT 2063#define RTC_MDAY_MASK (0x1F << RTC_MDAY_SHIFT) /* Day of Month [1-31] */64#define RTC_MON_SHIFT 2565#define RTC_MON_MASK (0xF << RTC_MON_SHIFT) /* Month [1-12] 1=January */66 67#define RTC_TIMER_FREQ 3276868 69/**70 * struct pl031_vendor_data - per-vendor variations71 * @ops: the vendor-specific operations used on this silicon version72 * @clockwatch: if this is an ST Microelectronics silicon version with a73 *	clockwatch function74 * @st_weekday: if this is an ST Microelectronics silicon version that need75 *	the weekday fix76 * @irqflags: special IRQ flags per variant77 */78struct pl031_vendor_data {79	struct rtc_class_ops ops;80	bool clockwatch;81	bool st_weekday;82	unsigned long irqflags;83	time64_t range_min;84	timeu64_t range_max;85};86 87struct pl031_local {88	struct pl031_vendor_data *vendor;89	struct rtc_device *rtc;90	void __iomem *base;91};92 93static int pl031_alarm_irq_enable(struct device *dev,94	unsigned int enabled)95{96	struct pl031_local *ldata = dev_get_drvdata(dev);97	unsigned long imsc;98 99	/* Clear any pending alarm interrupts. */100	writel(RTC_BIT_AI, ldata->base + RTC_ICR);101 102	imsc = readl(ldata->base + RTC_IMSC);103 104	if (enabled == 1)105		writel(imsc | RTC_BIT_AI, ldata->base + RTC_IMSC);106	else107		writel(imsc & ~RTC_BIT_AI, ldata->base + RTC_IMSC);108 109	return 0;110}111 112/*113 * Convert Gregorian date to ST v2 RTC format.114 */115static int pl031_stv2_tm_to_time(struct device *dev,116				 struct rtc_time *tm, unsigned long *st_time,117	unsigned long *bcd_year)118{119	int year = tm->tm_year + 1900;120	int wday = tm->tm_wday;121 122	/* wday masking is not working in hardware so wday must be valid */123	if (wday < -1 || wday > 6) {124		dev_err(dev, "invalid wday value %d\n", tm->tm_wday);125		return -EINVAL;126	} else if (wday == -1) {127		/* wday is not provided, calculate it here */128		struct rtc_time calc_tm;129 130		rtc_time64_to_tm(rtc_tm_to_time64(tm), &calc_tm);131		wday = calc_tm.tm_wday;132	}133 134	*bcd_year = (bin2bcd(year % 100) | bin2bcd(year / 100) << 8);135 136	*st_time = ((tm->tm_mon + 1) << RTC_MON_SHIFT)137			|	(tm->tm_mday << RTC_MDAY_SHIFT)138			|	((wday + 1) << RTC_WDAY_SHIFT)139			|	(tm->tm_hour << RTC_HOUR_SHIFT)140			|	(tm->tm_min << RTC_MIN_SHIFT)141			|	(tm->tm_sec << RTC_SEC_SHIFT);142 143	return 0;144}145 146/*147 * Convert ST v2 RTC format to Gregorian date.148 */149static int pl031_stv2_time_to_tm(unsigned long st_time, unsigned long bcd_year,150	struct rtc_time *tm)151{152	tm->tm_year = bcd2bin(bcd_year) + (bcd2bin(bcd_year >> 8) * 100);153	tm->tm_mon  = ((st_time & RTC_MON_MASK) >> RTC_MON_SHIFT) - 1;154	tm->tm_mday = ((st_time & RTC_MDAY_MASK) >> RTC_MDAY_SHIFT);155	tm->tm_wday = ((st_time & RTC_WDAY_MASK) >> RTC_WDAY_SHIFT) - 1;156	tm->tm_hour = ((st_time & RTC_HOUR_MASK) >> RTC_HOUR_SHIFT);157	tm->tm_min  = ((st_time & RTC_MIN_MASK) >> RTC_MIN_SHIFT);158	tm->tm_sec  = ((st_time & RTC_SEC_MASK) >> RTC_SEC_SHIFT);159 160	tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);161	tm->tm_year -= 1900;162 163	return 0;164}165 166static int pl031_stv2_read_time(struct device *dev, struct rtc_time *tm)167{168	struct pl031_local *ldata = dev_get_drvdata(dev);169 170	pl031_stv2_time_to_tm(readl(ldata->base + RTC_DR),171			readl(ldata->base + RTC_YDR), tm);172 173	return 0;174}175 176static int pl031_stv2_set_time(struct device *dev, struct rtc_time *tm)177{178	unsigned long time;179	unsigned long bcd_year;180	struct pl031_local *ldata = dev_get_drvdata(dev);181	int ret;182 183	ret = pl031_stv2_tm_to_time(dev, tm, &time, &bcd_year);184	if (ret == 0) {185		writel(bcd_year, ldata->base + RTC_YLR);186		writel(time, ldata->base + RTC_LR);187	}188 189	return ret;190}191 192static int pl031_stv2_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)193{194	struct pl031_local *ldata = dev_get_drvdata(dev);195	int ret;196 197	ret = pl031_stv2_time_to_tm(readl(ldata->base + RTC_MR),198			readl(ldata->base + RTC_YMR), &alarm->time);199 200	alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI;201	alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI;202 203	return ret;204}205 206static int pl031_stv2_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)207{208	struct pl031_local *ldata = dev_get_drvdata(dev);209	unsigned long time;210	unsigned long bcd_year;211	int ret;212 213	ret = pl031_stv2_tm_to_time(dev, &alarm->time,214				    &time, &bcd_year);215	if (ret == 0) {216		writel(bcd_year, ldata->base + RTC_YMR);217		writel(time, ldata->base + RTC_MR);218 219		pl031_alarm_irq_enable(dev, alarm->enabled);220	}221 222	return ret;223}224 225static irqreturn_t pl031_interrupt(int irq, void *dev_id)226{227	struct pl031_local *ldata = dev_id;228	unsigned long rtcmis;229	unsigned long events = 0;230 231	rtcmis = readl(ldata->base + RTC_MIS);232	if (rtcmis & RTC_BIT_AI) {233		writel(RTC_BIT_AI, ldata->base + RTC_ICR);234		events |= (RTC_AF | RTC_IRQF);235		rtc_update_irq(ldata->rtc, 1, events);236 237		return IRQ_HANDLED;238	}239 240	return IRQ_NONE;241}242 243static int pl031_read_time(struct device *dev, struct rtc_time *tm)244{245	struct pl031_local *ldata = dev_get_drvdata(dev);246 247	rtc_time64_to_tm(readl(ldata->base + RTC_DR), tm);248 249	return 0;250}251 252static int pl031_set_time(struct device *dev, struct rtc_time *tm)253{254	struct pl031_local *ldata = dev_get_drvdata(dev);255 256	writel(rtc_tm_to_time64(tm), ldata->base + RTC_LR);257 258	return 0;259}260 261static int pl031_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)262{263	struct pl031_local *ldata = dev_get_drvdata(dev);264 265	rtc_time64_to_tm(readl(ldata->base + RTC_MR), &alarm->time);266 267	alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI;268	alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI;269 270	return 0;271}272 273static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)274{275	struct pl031_local *ldata = dev_get_drvdata(dev);276 277	writel(rtc_tm_to_time64(&alarm->time), ldata->base + RTC_MR);278	pl031_alarm_irq_enable(dev, alarm->enabled);279 280	return 0;281}282 283static void pl031_remove(struct amba_device *adev)284{285	struct pl031_local *ldata = dev_get_drvdata(&adev->dev);286 287	dev_pm_clear_wake_irq(&adev->dev);288	device_init_wakeup(&adev->dev, false);289	if (adev->irq[0])290		free_irq(adev->irq[0], ldata);291	amba_release_regions(adev);292}293 294static int pl031_probe(struct amba_device *adev, const struct amba_id *id)295{296	int ret;297	struct pl031_local *ldata;298	struct pl031_vendor_data *vendor = id->data;299	struct rtc_class_ops *ops;300	unsigned long time, data;301 302	ret = amba_request_regions(adev, NULL);303	if (ret)304		goto err_req;305 306	ldata = devm_kzalloc(&adev->dev, sizeof(struct pl031_local),307			     GFP_KERNEL);308	ops = devm_kmemdup(&adev->dev, &vendor->ops, sizeof(vendor->ops),309			   GFP_KERNEL);310	if (!ldata || !ops) {311		ret = -ENOMEM;312		goto out;313	}314 315	ldata->vendor = vendor;316	ldata->base = devm_ioremap(&adev->dev, adev->res.start,317				   resource_size(&adev->res));318	if (!ldata->base) {319		ret = -ENOMEM;320		goto out;321	}322 323	amba_set_drvdata(adev, ldata);324 325	dev_dbg(&adev->dev, "designer ID = 0x%02x\n", amba_manf(adev));326	dev_dbg(&adev->dev, "revision = 0x%01x\n", amba_rev(adev));327 328	data = readl(ldata->base + RTC_CR);329	/* Enable the clockwatch on ST Variants */330	if (vendor->clockwatch)331		data |= RTC_CR_CWEN;332	else333		data |= RTC_CR_EN;334	writel(data, ldata->base + RTC_CR);335 336	/*337	 * On ST PL031 variants, the RTC reset value does not provide correct338	 * weekday for 2000-01-01. Correct the erroneous sunday to saturday.339	 */340	if (vendor->st_weekday) {341		if (readl(ldata->base + RTC_YDR) == 0x2000) {342			time = readl(ldata->base + RTC_DR);343			if ((time &344			     (RTC_MON_MASK | RTC_MDAY_MASK | RTC_WDAY_MASK))345			    == 0x02120000) {346				time = time | (0x7 << RTC_WDAY_SHIFT);347				writel(0x2000, ldata->base + RTC_YLR);348				writel(time, ldata->base + RTC_LR);349			}350		}351	}352 353	device_init_wakeup(&adev->dev, true);354	ldata->rtc = devm_rtc_allocate_device(&adev->dev);355	if (IS_ERR(ldata->rtc)) {356		ret = PTR_ERR(ldata->rtc);357		goto out;358	}359 360	if (!adev->irq[0])361		clear_bit(RTC_FEATURE_ALARM, ldata->rtc->features);362 363	ldata->rtc->ops = ops;364	ldata->rtc->range_min = vendor->range_min;365	ldata->rtc->range_max = vendor->range_max;366 367	ret = devm_rtc_register_device(ldata->rtc);368	if (ret)369		goto out;370 371	if (adev->irq[0]) {372		ret = request_irq(adev->irq[0], pl031_interrupt,373				  vendor->irqflags, "rtc-pl031", ldata);374		if (ret)375			goto out;376		dev_pm_set_wake_irq(&adev->dev, adev->irq[0]);377	}378	return 0;379 380out:381	amba_release_regions(adev);382err_req:383 384	return ret;385}386 387/* Operations for the original ARM version */388static struct pl031_vendor_data arm_pl031 = {389	.ops = {390		.read_time = pl031_read_time,391		.set_time = pl031_set_time,392		.read_alarm = pl031_read_alarm,393		.set_alarm = pl031_set_alarm,394		.alarm_irq_enable = pl031_alarm_irq_enable,395	},396	.range_max = U32_MAX,397};398 399/* The First ST derivative */400static struct pl031_vendor_data stv1_pl031 = {401	.ops = {402		.read_time = pl031_read_time,403		.set_time = pl031_set_time,404		.read_alarm = pl031_read_alarm,405		.set_alarm = pl031_set_alarm,406		.alarm_irq_enable = pl031_alarm_irq_enable,407	},408	.clockwatch = true,409	.st_weekday = true,410	.range_max = U32_MAX,411};412 413/* And the second ST derivative */414static struct pl031_vendor_data stv2_pl031 = {415	.ops = {416		.read_time = pl031_stv2_read_time,417		.set_time = pl031_stv2_set_time,418		.read_alarm = pl031_stv2_read_alarm,419		.set_alarm = pl031_stv2_set_alarm,420		.alarm_irq_enable = pl031_alarm_irq_enable,421	},422	.clockwatch = true,423	.st_weekday = true,424	/*425	 * This variant shares the IRQ with another block and must not426	 * suspend that IRQ line.427	 * TODO check if it shares with IRQF_NO_SUSPEND user, else we can428	 * remove IRQF_COND_SUSPEND429	 */430	.irqflags = IRQF_SHARED | IRQF_COND_SUSPEND,431	.range_min = RTC_TIMESTAMP_BEGIN_0000,432	.range_max = RTC_TIMESTAMP_END_9999,433};434 435static const struct amba_id pl031_ids[] = {436	{437		.id = 0x00041031,438		.mask = 0x000fffff,439		.data = &arm_pl031,440	},441	/* ST Micro variants */442	{443		.id = 0x00180031,444		.mask = 0x00ffffff,445		.data = &stv1_pl031,446	},447	{448		.id = 0x00280031,449		.mask = 0x00ffffff,450		.data = &stv2_pl031,451	},452	{0, 0},453};454 455MODULE_DEVICE_TABLE(amba, pl031_ids);456 457static struct amba_driver pl031_driver = {458	.drv = {459		.name = "rtc-pl031",460	},461	.id_table = pl031_ids,462	.probe = pl031_probe,463	.remove = pl031_remove,464};465 466module_amba_driver(pl031_driver);467 468MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");469MODULE_DESCRIPTION("ARM AMBA PL031 RTC Driver");470MODULE_LICENSE("GPL");471