1 /* $OpenBSD: pcf8563.c,v 1.7 2022/10/12 13:39:50 kettenis Exp $ */
2
3 /*
4 * Copyright (c) 2005 Kimihiro Nonaka
5 * Copyright (c) 2016, 2017 Mark Kettenis
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/device.h>
33
34 #include <dev/clock_subr.h>
35
36 #include <dev/i2c/i2cvar.h>
37
38 /*
39 * PCF8563 Real-Time Clock
40 */
41
42 #define PCF8563_CONTROL1 0x00
43 #define PCF8563_CONTROL2 0x01
44 #define PCF8563_SECONDS 0x02
45 #define PCF8563_MINUTES 0x03
46 #define PCF8563_HOURS 0x04
47 #define PCF8563_DAY 0x05
48 #define PCF8563_WDAY 0x06
49 #define PCF8563_MONTH 0x07
50 #define PCF8563_YEAR 0x08
51
52 #define PCF8563_NREGS 12
53 #define PCF8563_NRTC_REGS 7
54
55 /*
56 * Bit definitions.
57 */
58 #define PCF8563_CONTROL1_TESTC (1 << 3)
59 #define PCF8563_CONTROL1_STOP (1 << 5)
60 #define PCF8563_CONTROL1_TEST1 (1 << 1)
61 #define PCF8563_SECONDS_MASK 0x7f
62 #define PCF8563_SECONDS_VL (1 << 7)
63 #define PCF8563_MINUTES_MASK 0x7f
64 #define PCF8563_HOURS_MASK 0x3f
65 #define PCF8563_DAY_MASK 0x3f
66 #define PCF8563_WDAY_MASK 0x07
67 #define PCF8563_MONTH_MASK 0x1f
68 #define PCF8563_MONTH_C (1 << 7)
69
70 struct pcxrtc_softc {
71 struct device sc_dev;
72 i2c_tag_t sc_tag;
73 int sc_address;
74 struct todr_chip_handle sc_todr;
75 };
76
77 int pcxrtc_match(struct device *, void *, void *);
78 void pcxrtc_attach(struct device *, struct device *, void *);
79
80 const struct cfattach pcxrtc_ca = {
81 sizeof(struct pcxrtc_softc), pcxrtc_match, pcxrtc_attach
82 };
83
84 struct cfdriver pcxrtc_cd = {
85 NULL, "pcxrtc", DV_DULL
86 };
87
88 uint8_t pcxrtc_reg_read(struct pcxrtc_softc *, int);
89 void pcxrtc_reg_write(struct pcxrtc_softc *, int, uint8_t);
90 int pcxrtc_clock_read(struct pcxrtc_softc *, struct clock_ymdhms *);
91 int pcxrtc_clock_write(struct pcxrtc_softc *, struct clock_ymdhms *);
92 int pcxrtc_gettime(struct todr_chip_handle *, struct timeval *);
93 int pcxrtc_settime(struct todr_chip_handle *, struct timeval *);
94
95 int
pcxrtc_match(struct device * parent,void * v,void * arg)96 pcxrtc_match(struct device *parent, void *v, void *arg)
97 {
98 struct i2c_attach_args *ia = arg;
99
100 if (strcmp(ia->ia_name, "nxp,pcf8563") == 0 ||
101 strcmp(ia->ia_name, "haoyu,hym8563") == 0)
102 return (1);
103
104 return (0);
105 }
106
107 void
pcxrtc_attach(struct device * parent,struct device * self,void * arg)108 pcxrtc_attach(struct device *parent, struct device *self, void *arg)
109 {
110 struct pcxrtc_softc *sc = (struct pcxrtc_softc *)self;
111 struct i2c_attach_args *ia = arg;
112 uint8_t reg;
113
114 sc->sc_tag = ia->ia_tag;
115 sc->sc_address = ia->ia_addr;
116
117 sc->sc_todr.cookie = sc;
118 sc->sc_todr.todr_gettime = pcxrtc_gettime;
119 sc->sc_todr.todr_settime = pcxrtc_settime;
120 sc->sc_todr.todr_setwen = NULL;
121 sc->sc_todr.todr_quality = 1000;
122 todr_attach(&sc->sc_todr);
123
124 /* Enable. */
125 reg = pcxrtc_reg_read(sc, PCF8563_CONTROL1);
126 reg &= ~PCF8563_CONTROL1_STOP;
127 pcxrtc_reg_write(sc, PCF8563_CONTROL1, reg);
128
129 /* Report battery status. */
130 reg = pcxrtc_reg_read(sc, PCF8563_SECONDS);
131 printf(": battery %s\n", (reg & PCF8563_SECONDS_VL) ? "low" : "ok");
132 }
133
134 int
pcxrtc_gettime(struct todr_chip_handle * ch,struct timeval * tv)135 pcxrtc_gettime(struct todr_chip_handle *ch, struct timeval *tv)
136 {
137 struct pcxrtc_softc *sc = ch->cookie;
138 struct clock_ymdhms dt;
139
140 memset(&dt, 0, sizeof(dt));
141 if (pcxrtc_clock_read(sc, &dt) == 0)
142 return (-1);
143
144 tv->tv_sec = clock_ymdhms_to_secs(&dt);
145 tv->tv_usec = 0;
146 return (0);
147 }
148
149 int
pcxrtc_settime(struct todr_chip_handle * ch,struct timeval * tv)150 pcxrtc_settime(struct todr_chip_handle *ch, struct timeval *tv)
151 {
152 struct pcxrtc_softc *sc = ch->cookie;
153 struct clock_ymdhms dt;
154
155 clock_secs_to_ymdhms(tv->tv_sec, &dt);
156
157 if (pcxrtc_clock_write(sc, &dt) == 0)
158 return (-1);
159 return (0);
160 }
161
162 uint8_t
pcxrtc_reg_read(struct pcxrtc_softc * sc,int reg)163 pcxrtc_reg_read(struct pcxrtc_softc *sc, int reg)
164 {
165 uint8_t cmd = reg;
166 uint8_t val;
167
168 iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
169 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address,
170 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL)) {
171 iic_release_bus(sc->sc_tag, I2C_F_POLL);
172 printf("%s: pcxrtc_reg_read: failed to read reg%d\n",
173 sc->sc_dev.dv_xname, reg);
174 return 0;
175 }
176 iic_release_bus(sc->sc_tag, I2C_F_POLL);
177 return val;
178 }
179
180 void
pcxrtc_reg_write(struct pcxrtc_softc * sc,int reg,uint8_t val)181 pcxrtc_reg_write(struct pcxrtc_softc *sc, int reg, uint8_t val)
182 {
183 uint8_t cmd = reg;
184
185 iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
186 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address,
187 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL)) {
188 iic_release_bus(sc->sc_tag, I2C_F_POLL);
189 printf("%s: pcxrtc_reg_write: failed to write reg%d\n",
190 sc->sc_dev.dv_xname, reg);
191 return;
192 }
193 iic_release_bus(sc->sc_tag, I2C_F_POLL);
194 }
195
196 int
pcxrtc_clock_read(struct pcxrtc_softc * sc,struct clock_ymdhms * dt)197 pcxrtc_clock_read(struct pcxrtc_softc *sc, struct clock_ymdhms *dt)
198 {
199 uint8_t regs[PCF8563_NRTC_REGS];
200 uint8_t cmd = PCF8563_SECONDS;
201
202 iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
203 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address,
204 &cmd, sizeof(cmd), regs, PCF8563_NRTC_REGS, I2C_F_POLL)) {
205 iic_release_bus(sc->sc_tag, I2C_F_POLL);
206 printf("%s: pcxrtc_clock_read: failed to read rtc\n",
207 sc->sc_dev.dv_xname);
208 return (0);
209 }
210 iic_release_bus(sc->sc_tag, I2C_F_POLL);
211
212 /*
213 * Convert the PCF8563's register values into something useable
214 */
215 dt->dt_sec = FROMBCD(regs[0] & PCF8563_SECONDS_MASK);
216 dt->dt_min = FROMBCD(regs[1] & PCF8563_MINUTES_MASK);
217 dt->dt_hour = FROMBCD(regs[2] & PCF8563_HOURS_MASK);
218 dt->dt_day = FROMBCD(regs[3] & PCF8563_DAY_MASK);
219 dt->dt_mon = FROMBCD(regs[5] & PCF8563_MONTH_MASK);
220 dt->dt_year = FROMBCD(regs[6]) + 2000;
221
222 if ((regs[0] & PCF8563_SECONDS_VL))
223 return (0);
224
225 return (1);
226 }
227
228 int
pcxrtc_clock_write(struct pcxrtc_softc * sc,struct clock_ymdhms * dt)229 pcxrtc_clock_write(struct pcxrtc_softc *sc, struct clock_ymdhms *dt)
230 {
231 uint8_t regs[PCF8563_NRTC_REGS];
232 uint8_t cmd = PCF8563_SECONDS;
233
234 /*
235 * Convert our time representation into something the PCF8563
236 * can understand.
237 */
238 regs[0] = TOBCD(dt->dt_sec);
239 regs[1] = TOBCD(dt->dt_min);
240 regs[2] = TOBCD(dt->dt_hour);
241 regs[3] = TOBCD(dt->dt_day);
242 regs[4] = TOBCD(dt->dt_wday);
243 regs[5] = TOBCD(dt->dt_mon);
244 regs[6] = TOBCD(dt->dt_year - 2000);
245
246 iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
247 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address,
248 &cmd, sizeof(cmd), regs, PCF8563_NRTC_REGS, I2C_F_POLL)) {
249 iic_release_bus(sc->sc_tag, I2C_F_POLL);
250 printf("%s: pcxrtc_clock_write: failed to write rtc\n",
251 sc->sc_dev.dv_xname);
252 return (0);
253 }
254 iic_release_bus(sc->sc_tag, I2C_F_POLL);
255 return (1);
256 }
257