1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * |
4 | * Driver for ST M41T93 SPI RTC |
5 | * |
6 | * (c) 2010 Nikolaus Voss, Weinmann Medical GmbH |
7 | */ |
8 | |
9 | #include <linux/bcd.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/rtc.h> |
14 | #include <linux/spi/spi.h> |
15 | |
16 | #define M41T93_REG_SSEC 0 |
17 | #define M41T93_REG_ST_SEC 1 |
18 | #define M41T93_REG_MIN 2 |
19 | #define M41T93_REG_CENT_HOUR 3 |
20 | #define M41T93_REG_WDAY 4 |
21 | #define M41T93_REG_DAY 5 |
22 | #define M41T93_REG_MON 6 |
23 | #define M41T93_REG_YEAR 7 |
24 | |
25 | |
26 | #define M41T93_REG_ALM_HOUR_HT 0xc |
27 | #define M41T93_REG_FLAGS 0xf |
28 | |
29 | #define M41T93_FLAG_ST (1 << 7) |
30 | #define M41T93_FLAG_OF (1 << 2) |
31 | #define M41T93_FLAG_BL (1 << 4) |
32 | #define M41T93_FLAG_HT (1 << 6) |
33 | |
34 | static inline int m41t93_set_reg(struct spi_device *spi, u8 addr, u8 data) |
35 | { |
36 | u8 buf[2]; |
37 | |
38 | /* MSB must be '1' to write */ |
39 | buf[0] = addr | 0x80; |
40 | buf[1] = data; |
41 | |
42 | return spi_write(spi, buf, len: sizeof(buf)); |
43 | } |
44 | |
45 | static int m41t93_set_time(struct device *dev, struct rtc_time *tm) |
46 | { |
47 | struct spi_device *spi = to_spi_device(dev); |
48 | int tmp; |
49 | u8 buf[9] = {0x80}; /* write cmd + 8 data bytes */ |
50 | u8 * const data = &buf[1]; /* ptr to first data byte */ |
51 | |
52 | dev_dbg(dev, "%s secs=%d, mins=%d, " |
53 | "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n" , |
54 | "write" , tm->tm_sec, tm->tm_min, |
55 | tm->tm_hour, tm->tm_mday, |
56 | tm->tm_mon, tm->tm_year, tm->tm_wday); |
57 | |
58 | if (tm->tm_year < 100) { |
59 | dev_warn(&spi->dev, "unsupported date (before 2000-01-01).\n" ); |
60 | return -EINVAL; |
61 | } |
62 | |
63 | tmp = spi_w8r8(spi, M41T93_REG_FLAGS); |
64 | if (tmp < 0) |
65 | return tmp; |
66 | |
67 | if (tmp & M41T93_FLAG_OF) { |
68 | dev_warn(&spi->dev, "OF bit is set, resetting.\n" ); |
69 | m41t93_set_reg(spi, M41T93_REG_FLAGS, data: tmp & ~M41T93_FLAG_OF); |
70 | |
71 | tmp = spi_w8r8(spi, M41T93_REG_FLAGS); |
72 | if (tmp < 0) { |
73 | return tmp; |
74 | } else if (tmp & M41T93_FLAG_OF) { |
75 | /* OF cannot be immediately reset: oscillator has to be |
76 | * restarted. */ |
77 | u8 reset_osc = buf[M41T93_REG_ST_SEC] | M41T93_FLAG_ST; |
78 | |
79 | dev_warn(&spi->dev, |
80 | "OF bit is still set, kickstarting clock.\n" ); |
81 | m41t93_set_reg(spi, M41T93_REG_ST_SEC, data: reset_osc); |
82 | reset_osc &= ~M41T93_FLAG_ST; |
83 | m41t93_set_reg(spi, M41T93_REG_ST_SEC, data: reset_osc); |
84 | } |
85 | } |
86 | |
87 | data[M41T93_REG_SSEC] = 0; |
88 | data[M41T93_REG_ST_SEC] = bin2bcd(tm->tm_sec); |
89 | data[M41T93_REG_MIN] = bin2bcd(tm->tm_min); |
90 | data[M41T93_REG_CENT_HOUR] = bin2bcd(tm->tm_hour) | |
91 | ((tm->tm_year/100-1) << 6); |
92 | data[M41T93_REG_DAY] = bin2bcd(tm->tm_mday); |
93 | data[M41T93_REG_WDAY] = bin2bcd(tm->tm_wday + 1); |
94 | data[M41T93_REG_MON] = bin2bcd(tm->tm_mon + 1); |
95 | data[M41T93_REG_YEAR] = bin2bcd(tm->tm_year % 100); |
96 | |
97 | return spi_write(spi, buf, len: sizeof(buf)); |
98 | } |
99 | |
100 | |
101 | static int m41t93_get_time(struct device *dev, struct rtc_time *tm) |
102 | { |
103 | struct spi_device *spi = to_spi_device(dev); |
104 | const u8 start_addr = 0; |
105 | u8 buf[8]; |
106 | int century_after_1900; |
107 | int tmp; |
108 | int ret = 0; |
109 | |
110 | /* Check status of clock. Two states must be considered: |
111 | 1. halt bit (HT) is set: the clock is running but update of readout |
112 | registers has been disabled due to power failure. This is normal |
113 | case after poweron. Time is valid after resetting HT bit. |
114 | 2. oscillator fail bit (OF) is set: time is invalid. |
115 | */ |
116 | tmp = spi_w8r8(spi, M41T93_REG_ALM_HOUR_HT); |
117 | if (tmp < 0) |
118 | return tmp; |
119 | |
120 | if (tmp & M41T93_FLAG_HT) { |
121 | dev_dbg(&spi->dev, "HT bit is set, reenable clock update.\n" ); |
122 | m41t93_set_reg(spi, M41T93_REG_ALM_HOUR_HT, |
123 | data: tmp & ~M41T93_FLAG_HT); |
124 | } |
125 | |
126 | tmp = spi_w8r8(spi, M41T93_REG_FLAGS); |
127 | if (tmp < 0) |
128 | return tmp; |
129 | |
130 | if (tmp & M41T93_FLAG_OF) { |
131 | ret = -EINVAL; |
132 | dev_warn(&spi->dev, "OF bit is set, write time to restart.\n" ); |
133 | } |
134 | |
135 | if (tmp & M41T93_FLAG_BL) |
136 | dev_warn(&spi->dev, "BL bit is set, replace battery.\n" ); |
137 | |
138 | /* read actual time/date */ |
139 | tmp = spi_write_then_read(spi, txbuf: &start_addr, n_tx: 1, rxbuf: buf, n_rx: sizeof(buf)); |
140 | if (tmp < 0) |
141 | return tmp; |
142 | |
143 | tm->tm_sec = bcd2bin(buf[M41T93_REG_ST_SEC]); |
144 | tm->tm_min = bcd2bin(buf[M41T93_REG_MIN]); |
145 | tm->tm_hour = bcd2bin(buf[M41T93_REG_CENT_HOUR] & 0x3f); |
146 | tm->tm_mday = bcd2bin(buf[M41T93_REG_DAY]); |
147 | tm->tm_mon = bcd2bin(buf[M41T93_REG_MON]) - 1; |
148 | tm->tm_wday = bcd2bin(buf[M41T93_REG_WDAY] & 0x0f) - 1; |
149 | |
150 | century_after_1900 = (buf[M41T93_REG_CENT_HOUR] >> 6) + 1; |
151 | tm->tm_year = bcd2bin(buf[M41T93_REG_YEAR]) + century_after_1900 * 100; |
152 | |
153 | dev_dbg(dev, "%s secs=%d, mins=%d, " |
154 | "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n" , |
155 | "read" , tm->tm_sec, tm->tm_min, |
156 | tm->tm_hour, tm->tm_mday, |
157 | tm->tm_mon, tm->tm_year, tm->tm_wday); |
158 | |
159 | return ret; |
160 | } |
161 | |
162 | |
163 | static const struct rtc_class_ops m41t93_rtc_ops = { |
164 | .read_time = m41t93_get_time, |
165 | .set_time = m41t93_set_time, |
166 | }; |
167 | |
168 | static struct spi_driver m41t93_driver; |
169 | |
170 | static int m41t93_probe(struct spi_device *spi) |
171 | { |
172 | struct rtc_device *rtc; |
173 | int res; |
174 | |
175 | spi->bits_per_word = 8; |
176 | spi_setup(spi); |
177 | |
178 | res = spi_w8r8(spi, M41T93_REG_WDAY); |
179 | if (res < 0 || (res & 0xf8) != 0) { |
180 | dev_err(&spi->dev, "not found 0x%x.\n" , res); |
181 | return -ENODEV; |
182 | } |
183 | |
184 | rtc = devm_rtc_device_register(dev: &spi->dev, name: m41t93_driver.driver.name, |
185 | ops: &m41t93_rtc_ops, THIS_MODULE); |
186 | if (IS_ERR(ptr: rtc)) |
187 | return PTR_ERR(ptr: rtc); |
188 | |
189 | spi_set_drvdata(spi, data: rtc); |
190 | |
191 | return 0; |
192 | } |
193 | |
194 | static struct spi_driver m41t93_driver = { |
195 | .driver = { |
196 | .name = "rtc-m41t93" , |
197 | }, |
198 | .probe = m41t93_probe, |
199 | }; |
200 | |
201 | module_spi_driver(m41t93_driver); |
202 | |
203 | MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>" ); |
204 | MODULE_DESCRIPTION("Driver for ST M41T93 SPI RTC" ); |
205 | MODULE_LICENSE("GPL" ); |
206 | MODULE_ALIAS("spi:rtc-m41t93" ); |
207 | |