ds133x.c revision 181602
1/*-
2 * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>,
3 *                    Rafal Jaworowski <raj@FreeBSD.org>,
4 *                    Piotr Ziecik <kosmo@semihalf.com>.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/sys/dev/iicbus/ds133x.c 181602 2008-08-11 19:33:58Z raj $");
30/*
31 * Dallas Semiconductor DS133X RTC sitting on the I2C bus.
32 */
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/kernel.h>
36#include <sys/module.h>
37#include <sys/clock.h>
38#include <sys/time.h>
39#include <sys/bus.h>
40#include <sys/resource.h>
41#include <sys/rman.h>
42
43#include <machine/bus.h>
44#include <machine/cpu.h>
45#include <machine/cpufunc.h>
46#include <machine/frame.h>
47#include <machine/resource.h>
48#include <machine/intr.h>
49
50#include <dev/iicbus/iicbus.h>
51#include <dev/iicbus/iiconf.h>
52
53#include "iicbus_if.h"
54#include "clock_if.h"
55
56#define	DS133X_ADDR		0xd0	/* slave address */
57#define	DS133X_DATE_REG		0x0
58#define	DS133X_CTRL_REG		0x0e
59#define	DS133X_OSCD_FLAG	0x80
60#define	DS133X_OSF_FLAG		0x80
61
62#define	DS133X_24H_FLAG		0x40	/* 24 hours mode. */
63#define	DS133X_PM_FLAG		0x20	/* AM/PM bit. */
64#define	DS133X_CENT_FLAG	0x80	/* Century selector. */
65#define	DS133X_CENT_SHIFT	7
66
67#define	DS1338_REG_CLOCK_HALT	0x00
68#define	DS1338_REG_CONTROL	0x07
69#define	DS1338_CLOCK_HALT	(1 << 7)
70#define	DS1338_OSC_STOP		(1 << 5)
71
72#define	DS1339_REG_CONTROL	0x0E
73#define	DS1339_REG_STATUS	0x0F
74#define	DS1339_OSC_STOP		(1 << 7)
75#define	DS1339_ENABLE_OSC	(1 << 7)
76#define	DS1339_BBSQI		(1 << 5)
77
78#define	HALFSEC			500000000	/* 1/2 of second. */
79
80#define MAX_IIC_DATA_SIZE	7
81
82enum {
83	DS1337,
84	DS1338,
85	DS1339,
86};
87
88struct ds133x_softc {
89	int		sc_type;
90	device_t	sc_dev;
91};
92
93static int
94ds133x_read(device_t dev, uint8_t address, uint8_t *data, uint8_t size)
95{
96	struct iic_msg msg[] = {
97	    { DS133X_ADDR, IIC_M_WR, 1,	&address },
98	    { DS133X_ADDR, IIC_M_RD, size, data },
99	};
100
101	return (iicbus_transfer(dev, msg, 2));
102}
103
104static int
105ds133x_write(device_t dev, uint8_t address, uint8_t *data, uint8_t size)
106{
107	uint8_t buffer[MAX_IIC_DATA_SIZE + 1];
108	struct iic_msg msg[] = {
109		{ DS133X_ADDR, IIC_M_WR, size + 1, buffer },
110	};
111
112	if (size > MAX_IIC_DATA_SIZE)
113		return (ENOMEM);
114
115	buffer[0] = address;
116	memcpy(buffer + 1, data, size);
117
118	return (iicbus_transfer(dev, msg, 1));
119}
120
121static int
122ds133x_detect(device_t dev, int *sc_type)
123{
124	int error;
125	uint8_t reg, orig;
126
127	/*
128	 * Check for DS1338. At address 0x0F this chip has RAM, however
129	 * DS1337 and DS1339 have status register. Bits 6-2 in status
130	 * register will be always read as 0.
131	 */
132
133	if ((error = ds133x_read(dev, DS1339_REG_STATUS, &reg, 1)))
134		return (error);
135
136	orig = reg;
137	reg |= 0x7C;
138
139	if ((error = ds133x_write(dev, DS1339_REG_STATUS, &reg, 1)))
140		return (error);
141
142	if ((error = ds133x_read(dev, DS1339_REG_STATUS, &reg, 1)))
143		return (error);
144
145	if ((reg & 0x7C) != 0) {
146		/* This is DS1338 */
147
148		if ((error = ds133x_write(dev, DS1339_REG_STATUS, &orig, 1)))
149			return (error);
150
151		*sc_type = DS1338;
152
153		return (0);
154	}
155
156	/*
157	 * Now Check for DS1337. Bit 5 in control register of this chip will be
158	 * allways read as 0. In DS1339 changing of this bit is safe until
159	 * chip is powered up.
160	 */
161
162	if ((error = ds133x_read(dev, DS1339_REG_CONTROL, &reg, 1)))
163		return (error);
164
165	orig = reg;
166	reg |= DS1339_BBSQI;
167
168	if ((error = ds133x_write(dev, DS1339_REG_CONTROL, &reg, 1)))
169		return (error);
170
171	if ((error = ds133x_read(dev, DS1339_REG_CONTROL, &reg, 1)))
172		return (error);
173
174	if ((reg & DS1339_BBSQI) != 0) {
175		/* This is DS1339 */
176
177		if ((error = ds133x_write(dev, DS1339_REG_CONTROL, &orig, 1)))
178			return (error);
179
180		*sc_type = DS1339;
181		return (0);
182	}
183
184	/* This is DS1337 */
185	*sc_type = DS1337;
186
187	return (0);
188}
189
190static int
191ds133x_init(device_t dev, uint8_t cs_reg, uint8_t cs_bit, uint8_t osf_reg,
192    uint8_t osf_bit)
193{
194	int error;
195	uint8_t reg;
196
197	if ((error = ds133x_read(dev, cs_reg, &reg, 1)))
198		return (error);
199
200	if (reg & cs_bit) {	/* If clock is stopped - start it */
201		reg &= ~cs_bit;
202		if ((error = ds133x_write(dev, cs_reg, &reg, 1)))
203			return (error);
204	}
205
206	if ((error = ds133x_read(dev, osf_reg, &reg, 1)))
207		return (error);
208
209	if (reg & osf_bit) {	/* Clear oscillator stop flag */
210		device_printf(dev, "RTC oscillator was stopped. Check system"
211		    " time and RTC battery.\n");
212		reg &= ~osf_bit;
213		if ((error = ds133x_write(dev, osf_reg, &reg, 1)))
214			return (error);
215	}
216
217	return (0);
218}
219
220static int
221ds133x_probe(device_t dev)
222{
223	struct ds133x_softc *sc;
224	int error;
225
226	sc = device_get_softc(dev);
227
228	if ((error = ds133x_detect(dev, &sc->sc_type)))
229		return (error);
230
231	switch (sc->sc_type) {
232	case DS1337:
233		device_set_desc(dev, "Dallas Semiconductor DS1337 RTC");
234		break;
235	case DS1338:
236		device_set_desc(dev, "Dallas Semiconductor DS1338 RTC");
237		break;
238	case DS1339:
239		device_set_desc(dev, "Dallas Semiconductor DS1339 RTC");
240		break;
241	default:
242		break;
243	}
244
245	return (0);
246}
247
248static int
249ds133x_attach(device_t dev)
250{
251	struct ds133x_softc *sc = device_get_softc(dev);
252
253	sc->sc_dev = dev;
254
255	if (sc->sc_type == DS1338)
256		ds133x_init(dev, DS1338_REG_CLOCK_HALT, DS1338_CLOCK_HALT,
257		    DS1338_REG_CONTROL, DS1338_OSC_STOP);
258	else
259		ds133x_init(dev, DS1339_REG_CONTROL, DS1339_ENABLE_OSC,
260		    DS1339_REG_STATUS, DS1339_OSC_STOP);
261
262	clock_register(dev, 1000000);
263
264	return (0);
265}
266
267static uint8_t
268ds133x_get_hours(uint8_t val)
269{
270	uint8_t ret;
271
272	if (!(val & DS133X_24H_FLAG))
273		ret = FROMBCD(val & 0x3f);
274	else if (!(val & DS133X_PM_FLAG))
275		ret = FROMBCD(val & 0x1f);
276	else
277		ret = FROMBCD(val & 0x1f) + 12;
278
279	return (ret);
280}
281
282static int
283ds133x_gettime(device_t dev, struct timespec *ts)
284{
285	struct ds133x_softc *sc = device_get_softc(dev);
286	struct clocktime ct;
287	uint8_t date[7];
288	int error;
289
290	error = ds133x_read(dev, DS133X_DATE_REG, date, 7);
291	if (error == 0) {
292		ct.nsec = 0;
293		ct.sec = FROMBCD(date[0] & 0x7f);
294		ct.min = FROMBCD(date[1] & 0x7f);
295		ct.hour = ds133x_get_hours(date[2]);
296		ct.dow = FROMBCD(date[3] & 0x07) - 1;
297		ct.day = FROMBCD(date[4] & 0x3f);
298		ct.mon = FROMBCD(date[5] & 0x1f);
299
300		if (sc->sc_type == DS1338)
301			ct.year = 2000 + FROMBCD(date[6]);
302		else
303			ct.year = 1900 + FROMBCD(date[6]) +
304			    ((date[5] & DS133X_CENT_FLAG) >> DS133X_CENT_SHIFT) * 100;
305
306		error = clock_ct_to_ts(&ct, ts);
307	}
308
309	return (error);
310}
311
312static int
313ds133x_settime(device_t dev, struct timespec *ts)
314{
315	struct ds133x_softc *sc = device_get_softc(dev);
316	struct clocktime ct;
317	uint8_t date[7];
318
319	clock_ts_to_ct(ts, &ct);
320
321	date[0] = TOBCD(ct.nsec >= HALFSEC ? ct.sec + 1 : ct.sec) & 0x7f;
322	date[1] = TOBCD(ct.min) & 0x7f;
323	date[2] = TOBCD(ct.hour) & 0x3f;	/* We use 24-hours mode. */
324	date[3] = TOBCD(ct.dow + 1) & 0x07;
325	date[4] = TOBCD(ct.day) & 0x3f;
326	date[5] = TOBCD(ct.mon) & 0x1f;
327	if (sc->sc_type == DS1338)
328		date[6] = TOBCD(ct.year - 2000);
329	else if (ct.year >= 2000) {
330		date[5] |= DS133X_CENT_FLAG;
331		date[6] = TOBCD(ct.year - 2000);
332	} else
333		date[6] = TOBCD(ct.year - 1900);
334
335	return (ds133x_write(dev, DS133X_DATE_REG, date, 7));
336}
337
338static device_method_t ds133x_methods[] = {
339	DEVMETHOD(device_probe,		ds133x_probe),
340	DEVMETHOD(device_attach,	ds133x_attach),
341
342	DEVMETHOD(clock_gettime,	ds133x_gettime),
343	DEVMETHOD(clock_settime,	ds133x_settime),
344
345	{0, 0},
346};
347
348static driver_t ds133x_driver = {
349	"rtc",
350	ds133x_methods,
351	sizeof(struct ds133x_softc),
352};
353
354static devclass_t ds133x_devclass;
355
356DRIVER_MODULE(ds133x, iicbus, ds133x_driver, ds133x_devclass, 0, 0);
357MODULE_VERSION(ds133x, 1);
358MODULE_DEPEND(ds133x, iicbus, 1, 1, 1);
359