pcf8563.c revision 1.7
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
70struct 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
77int pcxrtc_match(struct device *, void *, void *);
78void pcxrtc_attach(struct device *, struct device *, void *);
79
80const struct cfattach pcxrtc_ca = {
81	sizeof(struct pcxrtc_softc), pcxrtc_match, pcxrtc_attach
82};
83
84struct cfdriver pcxrtc_cd = {
85	NULL, "pcxrtc", DV_DULL
86};
87
88uint8_t pcxrtc_reg_read(struct pcxrtc_softc *, int);
89void pcxrtc_reg_write(struct pcxrtc_softc *, int, uint8_t);
90int pcxrtc_clock_read(struct pcxrtc_softc *, struct clock_ymdhms *);
91int pcxrtc_clock_write(struct pcxrtc_softc *, struct clock_ymdhms *);
92int pcxrtc_gettime(struct todr_chip_handle *, struct timeval *);
93int pcxrtc_settime(struct todr_chip_handle *, struct timeval *);
94
95int
96pcxrtc_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
107void
108pcxrtc_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
134int
135pcxrtc_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
149int
150pcxrtc_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
162uint8_t
163pcxrtc_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
180void
181pcxrtc_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
196int
197pcxrtc_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
228int
229pcxrtc_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