Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  Oki MSM6242 RTC Driver
0004  *
0005  *  Copyright 2009 Geert Uytterhoeven
0006  *
0007  *  Based on the A2000 TOD code in arch/m68k/amiga/config.c
0008  *  Copyright (C) 1993 Hamish Macdonald
0009  */
0010 
0011 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0012 
0013 #include <linux/delay.h>
0014 #include <linux/io.h>
0015 #include <linux/kernel.h>
0016 #include <linux/module.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/rtc.h>
0019 #include <linux/slab.h>
0020 
0021 
0022 enum {
0023     MSM6242_SECOND1     = 0x0,  /* 1-second digit register */
0024     MSM6242_SECOND10    = 0x1,  /* 10-second digit register */
0025     MSM6242_MINUTE1     = 0x2,  /* 1-minute digit register */
0026     MSM6242_MINUTE10    = 0x3,  /* 10-minute digit register */
0027     MSM6242_HOUR1       = 0x4,  /* 1-hour digit register */
0028     MSM6242_HOUR10      = 0x5,  /* PM/AM, 10-hour digit register */
0029     MSM6242_DAY1        = 0x6,  /* 1-day digit register */
0030     MSM6242_DAY10       = 0x7,  /* 10-day digit register */
0031     MSM6242_MONTH1      = 0x8,  /* 1-month digit register */
0032     MSM6242_MONTH10     = 0x9,  /* 10-month digit register */
0033     MSM6242_YEAR1       = 0xa,  /* 1-year digit register */
0034     MSM6242_YEAR10      = 0xb,  /* 10-year digit register */
0035     MSM6242_WEEK        = 0xc,  /* Week register */
0036     MSM6242_CD      = 0xd,  /* Control Register D */
0037     MSM6242_CE      = 0xe,  /* Control Register E */
0038     MSM6242_CF      = 0xf,  /* Control Register F */
0039 };
0040 
0041 #define MSM6242_HOUR10_AM   (0 << 2)
0042 #define MSM6242_HOUR10_PM   (1 << 2)
0043 #define MSM6242_HOUR10_HR_MASK  (3 << 0)
0044 
0045 #define MSM6242_WEEK_SUNDAY 0
0046 #define MSM6242_WEEK_MONDAY 1
0047 #define MSM6242_WEEK_TUESDAY    2
0048 #define MSM6242_WEEK_WEDNESDAY  3
0049 #define MSM6242_WEEK_THURSDAY   4
0050 #define MSM6242_WEEK_FRIDAY 5
0051 #define MSM6242_WEEK_SATURDAY   6
0052 
0053 #define MSM6242_CD_30_S_ADJ (1 << 3)    /* 30-second adjustment */
0054 #define MSM6242_CD_IRQ_FLAG (1 << 2)
0055 #define MSM6242_CD_BUSY     (1 << 1)
0056 #define MSM6242_CD_HOLD     (1 << 0)
0057 
0058 #define MSM6242_CE_T_MASK   (3 << 2)
0059 #define MSM6242_CE_T_64HZ   (0 << 2)    /* period 1/64 second */
0060 #define MSM6242_CE_T_1HZ    (1 << 2)    /* period 1 second */
0061 #define MSM6242_CE_T_1MINUTE    (2 << 2)    /* period 1 minute */
0062 #define MSM6242_CE_T_1HOUR  (3 << 2)    /* period 1 hour */
0063 
0064 #define MSM6242_CE_ITRPT_STND   (1 << 1)
0065 #define MSM6242_CE_MASK     (1 << 0)    /* STD.P output control */
0066 
0067 #define MSM6242_CF_TEST     (1 << 3)
0068 #define MSM6242_CF_12H      (0 << 2)
0069 #define MSM6242_CF_24H      (1 << 2)
0070 #define MSM6242_CF_STOP     (1 << 1)
0071 #define MSM6242_CF_REST     (1 << 0)    /* reset */
0072 
0073 
0074 struct msm6242_priv {
0075     u32 __iomem *regs;
0076     struct rtc_device *rtc;
0077 };
0078 
0079 static inline unsigned int msm6242_read(struct msm6242_priv *priv,
0080                        unsigned int reg)
0081 {
0082     return __raw_readl(&priv->regs[reg]) & 0xf;
0083 }
0084 
0085 static inline void msm6242_write(struct msm6242_priv *priv, unsigned int val,
0086                 unsigned int reg)
0087 {
0088     __raw_writel(val, &priv->regs[reg]);
0089 }
0090 
0091 static void msm6242_lock(struct msm6242_priv *priv)
0092 {
0093     int cnt = 5;
0094 
0095     msm6242_write(priv, MSM6242_CD_HOLD|MSM6242_CD_IRQ_FLAG, MSM6242_CD);
0096 
0097     while ((msm6242_read(priv, MSM6242_CD) & MSM6242_CD_BUSY) && cnt) {
0098         msm6242_write(priv, MSM6242_CD_IRQ_FLAG, MSM6242_CD);
0099         udelay(70);
0100         msm6242_write(priv, MSM6242_CD_HOLD|MSM6242_CD_IRQ_FLAG, MSM6242_CD);
0101         cnt--;
0102     }
0103 
0104     if (!cnt)
0105         pr_warn("timed out waiting for RTC (0x%x)\n",
0106             msm6242_read(priv, MSM6242_CD));
0107 }
0108 
0109 static void msm6242_unlock(struct msm6242_priv *priv)
0110 {
0111     msm6242_write(priv, MSM6242_CD_IRQ_FLAG, MSM6242_CD);
0112 }
0113 
0114 static int msm6242_read_time(struct device *dev, struct rtc_time *tm)
0115 {
0116     struct msm6242_priv *priv = dev_get_drvdata(dev);
0117 
0118     msm6242_lock(priv);
0119 
0120     tm->tm_sec  = msm6242_read(priv, MSM6242_SECOND10) * 10 +
0121               msm6242_read(priv, MSM6242_SECOND1);
0122     tm->tm_min  = msm6242_read(priv, MSM6242_MINUTE10) * 10 +
0123               msm6242_read(priv, MSM6242_MINUTE1);
0124     tm->tm_hour = (msm6242_read(priv, MSM6242_HOUR10) &
0125                MSM6242_HOUR10_HR_MASK) * 10 +
0126               msm6242_read(priv, MSM6242_HOUR1);
0127     tm->tm_mday = msm6242_read(priv, MSM6242_DAY10) * 10 +
0128               msm6242_read(priv, MSM6242_DAY1);
0129     tm->tm_wday = msm6242_read(priv, MSM6242_WEEK);
0130     tm->tm_mon  = msm6242_read(priv, MSM6242_MONTH10) * 10 +
0131               msm6242_read(priv, MSM6242_MONTH1) - 1;
0132     tm->tm_year = msm6242_read(priv, MSM6242_YEAR10) * 10 +
0133               msm6242_read(priv, MSM6242_YEAR1);
0134     if (tm->tm_year <= 69)
0135         tm->tm_year += 100;
0136 
0137     if (!(msm6242_read(priv, MSM6242_CF) & MSM6242_CF_24H)) {
0138         unsigned int pm = msm6242_read(priv, MSM6242_HOUR10) &
0139                   MSM6242_HOUR10_PM;
0140         if (!pm && tm->tm_hour == 12)
0141             tm->tm_hour = 0;
0142         else if (pm && tm->tm_hour != 12)
0143             tm->tm_hour += 12;
0144     }
0145 
0146     msm6242_unlock(priv);
0147 
0148     return 0;
0149 }
0150 
0151 static int msm6242_set_time(struct device *dev, struct rtc_time *tm)
0152 {
0153     struct msm6242_priv *priv = dev_get_drvdata(dev);
0154 
0155     msm6242_lock(priv);
0156 
0157     msm6242_write(priv, tm->tm_sec / 10, MSM6242_SECOND10);
0158     msm6242_write(priv, tm->tm_sec % 10, MSM6242_SECOND1);
0159     msm6242_write(priv, tm->tm_min / 10, MSM6242_MINUTE10);
0160     msm6242_write(priv, tm->tm_min % 10, MSM6242_MINUTE1);
0161     if (msm6242_read(priv, MSM6242_CF) & MSM6242_CF_24H)
0162         msm6242_write(priv, tm->tm_hour / 10, MSM6242_HOUR10);
0163     else if (tm->tm_hour >= 12)
0164         msm6242_write(priv, MSM6242_HOUR10_PM + (tm->tm_hour - 12) / 10,
0165                   MSM6242_HOUR10);
0166     else
0167         msm6242_write(priv, tm->tm_hour / 10, MSM6242_HOUR10);
0168     msm6242_write(priv, tm->tm_hour % 10, MSM6242_HOUR1);
0169     msm6242_write(priv, tm->tm_mday / 10, MSM6242_DAY10);
0170     msm6242_write(priv, tm->tm_mday % 10, MSM6242_DAY1);
0171     if (tm->tm_wday != -1)
0172         msm6242_write(priv, tm->tm_wday, MSM6242_WEEK);
0173     msm6242_write(priv, (tm->tm_mon + 1) / 10, MSM6242_MONTH10);
0174     msm6242_write(priv, (tm->tm_mon + 1) % 10, MSM6242_MONTH1);
0175     if (tm->tm_year >= 100)
0176         tm->tm_year -= 100;
0177     msm6242_write(priv, tm->tm_year / 10, MSM6242_YEAR10);
0178     msm6242_write(priv, tm->tm_year % 10, MSM6242_YEAR1);
0179 
0180     msm6242_unlock(priv);
0181     return 0;
0182 }
0183 
0184 static const struct rtc_class_ops msm6242_rtc_ops = {
0185     .read_time  = msm6242_read_time,
0186     .set_time   = msm6242_set_time,
0187 };
0188 
0189 static int __init msm6242_rtc_probe(struct platform_device *pdev)
0190 {
0191     struct resource *res;
0192     struct msm6242_priv *priv;
0193     struct rtc_device *rtc;
0194 
0195     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0196     if (!res)
0197         return -ENODEV;
0198 
0199     priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0200     if (!priv)
0201         return -ENOMEM;
0202 
0203     priv->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
0204     if (!priv->regs)
0205         return -ENOMEM;
0206     platform_set_drvdata(pdev, priv);
0207 
0208     rtc = devm_rtc_device_register(&pdev->dev, "rtc-msm6242",
0209                 &msm6242_rtc_ops, THIS_MODULE);
0210     if (IS_ERR(rtc))
0211         return PTR_ERR(rtc);
0212 
0213     priv->rtc = rtc;
0214     return 0;
0215 }
0216 
0217 static struct platform_driver msm6242_rtc_driver = {
0218     .driver = {
0219         .name   = "rtc-msm6242",
0220     },
0221 };
0222 
0223 module_platform_driver_probe(msm6242_rtc_driver, msm6242_rtc_probe);
0224 
0225 MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
0226 MODULE_LICENSE("GPL");
0227 MODULE_DESCRIPTION("Oki MSM6242 RTC driver");
0228 MODULE_ALIAS("platform:rtc-msm6242");