brintos

brintos / linux-shallow public Read only

0
0
Text · 10.0 KiB · 711f62e Raw
452 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * drivers/rtc/rtc-rc5t619.c4 *5 * Real time clock driver for RICOH RC5T619 power management chip.6 *7 * Copyright (C) 2019 Andreas Kemnade8 */9 10#include <linux/kernel.h>11#include <linux/device.h>12#include <linux/errno.h>13#include <linux/init.h>14#include <linux/module.h>15#include <linux/mfd/rn5t618.h>16#include <linux/platform_device.h>17#include <linux/regmap.h>18#include <linux/bcd.h>19#include <linux/rtc.h>20#include <linux/slab.h>21#include <linux/irqdomain.h>22 23struct rc5t619_rtc {24	int			irq;25	struct rtc_device	*rtc;26	struct rn5t618 *rn5t618;27};28 29#define CTRL1_ALARM_ENABLED 0x4030#define CTRL1_24HR 0x2031#define CTRL1_PERIODIC_MASK 0xf32 33#define CTRL2_PON 0x1034#define CTRL2_ALARM_STATUS 0x8035#define CTRL2_CTFG 0x436#define CTRL2_CTC 0x137 38#define MONTH_CENTFLAG 0x8039#define HOUR_PMFLAG 0x2040#define MDAY_DAL_EXT 0x8041 42static uint8_t rtc5t619_12hour_bcd2bin(uint8_t hour)43{44	if (hour & HOUR_PMFLAG) {45		hour = bcd2bin(hour & ~HOUR_PMFLAG);46		return hour == 12 ? 12 : 12 + hour;47	}48 49	hour = bcd2bin(hour);50	return hour == 12 ? 0 : hour;51}52 53static uint8_t rtc5t619_12hour_bin2bcd(uint8_t hour)54{55	if (!hour)56		return 0x12;57 58	if (hour < 12)59		return bin2bcd(hour);60 61	if (hour == 12)62		return 0x12 | HOUR_PMFLAG;63 64	return bin2bcd(hour - 12) | HOUR_PMFLAG;65}66 67static int rc5t619_rtc_periodic_disable(struct device *dev)68{69	struct rc5t619_rtc *rtc = dev_get_drvdata(dev);70	int err;71 72	/* disable function */73	err = regmap_update_bits(rtc->rn5t618->regmap,74				 RN5T618_RTC_CTRL1, CTRL1_PERIODIC_MASK, 0);75	if (err < 0)76		return err;77 78	/* clear alarm flag and CTFG */79	err = regmap_update_bits(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2,80				 CTRL2_ALARM_STATUS | CTRL2_CTFG | CTRL2_CTC,81				 0);82	if (err < 0)83		return err;84 85	return 0;86}87 88/* things to be done once after power on */89static int rc5t619_rtc_pon_setup(struct device *dev)90{91	struct rc5t619_rtc *rtc = dev_get_drvdata(dev);92	int err;93	unsigned int reg_data;94 95	err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, &reg_data);96	if (err < 0)97		return err;98 99	/* clear VDET PON */100	reg_data &= ~(CTRL2_PON | CTRL2_CTC | 0x4a);	/* 0101-1011 */101	reg_data |= 0x20;	/* 0010-0000 */102	err = regmap_write(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, reg_data);103	if (err < 0)104		return err;105 106	/* clearing RTC Adjust register */107	err = regmap_write(rtc->rn5t618->regmap, RN5T618_RTC_ADJUST, 0);108	if (err)109		return err;110 111	return regmap_update_bits(rtc->rn5t618->regmap,112					RN5T618_RTC_CTRL1,113					CTRL1_24HR, CTRL1_24HR);114}115 116static int rc5t619_rtc_read_time(struct device *dev, struct rtc_time *tm)117{118	struct rc5t619_rtc *rtc = dev_get_drvdata(dev);119	u8 buff[7];120	int err;121	int cent_flag;122	unsigned int ctrl1;123	unsigned int ctrl2;124 125	err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, &ctrl2);126	if (err < 0)127		return err;128 129	if (ctrl2 & CTRL2_PON)130		return -EINVAL;131 132	err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL1, &ctrl1);133	if (err < 0)134		return err;135 136	err = regmap_bulk_read(rtc->rn5t618->regmap, RN5T618_RTC_SECONDS,137			       buff, sizeof(buff));138	if (err < 0)139		return err;140 141	if (buff[5] & MONTH_CENTFLAG)142		cent_flag = 1;143	else144		cent_flag = 0;145 146	tm->tm_sec  = bcd2bin(buff[0]);147	tm->tm_min  = bcd2bin(buff[1]);148 149	if (ctrl1 & CTRL1_24HR)150		tm->tm_hour = bcd2bin(buff[2]);151	else152		tm->tm_hour = rtc5t619_12hour_bcd2bin(buff[2]);153 154	tm->tm_wday = bcd2bin(buff[3]);155	tm->tm_mday = bcd2bin(buff[4]);156	tm->tm_mon  = bcd2bin(buff[5] & 0x1f) - 1; /* back to system 0-11 */157	tm->tm_year = bcd2bin(buff[6]) + 100 * cent_flag;158 159	return 0;160}161 162static int rc5t619_rtc_set_time(struct device *dev, struct rtc_time *tm)163{164	struct rc5t619_rtc *rtc = dev_get_drvdata(dev);165	u8 buff[7];166	int err;167	int cent_flag;168	unsigned int ctrl1;169	unsigned int ctrl2;170 171	err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, &ctrl2);172	if (err < 0)173		return err;174 175	if (ctrl2 & CTRL2_PON)176		rc5t619_rtc_pon_setup(dev);177 178	err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL1, &ctrl1);179	if (err < 0)180		return err;181 182	if (tm->tm_year >= 100)183		cent_flag = 1;184	else185		cent_flag = 0;186 187	buff[0] = bin2bcd(tm->tm_sec);188	buff[1] = bin2bcd(tm->tm_min);189 190	if (ctrl1 & CTRL1_24HR)191		buff[2] = bin2bcd(tm->tm_hour);192	else193		buff[2] = rtc5t619_12hour_bin2bcd(tm->tm_hour);194 195	buff[3] = bin2bcd(tm->tm_wday);196	buff[4] = bin2bcd(tm->tm_mday);197	buff[5] = bin2bcd(tm->tm_mon + 1);	/* system set 0-11 */198	buff[6] = bin2bcd(tm->tm_year - cent_flag * 100);199 200	if (cent_flag)201		buff[5] |= MONTH_CENTFLAG;202 203	err = regmap_bulk_write(rtc->rn5t618->regmap, RN5T618_RTC_SECONDS,204				buff, sizeof(buff));205	if (err < 0) {206		dev_err(dev, "failed to program new time: %d\n", err);207		return err;208	}209 210	return 0;211}212 213/* 0-disable, 1-enable */214static int rc5t619_rtc_alarm_enable(struct device *dev, unsigned int enabled)215{216	struct rc5t619_rtc *rtc = dev_get_drvdata(dev);217 218	return regmap_update_bits(rtc->rn5t618->regmap,219			RN5T618_RTC_CTRL1,220			CTRL1_ALARM_ENABLED,221			enabled ? CTRL1_ALARM_ENABLED : 0);222}223 224static int rc5t619_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)225{226	struct rc5t619_rtc *rtc = dev_get_drvdata(dev);227	u8 buff[6];228	unsigned int buff_cent;229	int err;230	int cent_flag;231	unsigned int ctrl1;232 233	err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL1, &ctrl1);234	if (err)235		return err;236 237	err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_MONTH, &buff_cent);238	if (err < 0) {239		dev_err(dev, "failed to read time: %d\n", err);240		return err;241	}242 243	if (buff_cent & MONTH_CENTFLAG)244		cent_flag = 1;245	else246		cent_flag = 0;247 248	err = regmap_bulk_read(rtc->rn5t618->regmap, RN5T618_RTC_ALARM_Y_SEC,249			       buff, sizeof(buff));250	if (err)251		return err;252 253	buff[3] = buff[3] & 0x3f;254 255	alrm->time.tm_sec  = bcd2bin(buff[0]);256	alrm->time.tm_min  = bcd2bin(buff[1]);257 258	if (ctrl1 & CTRL1_24HR)259		alrm->time.tm_hour = bcd2bin(buff[2]);260	else261		alrm->time.tm_hour = rtc5t619_12hour_bcd2bin(buff[2]);262 263	alrm->time.tm_mday = bcd2bin(buff[3]);264	alrm->time.tm_mon  = bcd2bin(buff[4]) - 1;265	alrm->time.tm_year = bcd2bin(buff[5]) + 100 * cent_flag;266	alrm->enabled = !!(ctrl1 & CTRL1_ALARM_ENABLED);267	dev_dbg(dev, "read alarm: %ptR\n", &alrm->time);268 269	return 0;270}271 272static int rc5t619_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)273{274	struct rc5t619_rtc *rtc = dev_get_drvdata(dev);275	u8 buff[6];276	int err;277	int cent_flag;278	unsigned int ctrl1;279 280	err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL1, &ctrl1);281	if (err)282		return err;283 284	err = rc5t619_rtc_alarm_enable(dev, 0);285	if (err < 0)286		return err;287 288	if (rtc->irq == -1)289		return -EINVAL;290 291	if (alrm->enabled == 0)292		return 0;293 294	if (alrm->time.tm_year >= 100)295		cent_flag = 1;296	else297		cent_flag = 0;298 299	alrm->time.tm_mon += 1;300	buff[0] = bin2bcd(alrm->time.tm_sec);301	buff[1] = bin2bcd(alrm->time.tm_min);302 303	if (ctrl1 & CTRL1_24HR)304		buff[2] = bin2bcd(alrm->time.tm_hour);305	else306		buff[2] = rtc5t619_12hour_bin2bcd(alrm->time.tm_hour);307 308	buff[3] = bin2bcd(alrm->time.tm_mday);309	buff[4] = bin2bcd(alrm->time.tm_mon);310	buff[5] = bin2bcd(alrm->time.tm_year - 100 * cent_flag);311	buff[3] |= MDAY_DAL_EXT;312 313	err = regmap_bulk_write(rtc->rn5t618->regmap, RN5T618_RTC_ALARM_Y_SEC,314				buff, sizeof(buff));315	if (err < 0)316		return err;317 318	return rc5t619_rtc_alarm_enable(dev, alrm->enabled);319}320 321static const struct rtc_class_ops rc5t619_rtc_ops = {322	.read_time	= rc5t619_rtc_read_time,323	.set_time	= rc5t619_rtc_set_time,324	.set_alarm	= rc5t619_rtc_set_alarm,325	.read_alarm	= rc5t619_rtc_read_alarm,326	.alarm_irq_enable = rc5t619_rtc_alarm_enable,327};328 329static int rc5t619_rtc_alarm_flag_clr(struct device *dev)330{331	struct rc5t619_rtc *rtc = dev_get_drvdata(dev);332 333	/* clear alarm-D status bits.*/334	return regmap_update_bits(rtc->rn5t618->regmap,335				RN5T618_RTC_CTRL2,336				CTRL2_ALARM_STATUS | CTRL2_CTC, 0);337}338 339static irqreturn_t rc5t619_rtc_irq(int irq, void *data)340{341	struct device *dev = data;342	struct rc5t619_rtc *rtc = dev_get_drvdata(dev);343 344	rc5t619_rtc_alarm_flag_clr(dev);345 346	rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);347	return IRQ_HANDLED;348}349 350static int rc5t619_rtc_probe(struct platform_device *pdev)351{352	struct device *dev = &pdev->dev;353	struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent);354	struct rc5t619_rtc *rtc;355	unsigned int ctrl2;356	int err;357 358	rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);359	if (!rtc)360		return -ENOMEM;361 362	rtc->rn5t618 = rn5t618;363 364	dev_set_drvdata(dev, rtc);365	rtc->irq = -1;366 367	if (rn5t618->irq_data)368		rtc->irq = regmap_irq_get_virq(rn5t618->irq_data,369					       RN5T618_IRQ_RTC);370 371	if (rtc->irq  < 0)372		rtc->irq = -1;373 374	err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, &ctrl2);375	if (err < 0)376		return err;377 378	/* disable rtc periodic function */379	err = rc5t619_rtc_periodic_disable(&pdev->dev);380	if (err)381		return err;382 383	if (ctrl2 & CTRL2_PON) {384		err = rc5t619_rtc_alarm_flag_clr(&pdev->dev);385		if (err)386			return err;387	}388 389	rtc->rtc = devm_rtc_allocate_device(&pdev->dev);390	if (IS_ERR(rtc->rtc)) {391		err = PTR_ERR(rtc->rtc);392		dev_err(dev, "RTC device register: err %d\n", err);393		return err;394	}395 396	rtc->rtc->ops = &rc5t619_rtc_ops;397	rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_1900;398	rtc->rtc->range_max = RTC_TIMESTAMP_END_2099;399 400	/* set interrupt and enable it */401	if (rtc->irq != -1) {402		err = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,403						rc5t619_rtc_irq,404						IRQF_ONESHOT,405						"rtc-rc5t619",406						&pdev->dev);407		if (err < 0) {408			dev_err(&pdev->dev, "request IRQ:%d fail\n", rtc->irq);409			rtc->irq = -1;410 411			err = rc5t619_rtc_alarm_enable(&pdev->dev, 0);412			if (err)413				return err;414 415		} else {416			/* enable wake */417			device_init_wakeup(&pdev->dev, 1);418			enable_irq_wake(rtc->irq);419		}420	} else {421		/* system don't want to using alarm interrupt, so close it */422		err = rc5t619_rtc_alarm_enable(&pdev->dev, 0);423		if (err)424			return err;425 426		dev_warn(&pdev->dev, "rc5t619 interrupt is disabled\n");427	}428 429	return devm_rtc_register_device(rtc->rtc);430}431 432static const struct platform_device_id rc5t619_rtc_id[] = {433	{434		.name = "rc5t619-rtc",435	}, {436		/* sentinel */437	}438};439MODULE_DEVICE_TABLE(platform, rc5t619_rtc_id);440 441static struct platform_driver rc5t619_rtc_driver = {442	.driver	= {443		.name	= "rc5t619-rtc",444	},445	.probe	= rc5t619_rtc_probe,446	.id_table = rc5t619_rtc_id,447};448module_platform_driver(rc5t619_rtc_driver);449 450MODULE_DESCRIPTION("RICOH RC5T619 RTC driver");451MODULE_LICENSE("GPL");452