1// SPDX-License-Identifier: GPL-2.0+ 2// 3// Copyright 2022 NXP. 4 5#include <linux/init.h> 6#include <linux/io.h> 7#include <linux/kernel.h> 8#include <linux/mfd/syscon.h> 9#include <linux/module.h> 10#include <linux/of.h> 11#include <linux/platform_device.h> 12#include <linux/pm_wakeirq.h> 13#include <linux/regmap.h> 14#include <linux/rtc.h> 15 16#define BBNSM_CTRL 0x8 17#define BBNSM_INT_EN 0x10 18#define BBNSM_EVENTS 0x14 19#define BBNSM_RTC_LS 0x40 20#define BBNSM_RTC_MS 0x44 21#define BBNSM_TA 0x50 22 23#define RTC_EN 0x2 24#define RTC_EN_MSK 0x3 25#define TA_EN (0x2 << 2) 26#define TA_DIS (0x1 << 2) 27#define TA_EN_MSK (0x3 << 2) 28#define RTC_INT_EN 0x2 29#define TA_INT_EN (0x2 << 2) 30 31#define BBNSM_EVENT_TA (0x2 << 2) 32 33#define CNTR_TO_SECS_SH 15 34 35struct bbnsm_rtc { 36 struct rtc_device *rtc; 37 struct regmap *regmap; 38 int irq; 39 struct clk *clk; 40}; 41 42static u32 bbnsm_read_counter(struct bbnsm_rtc *bbnsm) 43{ 44 u32 rtc_msb, rtc_lsb; 45 unsigned int timeout = 100; 46 u32 time; 47 u32 tmp = 0; 48 49 do { 50 time = tmp; 51 /* read the msb */ 52 regmap_read(bbnsm->regmap, BBNSM_RTC_MS, &rtc_msb); 53 /* read the lsb */ 54 regmap_read(bbnsm->regmap, BBNSM_RTC_LS, &rtc_lsb); 55 /* convert to seconds */ 56 tmp = (rtc_msb << 17) | (rtc_lsb >> 15); 57 } while (tmp != time && --timeout); 58 59 return time; 60} 61 62static int bbnsm_rtc_read_time(struct device *dev, struct rtc_time *tm) 63{ 64 struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); 65 unsigned long time; 66 u32 val; 67 68 regmap_read(bbnsm->regmap, BBNSM_CTRL, &val); 69 if ((val & RTC_EN_MSK) != RTC_EN) 70 return -EINVAL; 71 72 time = bbnsm_read_counter(bbnsm); 73 rtc_time64_to_tm(time, tm); 74 75 return 0; 76} 77 78static int bbnsm_rtc_set_time(struct device *dev, struct rtc_time *tm) 79{ 80 struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); 81 unsigned long time = rtc_tm_to_time64(tm); 82 83 /* disable the RTC first */ 84 regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, RTC_EN_MSK, 0); 85 86 /* write the 32bit sec time to 47 bit timer counter, leaving 15 LSBs blank */ 87 regmap_write(bbnsm->regmap, BBNSM_RTC_LS, time << CNTR_TO_SECS_SH); 88 regmap_write(bbnsm->regmap, BBNSM_RTC_MS, time >> (32 - CNTR_TO_SECS_SH)); 89 90 /* Enable the RTC again */ 91 regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, RTC_EN_MSK, RTC_EN); 92 93 return 0; 94} 95 96static int bbnsm_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 97{ 98 struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); 99 u32 bbnsm_events, bbnsm_ta; 100 101 regmap_read(bbnsm->regmap, BBNSM_TA, &bbnsm_ta); 102 rtc_time64_to_tm(bbnsm_ta, &alrm->time); 103 104 regmap_read(bbnsm->regmap, BBNSM_EVENTS, &bbnsm_events); 105 alrm->pending = (bbnsm_events & BBNSM_EVENT_TA) ? 1 : 0; 106 107 return 0; 108} 109 110static int bbnsm_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) 111{ 112 struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); 113 114 /* enable the alarm event */ 115 regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, TA_EN_MSK, enable ? TA_EN : TA_DIS); 116 /* enable the alarm interrupt */ 117 regmap_update_bits(bbnsm->regmap, BBNSM_INT_EN, TA_EN_MSK, enable ? TA_EN : TA_DIS); 118 119 return 0; 120} 121 122static int bbnsm_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 123{ 124 struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); 125 unsigned long time = rtc_tm_to_time64(&alrm->time); 126 127 /* disable the alarm */ 128 regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, TA_EN, TA_EN); 129 130 /* write the seconds to TA */ 131 regmap_write(bbnsm->regmap, BBNSM_TA, time); 132 133 return bbnsm_rtc_alarm_irq_enable(dev, alrm->enabled); 134} 135 136static const struct rtc_class_ops bbnsm_rtc_ops = { 137 .read_time = bbnsm_rtc_read_time, 138 .set_time = bbnsm_rtc_set_time, 139 .read_alarm = bbnsm_rtc_read_alarm, 140 .set_alarm = bbnsm_rtc_set_alarm, 141 .alarm_irq_enable = bbnsm_rtc_alarm_irq_enable, 142}; 143 144static irqreturn_t bbnsm_rtc_irq_handler(int irq, void *dev_id) 145{ 146 struct device *dev = dev_id; 147 struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); 148 u32 val; 149 150 regmap_read(bbnsm->regmap, BBNSM_EVENTS, &val); 151 if (val & BBNSM_EVENT_TA) { 152 bbnsm_rtc_alarm_irq_enable(dev, false); 153 /* clear the alarm event */ 154 regmap_write_bits(bbnsm->regmap, BBNSM_EVENTS, TA_EN_MSK, BBNSM_EVENT_TA); 155 rtc_update_irq(bbnsm->rtc, 1, RTC_AF | RTC_IRQF); 156 157 return IRQ_HANDLED; 158 } 159 160 return IRQ_NONE; 161} 162 163static int bbnsm_rtc_probe(struct platform_device *pdev) 164{ 165 struct device_node *np = pdev->dev.of_node; 166 struct bbnsm_rtc *bbnsm; 167 int ret; 168 169 bbnsm = devm_kzalloc(&pdev->dev, sizeof(*bbnsm), GFP_KERNEL); 170 if (!bbnsm) 171 return -ENOMEM; 172 173 bbnsm->rtc = devm_rtc_allocate_device(&pdev->dev); 174 if (IS_ERR(bbnsm->rtc)) 175 return PTR_ERR(bbnsm->rtc); 176 177 bbnsm->regmap = syscon_node_to_regmap(np->parent); 178 if (IS_ERR(bbnsm->regmap)) { 179 dev_dbg(&pdev->dev, "bbnsm get regmap failed\n"); 180 return PTR_ERR(bbnsm->regmap); 181 } 182 183 bbnsm->irq = platform_get_irq(pdev, 0); 184 if (bbnsm->irq < 0) 185 return bbnsm->irq; 186 187 platform_set_drvdata(pdev, bbnsm); 188 189 /* clear all the pending events */ 190 regmap_write(bbnsm->regmap, BBNSM_EVENTS, 0x7A); 191 192 device_init_wakeup(&pdev->dev, true); 193 dev_pm_set_wake_irq(&pdev->dev, bbnsm->irq); 194 195 ret = devm_request_irq(&pdev->dev, bbnsm->irq, bbnsm_rtc_irq_handler, 196 IRQF_SHARED, "rtc alarm", &pdev->dev); 197 if (ret) { 198 dev_err(&pdev->dev, "failed to request irq %d: %d\n", 199 bbnsm->irq, ret); 200 return ret; 201 } 202 203 bbnsm->rtc->ops = &bbnsm_rtc_ops; 204 bbnsm->rtc->range_max = U32_MAX; 205 206 return devm_rtc_register_device(bbnsm->rtc); 207} 208 209static const struct of_device_id bbnsm_dt_ids[] = { 210 { .compatible = "nxp,imx93-bbnsm-rtc" }, 211 { /* sentinel */ }, 212}; 213MODULE_DEVICE_TABLE(of, bbnsm_dt_ids); 214 215static struct platform_driver bbnsm_rtc_driver = { 216 .driver = { 217 .name = "bbnsm_rtc", 218 .of_match_table = bbnsm_dt_ids, 219 }, 220 .probe = bbnsm_rtc_probe, 221}; 222module_platform_driver(bbnsm_rtc_driver); 223 224MODULE_AUTHOR("Jacky Bai <ping.bai@nxp.com>"); 225MODULE_DESCRIPTION("NXP BBNSM RTC Driver"); 226MODULE_LICENSE("GPL"); 227