brintos

brintos / linux-shallow public Read only

0
0
Text · 6.6 KiB · 80e364b Raw
229 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *  Oki MSM6242 RTC Driver4 *5 *  Copyright 2009 Geert Uytterhoeven6 *7 *  Based on the A2000 TOD code in arch/m68k/amiga/config.c8 *  Copyright (C) 1993 Hamish Macdonald9 */10 11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt12 13#include <linux/delay.h>14#include <linux/io.h>15#include <linux/kernel.h>16#include <linux/module.h>17#include <linux/platform_device.h>18#include <linux/rtc.h>19#include <linux/slab.h>20 21 22enum {23	MSM6242_SECOND1		= 0x0,	/* 1-second digit register */24	MSM6242_SECOND10	= 0x1,	/* 10-second digit register */25	MSM6242_MINUTE1		= 0x2,	/* 1-minute digit register */26	MSM6242_MINUTE10	= 0x3,	/* 10-minute digit register */27	MSM6242_HOUR1		= 0x4,	/* 1-hour digit register */28	MSM6242_HOUR10		= 0x5,	/* PM/AM, 10-hour digit register */29	MSM6242_DAY1		= 0x6,	/* 1-day digit register */30	MSM6242_DAY10		= 0x7,	/* 10-day digit register */31	MSM6242_MONTH1		= 0x8,	/* 1-month digit register */32	MSM6242_MONTH10		= 0x9,	/* 10-month digit register */33	MSM6242_YEAR1		= 0xa,	/* 1-year digit register */34	MSM6242_YEAR10		= 0xb,	/* 10-year digit register */35	MSM6242_WEEK		= 0xc,	/* Week register */36	MSM6242_CD		= 0xd,	/* Control Register D */37	MSM6242_CE		= 0xe,	/* Control Register E */38	MSM6242_CF		= 0xf,	/* Control Register F */39};40 41#define MSM6242_HOUR10_AM	(0 << 2)42#define MSM6242_HOUR10_PM	(1 << 2)43#define MSM6242_HOUR10_HR_MASK	(3 << 0)44 45#define MSM6242_WEEK_SUNDAY	046#define MSM6242_WEEK_MONDAY	147#define MSM6242_WEEK_TUESDAY	248#define MSM6242_WEEK_WEDNESDAY	349#define MSM6242_WEEK_THURSDAY	450#define MSM6242_WEEK_FRIDAY	551#define MSM6242_WEEK_SATURDAY	652 53#define MSM6242_CD_30_S_ADJ	(1 << 3)	/* 30-second adjustment */54#define MSM6242_CD_IRQ_FLAG	(1 << 2)55#define MSM6242_CD_BUSY		(1 << 1)56#define MSM6242_CD_HOLD		(1 << 0)57 58#define MSM6242_CE_T_MASK	(3 << 2)59#define MSM6242_CE_T_64HZ	(0 << 2)	/* period 1/64 second */60#define MSM6242_CE_T_1HZ	(1 << 2)	/* period 1 second */61#define MSM6242_CE_T_1MINUTE	(2 << 2)	/* period 1 minute */62#define MSM6242_CE_T_1HOUR	(3 << 2)	/* period 1 hour */63 64#define MSM6242_CE_ITRPT_STND	(1 << 1)65#define MSM6242_CE_MASK		(1 << 0)	/* STD.P output control */66 67#define MSM6242_CF_TEST		(1 << 3)68#define MSM6242_CF_12H		(0 << 2)69#define MSM6242_CF_24H		(1 << 2)70#define MSM6242_CF_STOP		(1 << 1)71#define MSM6242_CF_REST		(1 << 0)	/* reset */72 73 74struct msm6242_priv {75	u32 __iomem *regs;76	struct rtc_device *rtc;77};78 79static inline unsigned int msm6242_read(struct msm6242_priv *priv,80				       unsigned int reg)81{82	return __raw_readl(&priv->regs[reg]) & 0xf;83}84 85static inline void msm6242_write(struct msm6242_priv *priv, unsigned int val,86				unsigned int reg)87{88	__raw_writel(val, &priv->regs[reg]);89}90 91static void msm6242_lock(struct msm6242_priv *priv)92{93	int cnt = 5;94 95	msm6242_write(priv, MSM6242_CD_HOLD|MSM6242_CD_IRQ_FLAG, MSM6242_CD);96 97	while ((msm6242_read(priv, MSM6242_CD) & MSM6242_CD_BUSY) && cnt) {98		msm6242_write(priv, MSM6242_CD_IRQ_FLAG, MSM6242_CD);99		udelay(70);100		msm6242_write(priv, MSM6242_CD_HOLD|MSM6242_CD_IRQ_FLAG, MSM6242_CD);101		cnt--;102	}103 104	if (!cnt)105		pr_warn("timed out waiting for RTC (0x%x)\n",106			msm6242_read(priv, MSM6242_CD));107}108 109static void msm6242_unlock(struct msm6242_priv *priv)110{111	msm6242_write(priv, MSM6242_CD_IRQ_FLAG, MSM6242_CD);112}113 114static int msm6242_read_time(struct device *dev, struct rtc_time *tm)115{116	struct msm6242_priv *priv = dev_get_drvdata(dev);117 118	msm6242_lock(priv);119 120	tm->tm_sec  = msm6242_read(priv, MSM6242_SECOND10) * 10 +121		      msm6242_read(priv, MSM6242_SECOND1);122	tm->tm_min  = msm6242_read(priv, MSM6242_MINUTE10) * 10 +123		      msm6242_read(priv, MSM6242_MINUTE1);124	tm->tm_hour = (msm6242_read(priv, MSM6242_HOUR10) &125		       MSM6242_HOUR10_HR_MASK) * 10 +126		      msm6242_read(priv, MSM6242_HOUR1);127	tm->tm_mday = msm6242_read(priv, MSM6242_DAY10) * 10 +128		      msm6242_read(priv, MSM6242_DAY1);129	tm->tm_wday = msm6242_read(priv, MSM6242_WEEK);130	tm->tm_mon  = msm6242_read(priv, MSM6242_MONTH10) * 10 +131		      msm6242_read(priv, MSM6242_MONTH1) - 1;132	tm->tm_year = msm6242_read(priv, MSM6242_YEAR10) * 10 +133		      msm6242_read(priv, MSM6242_YEAR1);134	if (tm->tm_year <= 69)135		tm->tm_year += 100;136 137	if (!(msm6242_read(priv, MSM6242_CF) & MSM6242_CF_24H)) {138		unsigned int pm = msm6242_read(priv, MSM6242_HOUR10) &139				  MSM6242_HOUR10_PM;140		if (!pm && tm->tm_hour == 12)141			tm->tm_hour = 0;142		else if (pm && tm->tm_hour != 12)143			tm->tm_hour += 12;144	}145 146	msm6242_unlock(priv);147 148	return 0;149}150 151static int msm6242_set_time(struct device *dev, struct rtc_time *tm)152{153	struct msm6242_priv *priv = dev_get_drvdata(dev);154 155	msm6242_lock(priv);156 157	msm6242_write(priv, tm->tm_sec / 10, MSM6242_SECOND10);158	msm6242_write(priv, tm->tm_sec % 10, MSM6242_SECOND1);159	msm6242_write(priv, tm->tm_min / 10, MSM6242_MINUTE10);160	msm6242_write(priv, tm->tm_min % 10, MSM6242_MINUTE1);161	if (msm6242_read(priv, MSM6242_CF) & MSM6242_CF_24H)162		msm6242_write(priv, tm->tm_hour / 10, MSM6242_HOUR10);163	else if (tm->tm_hour >= 12)164		msm6242_write(priv, MSM6242_HOUR10_PM + (tm->tm_hour - 12) / 10,165			      MSM6242_HOUR10);166	else167		msm6242_write(priv, tm->tm_hour / 10, MSM6242_HOUR10);168	msm6242_write(priv, tm->tm_hour % 10, MSM6242_HOUR1);169	msm6242_write(priv, tm->tm_mday / 10, MSM6242_DAY10);170	msm6242_write(priv, tm->tm_mday % 10, MSM6242_DAY1);171	if (tm->tm_wday != -1)172		msm6242_write(priv, tm->tm_wday, MSM6242_WEEK);173	msm6242_write(priv, (tm->tm_mon + 1) / 10, MSM6242_MONTH10);174	msm6242_write(priv, (tm->tm_mon + 1) % 10, MSM6242_MONTH1);175	if (tm->tm_year >= 100)176		tm->tm_year -= 100;177	msm6242_write(priv, tm->tm_year / 10, MSM6242_YEAR10);178	msm6242_write(priv, tm->tm_year % 10, MSM6242_YEAR1);179 180	msm6242_unlock(priv);181	return 0;182}183 184static const struct rtc_class_ops msm6242_rtc_ops = {185	.read_time	= msm6242_read_time,186	.set_time	= msm6242_set_time,187};188 189static int __init msm6242_rtc_probe(struct platform_device *pdev)190{191	struct resource *res;192	struct msm6242_priv *priv;193	struct rtc_device *rtc;194 195	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);196	if (!res)197		return -ENODEV;198 199	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);200	if (!priv)201		return -ENOMEM;202 203	priv->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));204	if (!priv->regs)205		return -ENOMEM;206	platform_set_drvdata(pdev, priv);207 208	rtc = devm_rtc_device_register(&pdev->dev, "rtc-msm6242",209				&msm6242_rtc_ops, THIS_MODULE);210	if (IS_ERR(rtc))211		return PTR_ERR(rtc);212 213	priv->rtc = rtc;214	return 0;215}216 217static struct platform_driver msm6242_rtc_driver = {218	.driver	= {219		.name	= "rtc-msm6242",220	},221};222 223module_platform_driver_probe(msm6242_rtc_driver, msm6242_rtc_probe);224 225MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");226MODULE_LICENSE("GPL");227MODULE_DESCRIPTION("Oki MSM6242 RTC driver");228MODULE_ALIAS("platform:rtc-msm6242");229