brintos

brintos / linux-shallow public Read only

0
0
Text · 12.5 KiB · 5cab995 Raw
487 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * An RTC driver for Allwinner A10/A204 *5 * Copyright (c) 2013, Carlo Caione <carlo.caione@gmail.com>6 */7 8#include <linux/delay.h>9#include <linux/err.h>10#include <linux/fs.h>11#include <linux/init.h>12#include <linux/interrupt.h>13#include <linux/io.h>14#include <linux/kernel.h>15#include <linux/module.h>16#include <linux/of.h>17#include <linux/platform_device.h>18#include <linux/rtc.h>19#include <linux/types.h>20 21#define SUNXI_LOSC_CTRL				0x000022#define SUNXI_LOSC_CTRL_RTC_HMS_ACC		BIT(8)23#define SUNXI_LOSC_CTRL_RTC_YMD_ACC		BIT(7)24 25#define SUNXI_RTC_YMD				0x000426 27#define SUNXI_RTC_HMS				0x000828 29#define SUNXI_ALRM_DHMS				0x000c30 31#define SUNXI_ALRM_EN				0x001432#define SUNXI_ALRM_EN_CNT_EN			BIT(8)33 34#define SUNXI_ALRM_IRQ_EN			0x001835#define SUNXI_ALRM_IRQ_EN_CNT_IRQ_EN		BIT(0)36 37#define SUNXI_ALRM_IRQ_STA			0x001c38#define SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND		BIT(0)39 40#define SUNXI_MASK_DH				0x0000001f41#define SUNXI_MASK_SM				0x0000003f42#define SUNXI_MASK_M				0x0000000f43#define SUNXI_MASK_LY				0x0000000144#define SUNXI_MASK_D				0x00000ffe45#define SUNXI_MASK_M				0x0000000f46 47#define SUNXI_GET(x, mask, shift)		(((x) & ((mask) << (shift))) \48							>> (shift))49 50#define SUNXI_SET(x, mask, shift)		(((x) & (mask)) << (shift))51 52/*53 * Get date values54 */55#define SUNXI_DATE_GET_DAY_VALUE(x)		SUNXI_GET(x, SUNXI_MASK_DH, 0)56#define SUNXI_DATE_GET_MON_VALUE(x)		SUNXI_GET(x, SUNXI_MASK_M, 8)57#define SUNXI_DATE_GET_YEAR_VALUE(x, mask)	SUNXI_GET(x, mask, 16)58 59/*60 * Get time values61 */62#define SUNXI_TIME_GET_SEC_VALUE(x)		SUNXI_GET(x, SUNXI_MASK_SM, 0)63#define SUNXI_TIME_GET_MIN_VALUE(x)		SUNXI_GET(x, SUNXI_MASK_SM, 8)64#define SUNXI_TIME_GET_HOUR_VALUE(x)		SUNXI_GET(x, SUNXI_MASK_DH, 16)65 66/*67 * Get alarm values68 */69#define SUNXI_ALRM_GET_SEC_VALUE(x)		SUNXI_GET(x, SUNXI_MASK_SM, 0)70#define SUNXI_ALRM_GET_MIN_VALUE(x)		SUNXI_GET(x, SUNXI_MASK_SM, 8)71#define SUNXI_ALRM_GET_HOUR_VALUE(x)		SUNXI_GET(x, SUNXI_MASK_DH, 16)72 73/*74 * Set date values75 */76#define SUNXI_DATE_SET_DAY_VALUE(x)		SUNXI_DATE_GET_DAY_VALUE(x)77#define SUNXI_DATE_SET_MON_VALUE(x)		SUNXI_SET(x, SUNXI_MASK_M, 8)78#define SUNXI_DATE_SET_YEAR_VALUE(x, mask)	SUNXI_SET(x, mask, 16)79#define SUNXI_LEAP_SET_VALUE(x, shift)		SUNXI_SET(x, SUNXI_MASK_LY, shift)80 81/*82 * Set time values83 */84#define SUNXI_TIME_SET_SEC_VALUE(x)		SUNXI_TIME_GET_SEC_VALUE(x)85#define SUNXI_TIME_SET_MIN_VALUE(x)		SUNXI_SET(x, SUNXI_MASK_SM, 8)86#define SUNXI_TIME_SET_HOUR_VALUE(x)		SUNXI_SET(x, SUNXI_MASK_DH, 16)87 88/*89 * Set alarm values90 */91#define SUNXI_ALRM_SET_SEC_VALUE(x)		SUNXI_ALRM_GET_SEC_VALUE(x)92#define SUNXI_ALRM_SET_MIN_VALUE(x)		SUNXI_SET(x, SUNXI_MASK_SM, 8)93#define SUNXI_ALRM_SET_HOUR_VALUE(x)		SUNXI_SET(x, SUNXI_MASK_DH, 16)94#define SUNXI_ALRM_SET_DAY_VALUE(x)		SUNXI_SET(x, SUNXI_MASK_D, 21)95 96/*97 * Time unit conversions98 */99#define SEC_IN_MIN				60100#define SEC_IN_HOUR				(60 * SEC_IN_MIN)101#define SEC_IN_DAY				(24 * SEC_IN_HOUR)102 103/*104 * The year parameter passed to the driver is usually an offset relative to105 * the year 1900. This macro is used to convert this offset to another one106 * relative to the minimum year allowed by the hardware.107 */108#define SUNXI_YEAR_OFF(x)			((x)->min - 1900)109 110/*111 * min and max year are arbitrary set considering the limited range of the112 * hardware register field113 */114struct sunxi_rtc_data_year {115	unsigned int min;		/* min year allowed */116	unsigned int max;		/* max year allowed */117	unsigned int mask;		/* mask for the year field */118	unsigned char leap_shift;	/* bit shift to get the leap year */119};120 121static const struct sunxi_rtc_data_year data_year_param[] = {122	[0] = {123		.min		= 2010,124		.max		= 2073,125		.mask		= 0x3f,126		.leap_shift	= 22,127	},128	[1] = {129		.min		= 1970,130		.max		= 2225,131		.mask		= 0xff,132		.leap_shift	= 24,133	},134};135 136struct sunxi_rtc_dev {137	struct rtc_device *rtc;138	struct device *dev;139	const struct sunxi_rtc_data_year *data_year;140	void __iomem *base;141	int irq;142};143 144static irqreturn_t sunxi_rtc_alarmirq(int irq, void *id)145{146	struct sunxi_rtc_dev *chip = (struct sunxi_rtc_dev *) id;147	u32 val;148 149	val = readl(chip->base + SUNXI_ALRM_IRQ_STA);150 151	if (val & SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND) {152		val |= SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND;153		writel(val, chip->base + SUNXI_ALRM_IRQ_STA);154 155		rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);156 157		return IRQ_HANDLED;158	}159 160	return IRQ_NONE;161}162 163static void sunxi_rtc_setaie(unsigned int to, struct sunxi_rtc_dev *chip)164{165	u32 alrm_val = 0;166	u32 alrm_irq_val = 0;167 168	if (to) {169		alrm_val = readl(chip->base + SUNXI_ALRM_EN);170		alrm_val |= SUNXI_ALRM_EN_CNT_EN;171 172		alrm_irq_val = readl(chip->base + SUNXI_ALRM_IRQ_EN);173		alrm_irq_val |= SUNXI_ALRM_IRQ_EN_CNT_IRQ_EN;174	} else {175		writel(SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND,176				chip->base + SUNXI_ALRM_IRQ_STA);177	}178 179	writel(alrm_val, chip->base + SUNXI_ALRM_EN);180	writel(alrm_irq_val, chip->base + SUNXI_ALRM_IRQ_EN);181}182 183static int sunxi_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)184{185	struct sunxi_rtc_dev *chip = dev_get_drvdata(dev);186	struct rtc_time *alrm_tm = &wkalrm->time;187	u32 alrm;188	u32 alrm_en;189	u32 date;190 191	alrm = readl(chip->base + SUNXI_ALRM_DHMS);192	date = readl(chip->base + SUNXI_RTC_YMD);193 194	alrm_tm->tm_sec = SUNXI_ALRM_GET_SEC_VALUE(alrm);195	alrm_tm->tm_min = SUNXI_ALRM_GET_MIN_VALUE(alrm);196	alrm_tm->tm_hour = SUNXI_ALRM_GET_HOUR_VALUE(alrm);197 198	alrm_tm->tm_mday = SUNXI_DATE_GET_DAY_VALUE(date);199	alrm_tm->tm_mon = SUNXI_DATE_GET_MON_VALUE(date);200	alrm_tm->tm_year = SUNXI_DATE_GET_YEAR_VALUE(date,201			chip->data_year->mask);202 203	alrm_tm->tm_mon -= 1;204 205	/*206	 * switch from (data_year->min)-relative offset to207	 * a (1900)-relative one208	 */209	alrm_tm->tm_year += SUNXI_YEAR_OFF(chip->data_year);210 211	alrm_en = readl(chip->base + SUNXI_ALRM_IRQ_EN);212	if (alrm_en & SUNXI_ALRM_EN_CNT_EN)213		wkalrm->enabled = 1;214 215	return 0;216}217 218static int sunxi_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)219{220	struct sunxi_rtc_dev *chip = dev_get_drvdata(dev);221	u32 date, time;222 223	/*224	 * read again in case it changes225	 */226	do {227		date = readl(chip->base + SUNXI_RTC_YMD);228		time = readl(chip->base + SUNXI_RTC_HMS);229	} while ((date != readl(chip->base + SUNXI_RTC_YMD)) ||230		 (time != readl(chip->base + SUNXI_RTC_HMS)));231 232	rtc_tm->tm_sec  = SUNXI_TIME_GET_SEC_VALUE(time);233	rtc_tm->tm_min  = SUNXI_TIME_GET_MIN_VALUE(time);234	rtc_tm->tm_hour = SUNXI_TIME_GET_HOUR_VALUE(time);235 236	rtc_tm->tm_mday = SUNXI_DATE_GET_DAY_VALUE(date);237	rtc_tm->tm_mon  = SUNXI_DATE_GET_MON_VALUE(date);238	rtc_tm->tm_year = SUNXI_DATE_GET_YEAR_VALUE(date,239					chip->data_year->mask);240 241	rtc_tm->tm_mon  -= 1;242 243	/*244	 * switch from (data_year->min)-relative offset to245	 * a (1900)-relative one246	 */247	rtc_tm->tm_year += SUNXI_YEAR_OFF(chip->data_year);248 249	return 0;250}251 252static int sunxi_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)253{254	struct sunxi_rtc_dev *chip = dev_get_drvdata(dev);255	struct rtc_time *alrm_tm = &wkalrm->time;256	struct rtc_time tm_now;257	u32 alrm;258	time64_t diff;259	unsigned long time_gap;260	unsigned long time_gap_day;261	unsigned long time_gap_hour;262	unsigned long time_gap_min;263	int ret;264 265	ret = sunxi_rtc_gettime(dev, &tm_now);266	if (ret < 0) {267		dev_err(dev, "Error in getting time\n");268		return -EINVAL;269	}270 271	diff = rtc_tm_sub(alrm_tm, &tm_now);272	if (diff <= 0) {273		dev_err(dev, "Date to set in the past\n");274		return -EINVAL;275	}276 277	if (diff > 255 * SEC_IN_DAY) {278		dev_err(dev, "Day must be in the range 0 - 255\n");279		return -EINVAL;280	}281 282	time_gap = diff;283	time_gap_day = time_gap / SEC_IN_DAY;284	time_gap -= time_gap_day * SEC_IN_DAY;285	time_gap_hour = time_gap / SEC_IN_HOUR;286	time_gap -= time_gap_hour * SEC_IN_HOUR;287	time_gap_min = time_gap / SEC_IN_MIN;288	time_gap -= time_gap_min * SEC_IN_MIN;289 290	sunxi_rtc_setaie(0, chip);291	writel(0, chip->base + SUNXI_ALRM_DHMS);292	usleep_range(100, 300);293 294	alrm = SUNXI_ALRM_SET_SEC_VALUE(time_gap) |295		SUNXI_ALRM_SET_MIN_VALUE(time_gap_min) |296		SUNXI_ALRM_SET_HOUR_VALUE(time_gap_hour) |297		SUNXI_ALRM_SET_DAY_VALUE(time_gap_day);298	writel(alrm, chip->base + SUNXI_ALRM_DHMS);299 300	writel(0, chip->base + SUNXI_ALRM_IRQ_EN);301	writel(SUNXI_ALRM_IRQ_EN_CNT_IRQ_EN, chip->base + SUNXI_ALRM_IRQ_EN);302 303	sunxi_rtc_setaie(wkalrm->enabled, chip);304 305	return 0;306}307 308static int sunxi_rtc_wait(struct sunxi_rtc_dev *chip, int offset,309			  unsigned int mask, unsigned int ms_timeout)310{311	const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout);312	u32 reg;313 314	do {315		reg = readl(chip->base + offset);316		reg &= mask;317 318		if (reg == mask)319			return 0;320 321	} while (time_before(jiffies, timeout));322 323	return -ETIMEDOUT;324}325 326static int sunxi_rtc_settime(struct device *dev, struct rtc_time *rtc_tm)327{328	struct sunxi_rtc_dev *chip = dev_get_drvdata(dev);329	u32 date = 0;330	u32 time = 0;331	unsigned int year;332 333	/*334	 * the input rtc_tm->tm_year is the offset relative to 1900. We use335	 * the SUNXI_YEAR_OFF macro to rebase it with respect to the min year336	 * allowed by the hardware337	 */338 339	year = rtc_tm->tm_year + 1900;340	if (year < chip->data_year->min || year > chip->data_year->max) {341		dev_err(dev, "rtc only supports year in range %u - %u\n",342			chip->data_year->min, chip->data_year->max);343		return -EINVAL;344	}345 346	rtc_tm->tm_year -= SUNXI_YEAR_OFF(chip->data_year);347	rtc_tm->tm_mon += 1;348 349	date = SUNXI_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) |350		SUNXI_DATE_SET_MON_VALUE(rtc_tm->tm_mon)  |351		SUNXI_DATE_SET_YEAR_VALUE(rtc_tm->tm_year,352				chip->data_year->mask);353 354	if (is_leap_year(year))355		date |= SUNXI_LEAP_SET_VALUE(1, chip->data_year->leap_shift);356 357	time = SUNXI_TIME_SET_SEC_VALUE(rtc_tm->tm_sec)  |358		SUNXI_TIME_SET_MIN_VALUE(rtc_tm->tm_min)  |359		SUNXI_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour);360 361	writel(0, chip->base + SUNXI_RTC_HMS);362	writel(0, chip->base + SUNXI_RTC_YMD);363 364	writel(time, chip->base + SUNXI_RTC_HMS);365 366	/*367	 * After writing the RTC HH-MM-SS register, the368	 * SUNXI_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not369	 * be cleared until the real writing operation is finished370	 */371 372	if (sunxi_rtc_wait(chip, SUNXI_LOSC_CTRL,373				SUNXI_LOSC_CTRL_RTC_HMS_ACC, 50)) {374		dev_err(dev, "Failed to set rtc time.\n");375		return -1;376	}377 378	writel(date, chip->base + SUNXI_RTC_YMD);379 380	/*381	 * After writing the RTC YY-MM-DD register, the382	 * SUNXI_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not383	 * be cleared until the real writing operation is finished384	 */385 386	if (sunxi_rtc_wait(chip, SUNXI_LOSC_CTRL,387				SUNXI_LOSC_CTRL_RTC_YMD_ACC, 50)) {388		dev_err(dev, "Failed to set rtc time.\n");389		return -1;390	}391 392	return 0;393}394 395static int sunxi_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)396{397	struct sunxi_rtc_dev *chip = dev_get_drvdata(dev);398 399	if (!enabled)400		sunxi_rtc_setaie(enabled, chip);401 402	return 0;403}404 405static const struct rtc_class_ops sunxi_rtc_ops = {406	.read_time		= sunxi_rtc_gettime,407	.set_time		= sunxi_rtc_settime,408	.read_alarm		= sunxi_rtc_getalarm,409	.set_alarm		= sunxi_rtc_setalarm,410	.alarm_irq_enable	= sunxi_rtc_alarm_irq_enable411};412 413static const struct of_device_id sunxi_rtc_dt_ids[] = {414	{ .compatible = "allwinner,sun4i-a10-rtc", .data = &data_year_param[0] },415	{ .compatible = "allwinner,sun7i-a20-rtc", .data = &data_year_param[1] },416	{ /* sentinel */ },417};418MODULE_DEVICE_TABLE(of, sunxi_rtc_dt_ids);419 420static int sunxi_rtc_probe(struct platform_device *pdev)421{422	struct sunxi_rtc_dev *chip;423	int ret;424 425	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);426	if (!chip)427		return -ENOMEM;428 429	platform_set_drvdata(pdev, chip);430	chip->dev = &pdev->dev;431 432	chip->rtc = devm_rtc_allocate_device(&pdev->dev);433	if (IS_ERR(chip->rtc))434		return PTR_ERR(chip->rtc);435 436	chip->base = devm_platform_ioremap_resource(pdev, 0);437	if (IS_ERR(chip->base))438		return PTR_ERR(chip->base);439 440	chip->irq = platform_get_irq(pdev, 0);441	if (chip->irq < 0)442		return chip->irq;443	ret = devm_request_irq(&pdev->dev, chip->irq, sunxi_rtc_alarmirq,444			0, dev_name(&pdev->dev), chip);445	if (ret) {446		dev_err(&pdev->dev, "Could not request IRQ\n");447		return ret;448	}449 450	chip->data_year = of_device_get_match_data(&pdev->dev);451	if (!chip->data_year) {452		dev_err(&pdev->dev, "Unable to setup RTC data\n");453		return -ENODEV;454	}455 456	/* clear the alarm count value */457	writel(0, chip->base + SUNXI_ALRM_DHMS);458 459	/* disable alarm, not generate irq pending */460	writel(0, chip->base + SUNXI_ALRM_EN);461 462	/* disable alarm week/cnt irq, unset to cpu */463	writel(0, chip->base + SUNXI_ALRM_IRQ_EN);464 465	/* clear alarm week/cnt irq pending */466	writel(SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND, chip->base +467			SUNXI_ALRM_IRQ_STA);468 469	chip->rtc->ops = &sunxi_rtc_ops;470 471	return devm_rtc_register_device(chip->rtc);472}473 474static struct platform_driver sunxi_rtc_driver = {475	.probe		= sunxi_rtc_probe,476	.driver		= {477		.name		= "sunxi-rtc",478		.of_match_table = sunxi_rtc_dt_ids,479	},480};481 482module_platform_driver(sunxi_rtc_driver);483 484MODULE_DESCRIPTION("sunxi RTC driver");485MODULE_AUTHOR("Carlo Caione <carlo.caione@gmail.com>");486MODULE_LICENSE("GPL");487