ds133x.c revision 182870
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 182870 2008-09-08 10:40:48Z 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_DEVNAME		"rtc"
57
58#define	DS133X_ADDR		0xd0	/* slave address */
59#define	DS133X_DATE_REG		0x0
60#define	DS133X_CTRL_REG		0x0e
61#define	DS133X_OSCD_FLAG	0x80
62#define	DS133X_OSF_FLAG		0x80
63
64#define	DS133X_24H_FLAG		0x40	/* 24 hours mode. */
65#define	DS133X_PM_FLAG		0x20	/* AM/PM bit. */
66#define	DS133X_CENT_FLAG	0x80	/* Century selector. */
67#define	DS133X_CENT_SHIFT	7
68
69#define	DS1338_REG_CLOCK_HALT	0x00
70#define	DS1338_REG_CONTROL	0x07
71#define	DS1338_CLOCK_HALT	(1 << 7)
72#define	DS1338_OSC_STOP		(1 << 5)
73
74#define	DS1339_REG_CONTROL	0x0E
75#define	DS1339_REG_STATUS	0x0F
76#define	DS1339_OSC_STOP		(1 << 7)
77#define	DS1339_ENABLE_OSC	(1 << 7)
78#define	DS1339_BBSQI		(1 << 5)
79
80#define	HALFSEC			500000000	/* 1/2 of second. */
81
82#define MAX_IIC_DATA_SIZE	7
83
84enum {
85	DS1337,
86	DS1338,
87	DS1339,
88};
89
90struct ds133x_softc {
91	int		sc_type;
92	device_t	sc_dev;
93};
94
95static int
96ds133x_read(device_t dev, uint8_t address, uint8_t *data, uint8_t size)
97{
98	struct iic_msg msg[] = {
99	    { DS133X_ADDR, IIC_M_WR, 1,	&address },
100	    { DS133X_ADDR, IIC_M_RD, size, data },
101	};
102
103	return (iicbus_transfer(dev, msg, 2));
104}
105
106static int
107ds133x_write(device_t dev, uint8_t address, uint8_t *data, uint8_t size)
108{
109	uint8_t buffer[MAX_IIC_DATA_SIZE + 1];
110	struct iic_msg msg[] = {
111		{ DS133X_ADDR, IIC_M_WR, size + 1, buffer },
112	};
113
114	if (size > MAX_IIC_DATA_SIZE)
115		return (ENOMEM);
116
117	buffer[0] = address;
118	memcpy(buffer + 1, data, size);
119
120	return (iicbus_transfer(dev, msg, 1));
121}
122
123static int
124ds133x_detect(device_t dev, int *sc_type)
125{
126	int error;
127	uint8_t reg, orig;
128
129	/*
130	 * Check for DS1338. At address 0x0F this chip has RAM, however
131	 * DS1337 and DS1339 have status register. Bits 6-2 in status
132	 * register will be always read as 0.
133	 */
134
135	if ((error = ds133x_read(dev, DS1339_REG_STATUS, &reg, 1)))
136		return (error);
137
138	orig = reg;
139	reg |= 0x7C;
140
141	if ((error = ds133x_write(dev, DS1339_REG_STATUS, &reg, 1)))
142		return (error);
143
144	if ((error = ds133x_read(dev, DS1339_REG_STATUS, &reg, 1)))
145		return (error);
146
147	if ((reg & 0x7C) != 0) {
148		/* This is DS1338 */
149
150		if ((error = ds133x_write(dev, DS1339_REG_STATUS, &orig, 1)))
151			return (error);
152
153		*sc_type = DS1338;
154
155		return (0);
156	}
157
158	/*
159	 * Now Check for DS1337. Bit 5 in control register of this chip will be
160	 * allways read as 0. In DS1339 changing of this bit is safe until
161	 * chip is powered up.
162	 */
163
164	if ((error = ds133x_read(dev, DS1339_REG_CONTROL, &reg, 1)))
165		return (error);
166
167	orig = reg;
168	reg |= DS1339_BBSQI;
169
170	if ((error = ds133x_write(dev, DS1339_REG_CONTROL, &reg, 1)))
171		return (error);
172
173	if ((error = ds133x_read(dev, DS1339_REG_CONTROL, &reg, 1)))
174		return (error);
175
176	if ((reg & DS1339_BBSQI) != 0) {
177		/* This is DS1339 */
178
179		if ((error = ds133x_write(dev, DS1339_REG_CONTROL, &orig, 1)))
180			return (error);
181
182		*sc_type = DS1339;
183		return (0);
184	}
185
186	/* This is DS1337 */
187	*sc_type = DS1337;
188
189	return (0);
190}
191
192static int
193ds133x_init(device_t dev, uint8_t cs_reg, uint8_t cs_bit, uint8_t osf_reg,
194    uint8_t osf_bit)
195{
196	int error;
197	uint8_t reg;
198
199	if ((error = ds133x_read(dev, cs_reg, &reg, 1)))
200		return (error);
201
202	if (reg & cs_bit) {	/* If clock is stopped - start it */
203		reg &= ~cs_bit;
204		if ((error = ds133x_write(dev, cs_reg, &reg, 1)))
205			return (error);
206	}
207
208	if ((error = ds133x_read(dev, osf_reg, &reg, 1)))
209		return (error);
210
211	if (reg & osf_bit) {	/* Clear oscillator stop flag */
212		device_printf(dev, "RTC oscillator was stopped. Check system"
213		    " time and RTC battery.\n");
214		reg &= ~osf_bit;
215		if ((error = ds133x_write(dev, osf_reg, &reg, 1)))
216			return (error);
217	}
218
219	return (0);
220}
221
222
223static void
224ds133x_identify(driver_t *driver, device_t parent)
225{
226
227	if (device_find_child(parent, DS133X_DEVNAME, -1) == NULL)
228		BUS_ADD_CHILD(parent, 0, DS133X_DEVNAME, -1);
229}
230
231static int
232ds133x_probe(device_t dev)
233{
234	struct ds133x_softc *sc;
235	int error;
236
237	sc = device_get_softc(dev);
238
239	if ((error = ds133x_detect(dev, &sc->sc_type)))
240		return (error);
241
242	switch (sc->sc_type) {
243	case DS1337:
244		device_set_desc(dev, "Dallas Semiconductor DS1337 RTC");
245		break;
246	case DS1338:
247		device_set_desc(dev, "Dallas Semiconductor DS1338 RTC");
248		break;
249	case DS1339:
250		device_set_desc(dev, "Dallas Semiconductor DS1339 RTC");
251		break;
252	default:
253		break;
254	}
255
256	return (0);
257}
258
259static int
260ds133x_attach(device_t dev)
261{
262	struct ds133x_softc *sc = device_get_softc(dev);
263
264	sc->sc_dev = dev;
265
266	if (sc->sc_type == DS1338)
267		ds133x_init(dev, DS1338_REG_CLOCK_HALT, DS1338_CLOCK_HALT,
268		    DS1338_REG_CONTROL, DS1338_OSC_STOP);
269	else
270		ds133x_init(dev, DS1339_REG_CONTROL, DS1339_ENABLE_OSC,
271		    DS1339_REG_STATUS, DS1339_OSC_STOP);
272
273	clock_register(dev, 1000000);
274
275	return (0);
276}
277
278static uint8_t
279ds133x_get_hours(uint8_t val)
280{
281	uint8_t ret;
282
283	if (!(val & DS133X_24H_FLAG))
284		ret = FROMBCD(val & 0x3f);
285	else if (!(val & DS133X_PM_FLAG))
286		ret = FROMBCD(val & 0x1f);
287	else
288		ret = FROMBCD(val & 0x1f) + 12;
289
290	return (ret);
291}
292
293static int
294ds133x_gettime(device_t dev, struct timespec *ts)
295{
296	struct ds133x_softc *sc = device_get_softc(dev);
297	struct clocktime ct;
298	uint8_t date[7];
299	int error;
300
301	error = ds133x_read(dev, DS133X_DATE_REG, date, 7);
302	if (error == 0) {
303		ct.nsec = 0;
304		ct.sec = FROMBCD(date[0] & 0x7f);
305		ct.min = FROMBCD(date[1] & 0x7f);
306		ct.hour = ds133x_get_hours(date[2]);
307		ct.dow = FROMBCD(date[3] & 0x07) - 1;
308		ct.day = FROMBCD(date[4] & 0x3f);
309		ct.mon = FROMBCD(date[5] & 0x1f);
310
311		if (sc->sc_type == DS1338)
312			ct.year = 2000 + FROMBCD(date[6]);
313		else
314			ct.year = 1900 + FROMBCD(date[6]) +
315			    ((date[5] & DS133X_CENT_FLAG) >> DS133X_CENT_SHIFT) * 100;
316
317		error = clock_ct_to_ts(&ct, ts);
318	}
319
320	return (error);
321}
322
323static int
324ds133x_settime(device_t dev, struct timespec *ts)
325{
326	struct ds133x_softc *sc = device_get_softc(dev);
327	struct clocktime ct;
328	uint8_t date[7];
329
330	clock_ts_to_ct(ts, &ct);
331
332	date[0] = TOBCD(ct.nsec >= HALFSEC ? ct.sec + 1 : ct.sec) & 0x7f;
333	date[1] = TOBCD(ct.min) & 0x7f;
334	date[2] = TOBCD(ct.hour) & 0x3f;	/* We use 24-hours mode. */
335	date[3] = TOBCD(ct.dow + 1) & 0x07;
336	date[4] = TOBCD(ct.day) & 0x3f;
337	date[5] = TOBCD(ct.mon) & 0x1f;
338	if (sc->sc_type == DS1338)
339		date[6] = TOBCD(ct.year - 2000);
340	else if (ct.year >= 2000) {
341		date[5] |= DS133X_CENT_FLAG;
342		date[6] = TOBCD(ct.year - 2000);
343	} else
344		date[6] = TOBCD(ct.year - 1900);
345
346	return (ds133x_write(dev, DS133X_DATE_REG, date, 7));
347}
348
349static device_method_t ds133x_methods[] = {
350	DEVMETHOD(device_identify,	ds133x_identify),
351	DEVMETHOD(device_probe,		ds133x_probe),
352	DEVMETHOD(device_attach,	ds133x_attach),
353
354	DEVMETHOD(clock_gettime,	ds133x_gettime),
355	DEVMETHOD(clock_settime,	ds133x_settime),
356
357	{0, 0},
358};
359
360static driver_t ds133x_driver = {
361	DS133X_DEVNAME,
362	ds133x_methods,
363	sizeof(struct ds133x_softc),
364};
365
366static devclass_t ds133x_devclass;
367
368DRIVER_MODULE(ds133x, iicbus, ds133x_driver, ds133x_devclass, 0, 0);
369MODULE_VERSION(ds133x, 1);
370MODULE_DEPEND(ds133x, iicbus, 1, 1, 1);
371