linux/drivers/rtc/rtc-digicolor.c
<<
>>
Prefs
   1/*
   2 * Real Time Clock driver for Conexant Digicolor
   3 *
   4 * Copyright (C) 2015 Paradox Innovation Ltd.
   5 *
   6 * Author: Baruch Siach <baruch@tkos.co.il>
   7 *
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms of the GNU General Public License as published by the
  10 * Free Software Foundation; either version 2 of the License, or (at your
  11 * option) any later version.
  12 */
  13
  14#include <linux/io.h>
  15#include <linux/iopoll.h>
  16#include <linux/delay.h>
  17#include <linux/module.h>
  18#include <linux/platform_device.h>
  19#include <linux/rtc.h>
  20#include <linux/of.h>
  21
  22#define DC_RTC_CONTROL          0x0
  23#define DC_RTC_TIME             0x8
  24#define DC_RTC_REFERENCE        0xc
  25#define DC_RTC_ALARM            0x10
  26#define DC_RTC_INTFLAG_CLEAR    0x14
  27#define DC_RTC_INTENABLE        0x16
  28
  29#define DC_RTC_CMD_MASK         0xf
  30#define DC_RTC_GO_BUSY          BIT(7)
  31
  32#define CMD_NOP                 0
  33#define CMD_RESET               1
  34#define CMD_WRITE               3
  35#define CMD_READ                4
  36
  37#define CMD_DELAY_US            (10*1000)
  38#define CMD_TIMEOUT_US          (500*CMD_DELAY_US)
  39
  40struct dc_rtc {
  41        struct rtc_device       *rtc_dev;
  42        void __iomem            *regs;
  43};
  44
  45static int dc_rtc_cmds(struct dc_rtc *rtc, const u8 *cmds, int len)
  46{
  47        u8 val;
  48        int i, ret;
  49
  50        for (i = 0; i < len; i++) {
  51                writeb_relaxed((cmds[i] & DC_RTC_CMD_MASK) | DC_RTC_GO_BUSY,
  52                               rtc->regs + DC_RTC_CONTROL);
  53                ret = readb_relaxed_poll_timeout(
  54                        rtc->regs + DC_RTC_CONTROL, val,
  55                        !(val & DC_RTC_GO_BUSY), CMD_DELAY_US, CMD_TIMEOUT_US);
  56                if (ret < 0)
  57                        return ret;
  58        }
  59
  60        return 0;
  61}
  62
  63static int dc_rtc_read(struct dc_rtc *rtc, unsigned long *val)
  64{
  65        static const u8 read_cmds[] = {CMD_READ, CMD_NOP};
  66        u32 reference, time1, time2;
  67        int ret;
  68
  69        ret = dc_rtc_cmds(rtc, read_cmds, ARRAY_SIZE(read_cmds));
  70        if (ret < 0)
  71                return ret;
  72
  73        reference = readl_relaxed(rtc->regs + DC_RTC_REFERENCE);
  74        time1 = readl_relaxed(rtc->regs + DC_RTC_TIME);
  75        /* Read twice to ensure consistency */
  76        while (1) {
  77                time2 = readl_relaxed(rtc->regs + DC_RTC_TIME);
  78                if (time1 == time2)
  79                        break;
  80                time1 = time2;
  81        }
  82
  83        *val = reference + time1;
  84        return 0;
  85}
  86
  87static int dc_rtc_write(struct dc_rtc *rtc, u32 val)
  88{
  89        static const u8 write_cmds[] = {CMD_WRITE, CMD_NOP, CMD_RESET, CMD_NOP};
  90
  91        writel_relaxed(val, rtc->regs + DC_RTC_REFERENCE);
  92        return dc_rtc_cmds(rtc, write_cmds, ARRAY_SIZE(write_cmds));
  93}
  94
  95static int dc_rtc_read_time(struct device *dev, struct rtc_time *tm)
  96{
  97        struct dc_rtc *rtc = dev_get_drvdata(dev);
  98        unsigned long now;
  99        int ret;
 100
 101        ret = dc_rtc_read(rtc, &now);
 102        if (ret < 0)
 103                return ret;
 104        rtc_time64_to_tm(now, tm);
 105
 106        return 0;
 107}
 108
 109static int dc_rtc_set_mmss(struct device *dev, unsigned long secs)
 110{
 111        struct dc_rtc *rtc = dev_get_drvdata(dev);
 112
 113        return dc_rtc_write(rtc, secs);
 114}
 115
 116static int dc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 117{
 118        struct dc_rtc *rtc = dev_get_drvdata(dev);
 119        u32 alarm_reg, reference;
 120        unsigned long now;
 121        int ret;
 122
 123        alarm_reg = readl_relaxed(rtc->regs + DC_RTC_ALARM);
 124        reference = readl_relaxed(rtc->regs + DC_RTC_REFERENCE);
 125        rtc_time64_to_tm(reference + alarm_reg, &alarm->time);
 126
 127        ret = dc_rtc_read(rtc, &now);
 128        if (ret < 0)
 129                return ret;
 130
 131        alarm->pending = alarm_reg + reference > now;
 132        alarm->enabled = readl_relaxed(rtc->regs + DC_RTC_INTENABLE);
 133
 134        return 0;
 135}
 136
 137static int dc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 138{
 139        struct dc_rtc *rtc = dev_get_drvdata(dev);
 140        time64_t alarm_time;
 141        u32 reference;
 142
 143        alarm_time = rtc_tm_to_time64(&alarm->time);
 144
 145        reference = readl_relaxed(rtc->regs + DC_RTC_REFERENCE);
 146        writel_relaxed(alarm_time - reference, rtc->regs + DC_RTC_ALARM);
 147
 148        writeb_relaxed(!!alarm->enabled, rtc->regs + DC_RTC_INTENABLE);
 149
 150        return 0;
 151}
 152
 153static int dc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
 154{
 155        struct dc_rtc *rtc = dev_get_drvdata(dev);
 156
 157        writeb_relaxed(!!enabled, rtc->regs + DC_RTC_INTENABLE);
 158
 159        return 0;
 160}
 161
 162static const struct rtc_class_ops dc_rtc_ops = {
 163        .read_time              = dc_rtc_read_time,
 164        .set_mmss               = dc_rtc_set_mmss,
 165        .read_alarm             = dc_rtc_read_alarm,
 166        .set_alarm              = dc_rtc_set_alarm,
 167        .alarm_irq_enable       = dc_rtc_alarm_irq_enable,
 168};
 169
 170static irqreturn_t dc_rtc_irq(int irq, void *dev_id)
 171{
 172        struct dc_rtc *rtc = dev_id;
 173
 174        writeb_relaxed(1, rtc->regs + DC_RTC_INTFLAG_CLEAR);
 175        rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
 176
 177        return IRQ_HANDLED;
 178}
 179
 180static int __init dc_rtc_probe(struct platform_device *pdev)
 181{
 182        struct resource *res;
 183        struct dc_rtc *rtc;
 184        int irq, ret;
 185
 186        rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
 187        if (!rtc)
 188                return -ENOMEM;
 189
 190        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 191        rtc->regs = devm_ioremap_resource(&pdev->dev, res);
 192        if (IS_ERR(rtc->regs))
 193                return PTR_ERR(rtc->regs);
 194
 195        irq = platform_get_irq(pdev, 0);
 196        if (irq < 0)
 197                return irq;
 198        ret = devm_request_irq(&pdev->dev, irq, dc_rtc_irq, 0, pdev->name, rtc);
 199        if (ret < 0)
 200                return ret;
 201
 202        platform_set_drvdata(pdev, rtc);
 203        rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
 204                                                &dc_rtc_ops, THIS_MODULE);
 205        if (IS_ERR(rtc->rtc_dev))
 206                return PTR_ERR(rtc->rtc_dev);
 207
 208        return 0;
 209}
 210
 211static const struct of_device_id dc_dt_ids[] = {
 212        { .compatible = "cnxt,cx92755-rtc" },
 213        { /* sentinel */ }
 214};
 215MODULE_DEVICE_TABLE(of, dc_dt_ids);
 216
 217static struct platform_driver dc_rtc_driver = {
 218        .driver = {
 219                .name = "digicolor_rtc",
 220                .of_match_table = of_match_ptr(dc_dt_ids),
 221        },
 222};
 223module_platform_driver_probe(dc_rtc_driver, dc_rtc_probe);
 224
 225MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
 226MODULE_DESCRIPTION("Conexant Digicolor Realtime Clock Driver (RTC)");
 227MODULE_LICENSE("GPL");
 228