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