1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * A driver for the RTC embedded in the Cirrus Logic EP93XX processors |
4 | * Copyright (c) 2006 Tower Technologies |
5 | * |
6 | * Author: Alessandro Zummo <a.zummo@towertech.it> |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/mod_devicetable.h> |
11 | #include <linux/rtc.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/io.h> |
14 | #include <linux/gfp.h> |
15 | |
16 | #define EP93XX_RTC_DATA 0x000 |
17 | #define EP93XX_RTC_MATCH 0x004 |
18 | #define EP93XX_RTC_STATUS 0x008 |
19 | #define EP93XX_RTC_STATUS_INTR BIT(0) |
20 | #define EP93XX_RTC_LOAD 0x00C |
21 | #define EP93XX_RTC_CONTROL 0x010 |
22 | #define EP93XX_RTC_CONTROL_MIE BIT(0) |
23 | #define EP93XX_RTC_SWCOMP 0x108 |
24 | #define EP93XX_RTC_SWCOMP_DEL_MASK 0x001f0000 |
25 | #define EP93XX_RTC_SWCOMP_DEL_SHIFT 16 |
26 | #define EP93XX_RTC_SWCOMP_INT_MASK 0x0000ffff |
27 | #define EP93XX_RTC_SWCOMP_INT_SHIFT 0 |
28 | |
29 | struct ep93xx_rtc { |
30 | void __iomem *mmio_base; |
31 | struct rtc_device *rtc; |
32 | }; |
33 | |
34 | static int ep93xx_rtc_get_swcomp(struct device *dev, unsigned short *preload, |
35 | unsigned short *delete) |
36 | { |
37 | struct ep93xx_rtc *ep93xx_rtc = dev_get_drvdata(dev); |
38 | unsigned long comp; |
39 | |
40 | comp = readl(addr: ep93xx_rtc->mmio_base + EP93XX_RTC_SWCOMP); |
41 | |
42 | if (preload) |
43 | *preload = (comp & EP93XX_RTC_SWCOMP_INT_MASK) |
44 | >> EP93XX_RTC_SWCOMP_INT_SHIFT; |
45 | |
46 | if (delete) |
47 | *delete = (comp & EP93XX_RTC_SWCOMP_DEL_MASK) |
48 | >> EP93XX_RTC_SWCOMP_DEL_SHIFT; |
49 | |
50 | return 0; |
51 | } |
52 | |
53 | static int ep93xx_rtc_read_time(struct device *dev, struct rtc_time *tm) |
54 | { |
55 | struct ep93xx_rtc *ep93xx_rtc = dev_get_drvdata(dev); |
56 | unsigned long time; |
57 | |
58 | time = readl(addr: ep93xx_rtc->mmio_base + EP93XX_RTC_DATA); |
59 | |
60 | rtc_time64_to_tm(time, tm); |
61 | return 0; |
62 | } |
63 | |
64 | static int ep93xx_rtc_set_time(struct device *dev, struct rtc_time *tm) |
65 | { |
66 | struct ep93xx_rtc *ep93xx_rtc = dev_get_drvdata(dev); |
67 | unsigned long secs = rtc_tm_to_time64(tm); |
68 | |
69 | writel(val: secs + 1, addr: ep93xx_rtc->mmio_base + EP93XX_RTC_LOAD); |
70 | return 0; |
71 | } |
72 | |
73 | static int ep93xx_rtc_proc(struct device *dev, struct seq_file *seq) |
74 | { |
75 | unsigned short preload, delete; |
76 | |
77 | ep93xx_rtc_get_swcomp(dev, preload: &preload, delete: &delete); |
78 | |
79 | seq_printf(m: seq, fmt: "preload\t\t: %d\n" , preload); |
80 | seq_printf(m: seq, fmt: "delete\t\t: %d\n" , delete); |
81 | |
82 | return 0; |
83 | } |
84 | |
85 | static const struct rtc_class_ops ep93xx_rtc_ops = { |
86 | .read_time = ep93xx_rtc_read_time, |
87 | .set_time = ep93xx_rtc_set_time, |
88 | .proc = ep93xx_rtc_proc, |
89 | }; |
90 | |
91 | static ssize_t comp_preload_show(struct device *dev, |
92 | struct device_attribute *attr, char *buf) |
93 | { |
94 | unsigned short preload; |
95 | |
96 | ep93xx_rtc_get_swcomp(dev: dev->parent, preload: &preload, NULL); |
97 | |
98 | return sprintf(buf, fmt: "%d\n" , preload); |
99 | } |
100 | static DEVICE_ATTR_RO(comp_preload); |
101 | |
102 | static ssize_t comp_delete_show(struct device *dev, |
103 | struct device_attribute *attr, char *buf) |
104 | { |
105 | unsigned short delete; |
106 | |
107 | ep93xx_rtc_get_swcomp(dev: dev->parent, NULL, delete: &delete); |
108 | |
109 | return sprintf(buf, fmt: "%d\n" , delete); |
110 | } |
111 | static DEVICE_ATTR_RO(comp_delete); |
112 | |
113 | static struct attribute *ep93xx_rtc_attrs[] = { |
114 | &dev_attr_comp_preload.attr, |
115 | &dev_attr_comp_delete.attr, |
116 | NULL |
117 | }; |
118 | |
119 | static const struct attribute_group ep93xx_rtc_sysfs_files = { |
120 | .attrs = ep93xx_rtc_attrs, |
121 | }; |
122 | |
123 | static int ep93xx_rtc_probe(struct platform_device *pdev) |
124 | { |
125 | struct ep93xx_rtc *ep93xx_rtc; |
126 | int err; |
127 | |
128 | ep93xx_rtc = devm_kzalloc(dev: &pdev->dev, size: sizeof(*ep93xx_rtc), GFP_KERNEL); |
129 | if (!ep93xx_rtc) |
130 | return -ENOMEM; |
131 | |
132 | ep93xx_rtc->mmio_base = devm_platform_ioremap_resource(pdev, index: 0); |
133 | if (IS_ERR(ptr: ep93xx_rtc->mmio_base)) |
134 | return PTR_ERR(ptr: ep93xx_rtc->mmio_base); |
135 | |
136 | platform_set_drvdata(pdev, data: ep93xx_rtc); |
137 | |
138 | ep93xx_rtc->rtc = devm_rtc_allocate_device(dev: &pdev->dev); |
139 | if (IS_ERR(ptr: ep93xx_rtc->rtc)) |
140 | return PTR_ERR(ptr: ep93xx_rtc->rtc); |
141 | |
142 | ep93xx_rtc->rtc->ops = &ep93xx_rtc_ops; |
143 | ep93xx_rtc->rtc->range_max = U32_MAX; |
144 | |
145 | err = rtc_add_group(rtc: ep93xx_rtc->rtc, grp: &ep93xx_rtc_sysfs_files); |
146 | if (err) |
147 | return err; |
148 | |
149 | return devm_rtc_register_device(ep93xx_rtc->rtc); |
150 | } |
151 | |
152 | static const struct of_device_id ep93xx_rtc_of_ids[] = { |
153 | { .compatible = "cirrus,ep9301-rtc" }, |
154 | { /* sentinel */ } |
155 | }; |
156 | MODULE_DEVICE_TABLE(of, ep93xx_rtc_of_ids); |
157 | |
158 | static struct platform_driver ep93xx_rtc_driver = { |
159 | .driver = { |
160 | .name = "ep93xx-rtc" , |
161 | .of_match_table = ep93xx_rtc_of_ids, |
162 | }, |
163 | .probe = ep93xx_rtc_probe, |
164 | }; |
165 | |
166 | module_platform_driver(ep93xx_rtc_driver); |
167 | |
168 | MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>" ); |
169 | MODULE_DESCRIPTION("EP93XX RTC driver" ); |
170 | MODULE_LICENSE("GPL" ); |
171 | MODULE_ALIAS("platform:ep93xx-rtc" ); |
172 | |