aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
authorBaruch Siach <baruch@tkos.co.il>2015-04-16 15:45:40 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-04-17 09:04:01 -0400
commitba17220878bf74b95ea24de568939e7b9e1b02db (patch)
tree6e825fc5b30a3a161b8cdd4b6bc4f1603d399d80 /drivers/rtc
parent71b800b628570ce315a3f820b79969e460cd297f (diff)
rtc: driver for Conexant Digicolor CX92755 on-chip RTC
Add driver for the RTC hardware block on the Conexant CX92755 SoC, from the Digicolor series of SoCs. Tested on the Equinox evaluation board for the CX92755 chip. [akpm@linux-foundation.org: build command arrays at compile-time] Signed-off-by: Baruch Siach <baruch@tkos.co.il> Cc: Alessandro Zummo <a.zummo@towertech.it> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig10
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-digicolor.c227
3 files changed, 238 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index b5b5c3d485d6..faeee2d2550b 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1111,6 +1111,16 @@ config RTC_DRV_DAVINCI
1111 This driver can also be built as a module. If so, the module 1111 This driver can also be built as a module. If so, the module
1112 will be called rtc-davinci. 1112 will be called rtc-davinci.
1113 1113
1114config RTC_DRV_DIGICOLOR
1115 tristate "Conexant Digicolor RTC"
1116 depends on ARCH_DIGICOLOR
1117 help
1118 If you say yes here you get support for the RTC on Conexant
1119 Digicolor platforms. This currently includes the CX92755 SoC.
1120
1121 This driver can also be built as a module. If so, the module
1122 will be called rtc-digicolor.
1123
1114config RTC_DRV_IMXDI 1124config RTC_DRV_IMXDI
1115 tristate "Freescale IMX DryIce Real Time Clock" 1125 tristate "Freescale IMX DryIce Real Time Clock"
1116 depends on ARCH_MXC 1126 depends on ARCH_MXC
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 69c87062b098..c31731c29762 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o
40obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o 40obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o
41obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o 41obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o
42obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o 42obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o
43obj-$(CONFIG_RTC_DRV_DIGICOLOR) += rtc-digicolor.o
43obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o 44obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o
44obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o 45obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o
45obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o 46obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o
diff --git a/drivers/rtc/rtc-digicolor.c b/drivers/rtc/rtc-digicolor.c
new file mode 100644
index 000000000000..8d05596a6765
--- /dev/null
+++ b/drivers/rtc/rtc-digicolor.c
@@ -0,0 +1,227 @@
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 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");