xref: /openbsd/sys/dev/i2c/isl1208.c (revision 4cfece93)
1 /*	$OpenBSD: isl1208.c,v 1.3 2020/04/24 22:42:31 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 
22 #include <dev/i2c/i2cvar.h>
23 
24 #include <dev/clock_subr.h>
25 
26 extern todr_chip_handle_t todr_handle;
27 
28 #define ISL1208_SC		0x00
29 #define ISL1208_MN		0x01
30 #define ISL1208_HR		0x02
31 #define  ISL1208_HR_HR21	(1 << 5)
32 #define  ISL1208_HR_MIL		(1 << 7)
33 #define ISL1208_DT		0x03
34 #define ISL1208_MO		0x04
35 #define ISL1208_YR		0x05
36 #define ISL1208_DW		0x06
37 #define ISL1208_SR		0x07
38 #define  ISL1208_SR_RTCF	(1 << 0)
39 #define  ISL1208_SR_WRTC	(1 << 4)
40 
41 #define ISL1208_NRTC_REGS	7
42 
43 struct islrtc_softc {
44 	struct device sc_dev;
45 	i2c_tag_t sc_tag;
46 	i2c_addr_t sc_addr;
47 
48 	struct todr_chip_handle sc_todr;
49 };
50 
51 int	islrtc_match(struct device *, void *, void *);
52 void	islrtc_attach(struct device *, struct device *, void *);
53 
54 struct cfattach islrtc_ca = {
55 	sizeof(struct islrtc_softc), islrtc_match, islrtc_attach
56 };
57 
58 struct cfdriver islrtc_cd = {
59 	NULL, "islrtc", DV_DULL
60 };
61 
62 uint8_t	islrtc_reg_read(struct islrtc_softc *, int);
63 void	islrtc_reg_write(struct islrtc_softc *, int, uint8_t);
64 int	islrtc_clock_read(struct islrtc_softc *, struct clock_ymdhms *);
65 int	islrtc_clock_write(struct islrtc_softc *, struct clock_ymdhms *);
66 int	islrtc_gettime(struct todr_chip_handle *, struct timeval *);
67 int	islrtc_settime(struct todr_chip_handle *, struct timeval *);
68 
69 int
70 islrtc_match(struct device *parent, void *match, void *aux)
71 {
72 	struct i2c_attach_args *ia = aux;
73 
74 	if (strcmp(ia->ia_name, "isil,isl1208") == 0 ||
75 	    strcmp(ia->ia_name, "isil,isl1218") == 0)
76 		return 1;
77 
78 	return 0;
79 }
80 
81 void
82 islrtc_attach(struct device *parent, struct device *self, void *aux)
83 {
84 	struct islrtc_softc *sc = (struct islrtc_softc *)self;
85 	struct i2c_attach_args *ia = aux;
86 
87 	sc->sc_tag = ia->ia_tag;
88 	sc->sc_addr = ia->ia_addr;
89 
90 	sc->sc_todr.cookie = sc;
91 	sc->sc_todr.todr_gettime = islrtc_gettime;
92 	sc->sc_todr.todr_settime = islrtc_settime;
93 	todr_handle = &sc->sc_todr;
94 
95 	printf("\n");
96 }
97 
98 int
99 islrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
100 {
101 	struct islrtc_softc *sc = handle->cookie;
102 	struct clock_ymdhms dt;
103 	int error;
104 
105 	error = islrtc_clock_read(sc, &dt);
106 	if (error)
107 		return error;
108 
109 	if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 ||
110 	    dt.dt_day > 31 || dt.dt_day == 0 ||
111 	    dt.dt_mon > 12 || dt.dt_mon == 0 ||
112 	    dt.dt_year < POSIX_BASE_YEAR)
113 		return EINVAL;
114 
115 	tv->tv_sec = clock_ymdhms_to_secs(&dt);
116 	tv->tv_usec = 0;
117 	return 0;
118 }
119 
120 int
121 islrtc_settime(struct todr_chip_handle *handle, struct timeval *tv)
122 {
123 	struct islrtc_softc *sc = handle->cookie;
124 	struct clock_ymdhms dt;
125 
126 	clock_secs_to_ymdhms(tv->tv_sec, &dt);
127 
128 	return islrtc_clock_write(sc, &dt);
129 }
130 
131 uint8_t
132 islrtc_reg_read(struct islrtc_softc *sc, int reg)
133 {
134 	uint8_t cmd = reg;
135 	uint8_t val;
136 	int error;
137 
138 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
139 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
140 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
141 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
142 
143 	if (error) {
144 		printf("%s: can't read register 0x%02x\n",
145 		    sc->sc_dev.dv_xname, reg);
146 		val = 0xff;
147 	}
148 
149 	return val;
150 }
151 
152 void
153 islrtc_reg_write(struct islrtc_softc *sc, int reg, uint8_t val)
154 {
155 	uint8_t cmd = reg;
156 	int error;
157 
158 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
159 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
160 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
161 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
162 
163 	if (error) {
164 		printf("%s: can't write register 0x%02x\n",
165 		    sc->sc_dev.dv_xname, reg);
166 	}
167 }
168 
169 int
170 islrtc_clock_read(struct islrtc_softc *sc, struct clock_ymdhms *dt)
171 {
172 	uint8_t regs[ISL1208_NRTC_REGS];
173 	uint8_t cmd = ISL1208_SC;
174 	uint8_t status;
175 	int error;
176 
177 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
178 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
179 	    &cmd, sizeof(cmd), regs, ISL1208_NRTC_REGS, I2C_F_POLL);
180 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
181 
182 	if (error) {
183 		printf("%s: can't read RTC\n", sc->sc_dev.dv_xname);
184 		return error;
185 	}
186 
187 	/*
188 	 * Convert the ISL1208's register values into something useable.
189 	 */
190 	dt->dt_sec = FROMBCD(regs[0]);
191 	dt->dt_min = FROMBCD(regs[1]);
192 	if (regs[2] & ISL1208_HR_MIL) {
193 		dt->dt_hour = FROMBCD(regs[2] & ~ISL1208_HR_MIL);
194 	} else {
195 		dt->dt_hour = FROMBCD(regs[2] & ~ISL1208_HR_HR21);
196 		if (regs[2] & ISL1208_HR_HR21)
197 			dt->dt_hour += 12;
198 	}
199 	dt->dt_day = FROMBCD(regs[3]);
200 	dt->dt_mon = FROMBCD(regs[4]);
201 	dt->dt_year = FROMBCD(regs[5]) + 2000;
202 
203 	/* Consider the time to be invalid if we lost power. */
204 	status = islrtc_reg_read(sc, ISL1208_SR);
205 	if (status & ISL1208_SR_RTCF)
206 		return EINVAL;
207 
208 	return 0;
209 }
210 
211 int
212 islrtc_clock_write(struct islrtc_softc *sc, struct clock_ymdhms *dt)
213 {
214 	uint8_t regs[ISL1208_NRTC_REGS];
215 	uint8_t cmd = ISL1208_SC;
216 	uint8_t reg;
217 	int error;
218 
219 	/*
220 	 * Convert our time representation into something the ISL1208
221 	 * can understand.
222 	 */
223 	regs[0] = TOBCD(dt->dt_sec);
224 	regs[1] = TOBCD(dt->dt_min);
225 	regs[2] = TOBCD(dt->dt_hour) | ISL1208_HR_MIL;
226 	regs[3] = TOBCD(dt->dt_day);
227 	regs[4] = TOBCD(dt->dt_mon);
228 	regs[5] = TOBCD(dt->dt_year - 2000);
229 	regs[6] = TOBCD(dt->dt_wday);
230 
231 	/* Stop RTC such that we can write to it. */
232 	reg = islrtc_reg_read(sc, ISL1208_SR);
233 	if (reg == 0xff) {
234 		error = EIO;
235 		goto fail;
236 	}
237 	islrtc_reg_write(sc, ISL1208_SR, reg | ISL1208_SR_WRTC);
238 
239 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
240 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
241 	    &cmd, sizeof(cmd), regs, ISL1208_NRTC_REGS, I2C_F_POLL);
242 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
243 
244 	/* Restart RTC. */
245 	islrtc_reg_write(sc, ISL1208_SR, reg & ~ISL1208_SR_WRTC);
246 
247 fail:
248 	if (error) {
249 		printf("%s: can't write RTC\n", sc->sc_dev.dv_xname);
250 		return error;
251 	}
252 
253 	return 0;
254 }
255