linux/drivers/rtc/rtc-fsl-ftm-alarm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Freescale FlexTimer Module (FTM) alarm device driver.
   4 *
   5 * Copyright 2014 Freescale Semiconductor, Inc.
   6 * Copyright 2019-2020 NXP
   7 *
   8 */
   9
  10#include <linux/device.h>
  11#include <linux/err.h>
  12#include <linux/interrupt.h>
  13#include <linux/io.h>
  14#include <linux/of_address.h>
  15#include <linux/of_irq.h>
  16#include <linux/platform_device.h>
  17#include <linux/of.h>
  18#include <linux/of_device.h>
  19#include <linux/module.h>
  20#include <linux/fsl/ftm.h>
  21#include <linux/rtc.h>
  22#include <linux/time.h>
  23#include <linux/acpi.h>
  24#include <linux/pm_wakeirq.h>
  25
  26#define FTM_SC_CLK(c)           ((c) << FTM_SC_CLK_MASK_SHIFT)
  27
  28/*
  29 * Select Fixed frequency clock (32KHz) as clock source
  30 * of FlexTimer Module
  31 */
  32#define FTM_SC_CLKS_FIXED_FREQ  0x02
  33#define FIXED_FREQ_CLK          32000
  34
  35/* Select 128 (2^7) as divider factor */
  36#define MAX_FREQ_DIV            (1 << FTM_SC_PS_MASK)
  37
  38/* Maximum counter value in FlexTimer's CNT registers */
  39#define MAX_COUNT_VAL           0xffff
  40
  41struct ftm_rtc {
  42        struct rtc_device *rtc_dev;
  43        void __iomem *base;
  44        bool big_endian;
  45        u32 alarm_freq;
  46};
  47
  48static inline u32 rtc_readl(struct ftm_rtc *dev, u32 reg)
  49{
  50        if (dev->big_endian)
  51                return ioread32be(dev->base + reg);
  52        else
  53                return ioread32(dev->base + reg);
  54}
  55
  56static inline void rtc_writel(struct ftm_rtc *dev, u32 reg, u32 val)
  57{
  58        if (dev->big_endian)
  59                iowrite32be(val, dev->base + reg);
  60        else
  61                iowrite32(val, dev->base + reg);
  62}
  63
  64static inline void ftm_counter_enable(struct ftm_rtc *rtc)
  65{
  66        u32 val;
  67
  68        /* select and enable counter clock source */
  69        val = rtc_readl(rtc, FTM_SC);
  70        val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
  71        val |= (FTM_SC_PS_MASK | FTM_SC_CLK(FTM_SC_CLKS_FIXED_FREQ));
  72        rtc_writel(rtc, FTM_SC, val);
  73}
  74
  75static inline void ftm_counter_disable(struct ftm_rtc *rtc)
  76{
  77        u32 val;
  78
  79        /* disable counter clock source */
  80        val = rtc_readl(rtc, FTM_SC);
  81        val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
  82        rtc_writel(rtc, FTM_SC, val);
  83}
  84
  85static inline void ftm_irq_acknowledge(struct ftm_rtc *rtc)
  86{
  87        unsigned int timeout = 100;
  88
  89        /*
  90         *Fix errata A-007728 for flextimer
  91         *      If the FTM counter reaches the FTM_MOD value between
  92         *      the reading of the TOF bit and the writing of 0 to
  93         *      the TOF bit, the process of clearing the TOF bit
  94         *      does not work as expected when FTMx_CONF[NUMTOF] != 0
  95         *      and the current TOF count is less than FTMx_CONF[NUMTOF].
  96         *      If the above condition is met, the TOF bit remains set.
  97         *      If the TOF interrupt is enabled (FTMx_SC[TOIE] = 1),the
  98         *      TOF interrupt also remains asserted.
  99         *
 100         *      Above is the errata discription
 101         *
 102         *      In one word: software clearing TOF bit not works when
 103         *      FTMx_CONF[NUMTOF] was seted as nonzero and FTM counter
 104         *      reaches the FTM_MOD value.
 105         *
 106         *      The workaround is clearing TOF bit until it works
 107         *      (FTM counter doesn't always reache the FTM_MOD anyway),
 108         *      which may cost some cycles.
 109         */
 110        while ((FTM_SC_TOF & rtc_readl(rtc, FTM_SC)) && timeout--)
 111                rtc_writel(rtc, FTM_SC, rtc_readl(rtc, FTM_SC) & (~FTM_SC_TOF));
 112}
 113
 114static inline void ftm_irq_enable(struct ftm_rtc *rtc)
 115{
 116        u32 val;
 117
 118        val = rtc_readl(rtc, FTM_SC);
 119        val |= FTM_SC_TOIE;
 120        rtc_writel(rtc, FTM_SC, val);
 121}
 122
 123static inline void ftm_irq_disable(struct ftm_rtc *rtc)
 124{
 125        u32 val;
 126
 127        val = rtc_readl(rtc, FTM_SC);
 128        val &= ~FTM_SC_TOIE;
 129        rtc_writel(rtc, FTM_SC, val);
 130}
 131
 132static inline void ftm_reset_counter(struct ftm_rtc *rtc)
 133{
 134        /*
 135         * The CNT register contains the FTM counter value.
 136         * Reset clears the CNT register. Writing any value to COUNT
 137         * updates the counter with its initial value, CNTIN.
 138         */
 139        rtc_writel(rtc, FTM_CNT, 0x00);
 140}
 141
 142static void ftm_clean_alarm(struct ftm_rtc *rtc)
 143{
 144        ftm_counter_disable(rtc);
 145
 146        rtc_writel(rtc, FTM_CNTIN, 0x00);
 147        rtc_writel(rtc, FTM_MOD, ~0U);
 148
 149        ftm_reset_counter(rtc);
 150}
 151
 152static irqreturn_t ftm_rtc_alarm_interrupt(int irq, void *dev)
 153{
 154        struct ftm_rtc *rtc = dev;
 155
 156        rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
 157
 158        ftm_irq_acknowledge(rtc);
 159        ftm_irq_disable(rtc);
 160        ftm_clean_alarm(rtc);
 161
 162        return IRQ_HANDLED;
 163}
 164
 165static int ftm_rtc_alarm_irq_enable(struct device *dev,
 166                unsigned int enabled)
 167{
 168        struct ftm_rtc *rtc = dev_get_drvdata(dev);
 169
 170        if (enabled)
 171                ftm_irq_enable(rtc);
 172        else
 173                ftm_irq_disable(rtc);
 174
 175        return 0;
 176}
 177
 178/*
 179 * Note:
 180 *      The function is not really getting time from the RTC
 181 *      since FlexTimer is not a RTC device, but we need to
 182 *      get time to setup alarm, so we are using system time
 183 *      for now.
 184 */
 185static int ftm_rtc_read_time(struct device *dev, struct rtc_time *tm)
 186{
 187        rtc_time64_to_tm(ktime_get_real_seconds(), tm);
 188
 189        return 0;
 190}
 191
 192static int ftm_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
 193{
 194        return 0;
 195}
 196
 197/*
 198 * 1. Select fixed frequency clock (32KHz) as clock source;
 199 * 2. Select 128 (2^7) as divider factor;
 200 * So clock is 250 Hz (32KHz/128).
 201 *
 202 * 3. FlexTimer's CNT register is a 32bit register,
 203 * but the register's 16 bit as counter value,it's other 16 bit
 204 * is reserved.So minimum counter value is 0x0,maximum counter
 205 * value is 0xffff.
 206 * So max alarm value is 262 (65536 / 250) seconds
 207 */
 208static int ftm_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
 209{
 210        time64_t alm_time;
 211        unsigned long long cycle;
 212        struct ftm_rtc *rtc = dev_get_drvdata(dev);
 213
 214        alm_time = rtc_tm_to_time64(&alm->time);
 215
 216        ftm_clean_alarm(rtc);
 217        cycle = (alm_time - ktime_get_real_seconds()) * rtc->alarm_freq;
 218        if (cycle > MAX_COUNT_VAL) {
 219                pr_err("Out of alarm range {0~262} seconds.\n");
 220                return -ERANGE;
 221        }
 222
 223        ftm_irq_disable(rtc);
 224
 225        /*
 226         * The counter increments until the value of MOD is reached,
 227         * at which point the counter is reloaded with the value of CNTIN.
 228         * The TOF (the overflow flag) bit is set when the FTM counter
 229         * changes from MOD to CNTIN. So we should using the cycle - 1.
 230         */
 231        rtc_writel(rtc, FTM_MOD, cycle - 1);
 232
 233        ftm_counter_enable(rtc);
 234        ftm_irq_enable(rtc);
 235
 236        return 0;
 237
 238}
 239
 240static const struct rtc_class_ops ftm_rtc_ops = {
 241        .read_time              = ftm_rtc_read_time,
 242        .read_alarm             = ftm_rtc_read_alarm,
 243        .set_alarm              = ftm_rtc_set_alarm,
 244        .alarm_irq_enable       = ftm_rtc_alarm_irq_enable,
 245};
 246
 247static int ftm_rtc_probe(struct platform_device *pdev)
 248{
 249        int irq;
 250        int ret;
 251        struct ftm_rtc *rtc;
 252
 253        rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
 254        if (unlikely(!rtc)) {
 255                dev_err(&pdev->dev, "cannot alloc memory for rtc\n");
 256                return -ENOMEM;
 257        }
 258
 259        platform_set_drvdata(pdev, rtc);
 260
 261        rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
 262        if (IS_ERR(rtc->rtc_dev))
 263                return PTR_ERR(rtc->rtc_dev);
 264
 265        rtc->base = devm_platform_ioremap_resource(pdev, 0);
 266        if (IS_ERR(rtc->base)) {
 267                dev_err(&pdev->dev, "cannot ioremap resource for rtc\n");
 268                return PTR_ERR(rtc->base);
 269        }
 270
 271        irq = platform_get_irq(pdev, 0);
 272        if (irq < 0)
 273                return irq;
 274
 275        ret = devm_request_irq(&pdev->dev, irq, ftm_rtc_alarm_interrupt,
 276                               0, dev_name(&pdev->dev), rtc);
 277        if (ret < 0) {
 278                dev_err(&pdev->dev, "failed to request irq\n");
 279                return ret;
 280        }
 281
 282        rtc->big_endian =
 283                device_property_read_bool(&pdev->dev, "big-endian");
 284
 285        rtc->alarm_freq = (u32)FIXED_FREQ_CLK / (u32)MAX_FREQ_DIV;
 286        rtc->rtc_dev->ops = &ftm_rtc_ops;
 287
 288        device_init_wakeup(&pdev->dev, true);
 289        ret = dev_pm_set_wake_irq(&pdev->dev, irq);
 290        if (ret)
 291                dev_err(&pdev->dev, "failed to enable irq wake\n");
 292
 293        ret = devm_rtc_register_device(rtc->rtc_dev);
 294        if (ret) {
 295                dev_err(&pdev->dev, "can't register rtc device\n");
 296                return ret;
 297        }
 298
 299        return 0;
 300}
 301
 302static const struct of_device_id ftm_rtc_match[] = {
 303        { .compatible = "fsl,ls1012a-ftm-alarm", },
 304        { .compatible = "fsl,ls1021a-ftm-alarm", },
 305        { .compatible = "fsl,ls1028a-ftm-alarm", },
 306        { .compatible = "fsl,ls1043a-ftm-alarm", },
 307        { .compatible = "fsl,ls1046a-ftm-alarm", },
 308        { .compatible = "fsl,ls1088a-ftm-alarm", },
 309        { .compatible = "fsl,ls208xa-ftm-alarm", },
 310        { .compatible = "fsl,lx2160a-ftm-alarm", },
 311        { },
 312};
 313MODULE_DEVICE_TABLE(of, ftm_rtc_match);
 314
 315static const struct acpi_device_id ftm_imx_acpi_ids[] = {
 316        {"NXP0014",},
 317        { }
 318};
 319MODULE_DEVICE_TABLE(acpi, ftm_imx_acpi_ids);
 320
 321static struct platform_driver ftm_rtc_driver = {
 322        .probe          = ftm_rtc_probe,
 323        .driver         = {
 324                .name   = "ftm-alarm",
 325                .of_match_table = ftm_rtc_match,
 326                .acpi_match_table = ACPI_PTR(ftm_imx_acpi_ids),
 327        },
 328};
 329
 330static int __init ftm_alarm_init(void)
 331{
 332        return platform_driver_register(&ftm_rtc_driver);
 333}
 334
 335device_initcall(ftm_alarm_init);
 336
 337MODULE_DESCRIPTION("NXP/Freescale FlexTimer alarm driver");
 338MODULE_AUTHOR("Biwen Li <biwen.li@nxp.com>");
 339MODULE_LICENSE("GPL");
 340