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/mfd/syscon.h> | ||||||
| #include <linux/module.h> | #include <linux/module.h> | ||||||
| #include <linux/of_device.h> | #include <linux/of_device.h> | ||||||
|  | #include <linux/pm_wakeirq.h> | ||||||
| #include <linux/regmap.h> | #include <linux/regmap.h> | ||||||
| #include <linux/rtc.h> | #include <linux/rtc.h> | ||||||
| 
 | 
 | ||||||
|  | @ -39,7 +40,7 @@ | ||||||
| #define STM32_RTC_CR_ALRAE		BIT(8) | #define STM32_RTC_CR_ALRAE		BIT(8) | ||||||
| #define STM32_RTC_CR_ALRAIE		BIT(12) | #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_ALRAWF		BIT(0) | ||||||
| #define STM32_RTC_ISR_INITS		BIT(4) | #define STM32_RTC_ISR_INITS		BIT(4) | ||||||
| #define STM32_RTC_ISR_RSF		BIT(5) | #define STM32_RTC_ISR_RSF		BIT(5) | ||||||
|  | @ -71,21 +72,36 @@ | ||||||
| #define STM32_RTC_ALRMXR_WDAY		GENMASK(27, 24) | #define STM32_RTC_ALRMXR_WDAY		GENMASK(27, 24) | ||||||
| #define STM32_RTC_ALRMXR_DATE_MASK	BIT(31) | #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 */ | /* STM32_RTC_WPR key constants */ | ||||||
| #define RTC_WPR_1ST_KEY			0xCA | #define RTC_WPR_1ST_KEY			0xCA | ||||||
| #define RTC_WPR_2ND_KEY			0x53 | #define RTC_WPR_2ND_KEY			0x53 | ||||||
| #define RTC_WPR_WRONG_KEY		0xFF | #define RTC_WPR_WRONG_KEY		0xFF | ||||||
| 
 | 
 | ||||||
|  | /* Max STM32 RTC register offset is 0x3FC */ | ||||||
|  | #define UNDEF_REG			0xFFFF | ||||||
|  | 
 | ||||||
| struct stm32_rtc; | struct stm32_rtc; | ||||||
| 
 | 
 | ||||||
| struct stm32_rtc_registers { | struct stm32_rtc_registers { | ||||||
| 	u8 tr; | 	u16 tr; | ||||||
| 	u8 dr; | 	u16 dr; | ||||||
| 	u8 cr; | 	u16 cr; | ||||||
| 	u8 isr; | 	u16 isr; | ||||||
| 	u8 prer; | 	u16 prer; | ||||||
| 	u8 alrmar; | 	u16 alrmar; | ||||||
| 	u8 wpr; | 	u16 wpr; | ||||||
|  | 	u16 sr; | ||||||
|  | 	u16 scr; | ||||||
|  | 	u16 verr; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct stm32_rtc_events { | struct stm32_rtc_events { | ||||||
|  | @ -98,6 +114,7 @@ struct stm32_rtc_data { | ||||||
| 	void (*clear_events)(struct stm32_rtc *rtc, unsigned int flags); | 	void (*clear_events)(struct stm32_rtc *rtc, unsigned int flags); | ||||||
| 	bool has_pclk; | 	bool has_pclk; | ||||||
| 	bool need_dbp; | 	bool need_dbp; | ||||||
|  | 	bool has_wakeirq; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct stm32_rtc { | struct stm32_rtc { | ||||||
|  | @ -110,6 +127,7 @@ struct stm32_rtc { | ||||||
| 	struct clk *rtc_ck; | 	struct clk *rtc_ck; | ||||||
| 	const struct stm32_rtc_data *data; | 	const struct stm32_rtc_data *data; | ||||||
| 	int irq_alarm; | 	int irq_alarm; | ||||||
|  | 	int wakeirq_alarm; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void stm32_rtc_wpr_unlock(struct stm32_rtc *rtc) | 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); | 	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); | 	cr = readl_relaxed(rtc->base + regs->cr); | ||||||
| 
 | 
 | ||||||
| 	if ((status & evts->alra) && | 	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); | 	alrmar = readl_relaxed(rtc->base + regs->alrmar); | ||||||
| 	cr = readl_relaxed(rtc->base + regs->cr); | 	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) { | 	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 = { | static const struct stm32_rtc_data stm32_rtc_data = { | ||||||
| 	.has_pclk = false, | 	.has_pclk = false, | ||||||
| 	.need_dbp = true, | 	.need_dbp = true, | ||||||
|  | 	.has_wakeirq = false, | ||||||
| 	.regs = { | 	.regs = { | ||||||
| 		.tr = 0x00, | 		.tr = 0x00, | ||||||
| 		.dr = 0x04, | 		.dr = 0x04, | ||||||
|  | @ -541,6 +560,9 @@ static const struct stm32_rtc_data stm32_rtc_data = { | ||||||
| 		.prer = 0x10, | 		.prer = 0x10, | ||||||
| 		.alrmar = 0x1C, | 		.alrmar = 0x1C, | ||||||
| 		.wpr = 0x24, | 		.wpr = 0x24, | ||||||
|  | 		.sr = 0x0C, /* set to ISR offset to ease alarm management */ | ||||||
|  | 		.scr = UNDEF_REG, | ||||||
|  | 		.verr = UNDEF_REG, | ||||||
| 	}, | 	}, | ||||||
| 	.events = { | 	.events = { | ||||||
| 		.alra = STM32_RTC_ISR_ALRAF, | 		.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 = { | static const struct stm32_rtc_data stm32h7_rtc_data = { | ||||||
| 	.has_pclk = true, | 	.has_pclk = true, | ||||||
| 	.need_dbp = true, | 	.need_dbp = true, | ||||||
|  | 	.has_wakeirq = false, | ||||||
| 	.regs = { | 	.regs = { | ||||||
| 		.tr = 0x00, | 		.tr = 0x00, | ||||||
| 		.dr = 0x04, | 		.dr = 0x04, | ||||||
|  | @ -559,6 +582,9 @@ static const struct stm32_rtc_data stm32h7_rtc_data = { | ||||||
| 		.prer = 0x10, | 		.prer = 0x10, | ||||||
| 		.alrmar = 0x1C, | 		.alrmar = 0x1C, | ||||||
| 		.wpr = 0x24, | 		.wpr = 0x24, | ||||||
|  | 		.sr = 0x0C, /* set to ISR offset to ease alarm management */ | ||||||
|  | 		.scr = UNDEF_REG, | ||||||
|  | 		.verr = UNDEF_REG, | ||||||
| 	}, | 	}, | ||||||
| 	.events = { | 	.events = { | ||||||
| 		.alra = STM32_RTC_ISR_ALRAF, | 		.alra = STM32_RTC_ISR_ALRAF, | ||||||
|  | @ -566,9 +592,41 @@ static const struct stm32_rtc_data stm32h7_rtc_data = { | ||||||
| 	.clear_events = stm32_rtc_clear_events, | 	.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[] = { | static const struct of_device_id stm32_rtc_of_match[] = { | ||||||
| 	{ .compatible = "st,stm32-rtc", .data = &stm32_rtc_data }, | 	{ .compatible = "st,stm32-rtc", .data = &stm32_rtc_data }, | ||||||
| 	{ .compatible = "st,stm32h7-rtc", .data = &stm32h7_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); | MODULE_DEVICE_TABLE(of, stm32_rtc_of_match); | ||||||
|  | @ -727,12 +785,19 @@ static int stm32_rtc_probe(struct platform_device *pdev) | ||||||
| 		goto err; | 		goto err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	platform_set_drvdata(pdev, rtc); |  | ||||||
| 
 |  | ||||||
| 	ret = device_init_wakeup(&pdev->dev, true); | 	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) | 	if (ret) | ||||||
| 		dev_warn(&pdev->dev, | 		dev_warn(&pdev->dev, "alarm can't wake up the system: %d", ret); | ||||||
| 			 "alarm won't be able to wake up the system"); | 
 | ||||||
|  | 	platform_set_drvdata(pdev, rtc); | ||||||
| 
 | 
 | ||||||
| 	rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name, | 	rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name, | ||||||
| 						&stm32_rtc_ops, THIS_MODULE); | 						&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)) | 	if (!(readl_relaxed(rtc->base + regs->isr) & STM32_RTC_ISR_INITS)) | ||||||
| 		dev_warn(&pdev->dev, "Date/Time must be initialized\n"); | 		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; | 	return 0; | ||||||
| err: | err: | ||||||
| 	if (rtc->data->has_pclk) | 	if (rtc->data->has_pclk) | ||||||
|  | @ -769,6 +842,7 @@ static int stm32_rtc_probe(struct platform_device *pdev) | ||||||
| 	if (rtc->data->need_dbp) | 	if (rtc->data->need_dbp) | ||||||
| 		regmap_update_bits(rtc->dbp, rtc->dbp_reg, rtc->dbp_mask, 0); | 		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); | 	device_init_wakeup(&pdev->dev, false); | ||||||
| 
 | 
 | ||||||
| 	return ret; | 	return ret; | ||||||
|  | @ -795,6 +869,7 @@ static int stm32_rtc_remove(struct platform_device *pdev) | ||||||
| 	if (rtc->data->need_dbp) | 	if (rtc->data->need_dbp) | ||||||
| 		regmap_update_bits(rtc->dbp, rtc->dbp_reg, rtc->dbp_mask, 0); | 		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); | 	device_init_wakeup(&pdev->dev, false); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Amelie Delaunay
						Amelie Delaunay