forked from mirrors/linux
		
	rtc: stm32: add stm32mp1 rtc support
This patch adds support for stm32mp1 RTC. Some common registers with previous RTC version have a different offset. It is the case for Control Register (CR) and ALaRMA Register (ALRMAR). There are also new registers regarding event flags: now, Alarm event flag is in Status Register (SR) and write 1 in Status Clear Register (SCR) is required to clear the event. Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
This commit is contained in:
		
							parent
							
								
									5c4554d62b
								
							
						
					
					
						commit
						b72252b658
					
				
					 1 changed files with 89 additions and 14 deletions
				
			
		|  | @ -11,6 +11,7 @@ | |||
| #include <linux/mfd/syscon.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/of_device.h> | ||||
| #include <linux/pm_wakeirq.h> | ||||
| #include <linux/regmap.h> | ||||
| #include <linux/rtc.h> | ||||
| 
 | ||||
|  | @ -39,7 +40,7 @@ | |||
| #define STM32_RTC_CR_ALRAE		BIT(8) | ||||
| #define STM32_RTC_CR_ALRAIE		BIT(12) | ||||
| 
 | ||||
| /* STM32_RTC_ISR bit fields */ | ||||
| /* STM32_RTC_ISR/STM32_RTC_ICSR bit fields */ | ||||
| #define STM32_RTC_ISR_ALRAWF		BIT(0) | ||||
| #define STM32_RTC_ISR_INITS		BIT(4) | ||||
| #define STM32_RTC_ISR_RSF		BIT(5) | ||||
|  | @ -71,21 +72,36 @@ | |||
| #define STM32_RTC_ALRMXR_WDAY		GENMASK(27, 24) | ||||
| #define STM32_RTC_ALRMXR_DATE_MASK	BIT(31) | ||||
| 
 | ||||
| /* STM32_RTC_SR/_SCR bit fields */ | ||||
| #define STM32_RTC_SR_ALRA		BIT(0) | ||||
| 
 | ||||
| /* STM32_RTC_VERR bit fields */ | ||||
| #define STM32_RTC_VERR_MINREV_SHIFT	0 | ||||
| #define STM32_RTC_VERR_MINREV		GENMASK(3, 0) | ||||
| #define STM32_RTC_VERR_MAJREV_SHIFT	4 | ||||
| #define STM32_RTC_VERR_MAJREV		GENMASK(7, 4) | ||||
| 
 | ||||
| /* STM32_RTC_WPR key constants */ | ||||
| #define RTC_WPR_1ST_KEY			0xCA | ||||
| #define RTC_WPR_2ND_KEY			0x53 | ||||
| #define RTC_WPR_WRONG_KEY		0xFF | ||||
| 
 | ||||
| /* Max STM32 RTC register offset is 0x3FC */ | ||||
| #define UNDEF_REG			0xFFFF | ||||
| 
 | ||||
| struct stm32_rtc; | ||||
| 
 | ||||
| struct stm32_rtc_registers { | ||||
| 	u8 tr; | ||||
| 	u8 dr; | ||||
| 	u8 cr; | ||||
| 	u8 isr; | ||||
| 	u8 prer; | ||||
| 	u8 alrmar; | ||||
| 	u8 wpr; | ||||
| 	u16 tr; | ||||
| 	u16 dr; | ||||
| 	u16 cr; | ||||
| 	u16 isr; | ||||
| 	u16 prer; | ||||
| 	u16 alrmar; | ||||
| 	u16 wpr; | ||||
| 	u16 sr; | ||||
| 	u16 scr; | ||||
| 	u16 verr; | ||||
| }; | ||||
| 
 | ||||
| struct stm32_rtc_events { | ||||
|  | @ -98,6 +114,7 @@ struct stm32_rtc_data { | |||
| 	void (*clear_events)(struct stm32_rtc *rtc, unsigned int flags); | ||||
| 	bool has_pclk; | ||||
| 	bool need_dbp; | ||||
| 	bool has_wakeirq; | ||||
| }; | ||||
| 
 | ||||
| struct stm32_rtc { | ||||
|  | @ -110,6 +127,7 @@ struct stm32_rtc { | |||
| 	struct clk *rtc_ck; | ||||
| 	const struct stm32_rtc_data *data; | ||||
| 	int irq_alarm; | ||||
| 	int wakeirq_alarm; | ||||
| }; | ||||
| 
 | ||||
| static void stm32_rtc_wpr_unlock(struct stm32_rtc *rtc) | ||||
|  | @ -193,7 +211,7 @@ static irqreturn_t stm32_rtc_alarm_irq(int irq, void *dev_id) | |||
| 
 | ||||
| 	mutex_lock(&rtc->rtc_dev->ops_lock); | ||||
| 
 | ||||
| 	status = readl_relaxed(rtc->base + regs->isr); | ||||
| 	status = readl_relaxed(rtc->base + regs->sr); | ||||
| 	cr = readl_relaxed(rtc->base + regs->cr); | ||||
| 
 | ||||
| 	if ((status & evts->alra) && | ||||
|  | @ -325,7 +343,7 @@ static int stm32_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
| 
 | ||||
| 	alrmar = readl_relaxed(rtc->base + regs->alrmar); | ||||
| 	cr = readl_relaxed(rtc->base + regs->cr); | ||||
| 	status = readl_relaxed(rtc->base + regs->isr); | ||||
| 	status = readl_relaxed(rtc->base + regs->sr); | ||||
| 
 | ||||
| 	if (alrmar & STM32_RTC_ALRMXR_DATE_MASK) { | ||||
| 		/*
 | ||||
|  | @ -533,6 +551,7 @@ static void stm32_rtc_clear_events(struct stm32_rtc *rtc, | |||
| static const struct stm32_rtc_data stm32_rtc_data = { | ||||
| 	.has_pclk = false, | ||||
| 	.need_dbp = true, | ||||
| 	.has_wakeirq = false, | ||||
| 	.regs = { | ||||
| 		.tr = 0x00, | ||||
| 		.dr = 0x04, | ||||
|  | @ -541,6 +560,9 @@ static const struct stm32_rtc_data stm32_rtc_data = { | |||
| 		.prer = 0x10, | ||||
| 		.alrmar = 0x1C, | ||||
| 		.wpr = 0x24, | ||||
| 		.sr = 0x0C, /* set to ISR offset to ease alarm management */ | ||||
| 		.scr = UNDEF_REG, | ||||
| 		.verr = UNDEF_REG, | ||||
| 	}, | ||||
| 	.events = { | ||||
| 		.alra = STM32_RTC_ISR_ALRAF, | ||||
|  | @ -551,6 +573,7 @@ static const struct stm32_rtc_data stm32_rtc_data = { | |||
| static const struct stm32_rtc_data stm32h7_rtc_data = { | ||||
| 	.has_pclk = true, | ||||
| 	.need_dbp = true, | ||||
| 	.has_wakeirq = false, | ||||
| 	.regs = { | ||||
| 		.tr = 0x00, | ||||
| 		.dr = 0x04, | ||||
|  | @ -559,6 +582,9 @@ static const struct stm32_rtc_data stm32h7_rtc_data = { | |||
| 		.prer = 0x10, | ||||
| 		.alrmar = 0x1C, | ||||
| 		.wpr = 0x24, | ||||
| 		.sr = 0x0C, /* set to ISR offset to ease alarm management */ | ||||
| 		.scr = UNDEF_REG, | ||||
| 		.verr = UNDEF_REG, | ||||
| 	}, | ||||
| 	.events = { | ||||
| 		.alra = STM32_RTC_ISR_ALRAF, | ||||
|  | @ -566,9 +592,41 @@ static const struct stm32_rtc_data stm32h7_rtc_data = { | |||
| 	.clear_events = stm32_rtc_clear_events, | ||||
| }; | ||||
| 
 | ||||
| static void stm32mp1_rtc_clear_events(struct stm32_rtc *rtc, | ||||
| 				      unsigned int flags) | ||||
| { | ||||
| 	struct stm32_rtc_registers regs = rtc->data->regs; | ||||
| 
 | ||||
| 	/* Flags are cleared by writing 1 in RTC_SCR */ | ||||
| 	writel_relaxed(flags, rtc->base + regs.scr); | ||||
| } | ||||
| 
 | ||||
| static const struct stm32_rtc_data stm32mp1_data = { | ||||
| 	.has_pclk = true, | ||||
| 	.need_dbp = false, | ||||
| 	.has_wakeirq = true, | ||||
| 	.regs = { | ||||
| 		.tr = 0x00, | ||||
| 		.dr = 0x04, | ||||
| 		.cr = 0x18, | ||||
| 		.isr = 0x0C, /* named RTC_ICSR on stm32mp1 */ | ||||
| 		.prer = 0x10, | ||||
| 		.alrmar = 0x40, | ||||
| 		.wpr = 0x24, | ||||
| 		.sr = 0x50, | ||||
| 		.scr = 0x5C, | ||||
| 		.verr = 0x3F4, | ||||
| 	}, | ||||
| 	.events = { | ||||
| 		.alra = STM32_RTC_SR_ALRA, | ||||
| 	}, | ||||
| 	.clear_events = stm32mp1_rtc_clear_events, | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id stm32_rtc_of_match[] = { | ||||
| 	{ .compatible = "st,stm32-rtc", .data = &stm32_rtc_data }, | ||||
| 	{ .compatible = "st,stm32h7-rtc", .data = &stm32h7_rtc_data }, | ||||
| 	{ .compatible = "st,stm32mp1-rtc", .data = &stm32mp1_data }, | ||||
| 	{} | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(of, stm32_rtc_of_match); | ||||
|  | @ -727,12 +785,19 @@ static int stm32_rtc_probe(struct platform_device *pdev) | |||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, rtc); | ||||
| 
 | ||||
| 	ret = device_init_wakeup(&pdev->dev, true); | ||||
| 	if (rtc->data->has_wakeirq) { | ||||
| 		rtc->wakeirq_alarm = platform_get_irq(pdev, 1); | ||||
| 		if (rtc->wakeirq_alarm <= 0) | ||||
| 			ret = rtc->wakeirq_alarm; | ||||
| 		else | ||||
| 			ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, | ||||
| 							    rtc->wakeirq_alarm); | ||||
| 	} | ||||
| 	if (ret) | ||||
| 		dev_warn(&pdev->dev, | ||||
| 			 "alarm won't be able to wake up the system"); | ||||
| 		dev_warn(&pdev->dev, "alarm can't wake up the system: %d", ret); | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, rtc); | ||||
| 
 | ||||
| 	rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name, | ||||
| 						&stm32_rtc_ops, THIS_MODULE); | ||||
|  | @ -760,6 +825,14 @@ static int stm32_rtc_probe(struct platform_device *pdev) | |||
| 	if (!(readl_relaxed(rtc->base + regs->isr) & STM32_RTC_ISR_INITS)) | ||||
| 		dev_warn(&pdev->dev, "Date/Time must be initialized\n"); | ||||
| 
 | ||||
| 	if (regs->verr != UNDEF_REG) { | ||||
| 		u32 ver = readl_relaxed(rtc->base + regs->verr); | ||||
| 
 | ||||
| 		dev_info(&pdev->dev, "registered rev:%d.%d\n", | ||||
| 			 (ver >> STM32_RTC_VERR_MAJREV_SHIFT) & 0xF, | ||||
| 			 (ver >> STM32_RTC_VERR_MINREV_SHIFT) & 0xF); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| err: | ||||
| 	if (rtc->data->has_pclk) | ||||
|  | @ -769,6 +842,7 @@ static int stm32_rtc_probe(struct platform_device *pdev) | |||
| 	if (rtc->data->need_dbp) | ||||
| 		regmap_update_bits(rtc->dbp, rtc->dbp_reg, rtc->dbp_mask, 0); | ||||
| 
 | ||||
| 	dev_pm_clear_wake_irq(&pdev->dev); | ||||
| 	device_init_wakeup(&pdev->dev, false); | ||||
| 
 | ||||
| 	return ret; | ||||
|  | @ -795,6 +869,7 @@ static int stm32_rtc_remove(struct platform_device *pdev) | |||
| 	if (rtc->data->need_dbp) | ||||
| 		regmap_update_bits(rtc->dbp, rtc->dbp_reg, rtc->dbp_mask, 0); | ||||
| 
 | ||||
| 	dev_pm_clear_wake_irq(&pdev->dev); | ||||
| 	device_init_wakeup(&pdev->dev, false); | ||||
| 
 | ||||
| 	return 0; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Amelie Delaunay
						Amelie Delaunay