ds133x.c revision 180811
1/*-
2 * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>,
3 *                    Rafal Jaworowski <raj@FreeBSD.org>.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/dev/iicbus/ds1339.c 180811 2008-07-25 19:34:44Z stas $");
29/*
30 * Dallas Semiconductor DS1339 RTC sitting on the I2C bus.
31 */
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/module.h>
36#include <sys/clock.h>
37#include <sys/time.h>
38#include <sys/bus.h>
39#include <sys/resource.h>
40#include <sys/rman.h>
41
42#include <machine/bus.h>
43#include <machine/cpu.h>
44#include <machine/cpufunc.h>
45#include <machine/frame.h>
46#include <machine/resource.h>
47#include <machine/intr.h>
48
49#include <dev/iicbus/iicbus.h>
50#include <dev/iicbus/iiconf.h>
51
52#include "iicbus_if.h"
53#include "clock_if.h"
54
55#define	DS1339_ADDR		0xd0	/* slave address */
56#define	DS1339_DATE_REG		0x0
57#define	DS1339_CTRL_REG		0x0e
58#define	DS1339_OSCD_FLAG	0x80
59#define	DS1339_OSF_FLAG		0x80
60
61#define	DS1339_24H_FLAG		0x40	/* 24 hours mode. */
62#define	DS1339_PM_FLAG		0x20	/* AM/PM bit. */
63#define	DS1339_CENT_FLAG	0x80	/* Century selector. */
64#define	DS1339_CENT_SHIFT	7
65
66#define	HALFSEC	500000000	/* 1/2 of second. */
67
68struct ds1339_softc {
69	device_t		sc_dev;
70};
71
72static int
73ds1339_probe(device_t dev)
74{
75	struct ds1339_softc *sc;
76	int addr;
77
78	sc = device_get_softc(dev);
79	iicbus_get_addr(dev, &addr);
80	if (addr != DS1339_ADDR) {
81		if (bootverbose)
82			device_printf(dev, "fixed I2C slave address should "
83			    "be 0x%.2x instead of 0x%.2x.\n", DS1339_ADDR,
84			    addr);
85	}
86	device_set_desc(dev, "Dallas Semiconductor DS1339 RTC");
87	return (0);
88}
89
90static int
91ds1339_attach(device_t dev)
92{
93	struct ds1339_softc *sc = device_get_softc(dev);
94	int error;
95
96	sc->sc_dev = dev;
97
98	uint8_t addr[1] = { DS1339_CTRL_REG };
99	uint8_t rdata[2];
100	uint8_t wrdata[3];
101	struct iic_msg rmsgs[2] = {
102	     { DS1339_ADDR, IIC_M_WR, 1, addr },
103	     { DS1339_ADDR, IIC_M_RD, 2, rdata },
104	};
105	struct iic_msg wrmsgs[1] = {
106	     { DS1339_ADDR, IIC_M_WR, 3, wrdata },
107	};
108
109	/* Read control and status registers. */
110	error = iicbus_transfer(dev, rmsgs, 2);
111	if (error != 0) {
112		device_printf(dev, "could not read control registers.\n");
113		return (ENXIO);
114	}
115
116	if ((rdata[0] & DS1339_OSCD_FLAG) != 0)
117		rdata[0] &= ~DS1339_OSCD_FLAG;	/* Enable oscillator
118						 * if disabled.
119						 */
120	if ((rdata[1] & DS1339_OSF_FLAG) != 0)
121		rdata[1] &= ~DS1339_OSF_FLAG;	/* Clear oscillator stop flag */
122
123	/* Write modified registers back. */
124	wrdata[0] = DS1339_CTRL_REG;
125	wrdata[1] = rdata[0];
126	wrdata[2] = rdata[1];
127
128	goto out;
129
130	error = iicbus_transfer(dev, wrmsgs, 1);
131	if (error != 0) {
132		device_printf(dev, "could not write control registers.\n");
133		return (ENXIO);
134	}
135
136out:
137
138	clock_register(dev, 1000000);
139	return (0);
140}
141
142static uint8_t
143ds1339_get_hours(uint8_t val)
144{
145	uint8_t ret;
146
147	if (!(val & DS1339_24H_FLAG))
148		ret = FROMBCD(val & 0x3f);
149	else if (!(val & DS1339_PM_FLAG))
150		ret = FROMBCD(val & 0x1f);
151	else
152		ret = FROMBCD(val & 0x1f) + 12;
153
154	return (ret);
155}
156
157static int
158ds1339_gettime(device_t dev, struct timespec *ts)
159{
160	uint8_t addr[1] = { DS1339_DATE_REG };
161	uint8_t date[7];
162	struct iic_msg msgs[2] = {
163	     { DS1339_ADDR, IIC_M_WR, 1, addr },
164	     { DS1339_ADDR, IIC_M_RD, 7, date },
165	};
166	struct clocktime ct;
167	int error;
168
169	error = iicbus_transfer(dev, msgs, 2);
170	if (error == 0) {
171		ct.nsec = 0;
172		ct.sec = FROMBCD(date[0] & 0x7f);
173		ct.min = FROMBCD(date[1] & 0x7f);
174		ct.hour = ds1339_get_hours(date[2]);
175		ct.dow = FROMBCD(date[3] & 0x07) - 1;
176		ct.day = FROMBCD(date[4] & 0x3f);
177		ct.mon = FROMBCD(date[5] & 0x1f);
178		ct.year = 1900 + FROMBCD(date[6]) +
179		    ((date[5] & DS1339_CENT_FLAG) >> DS1339_CENT_SHIFT) * 100;
180
181		error = clock_ct_to_ts(&ct, ts);
182	}
183
184	return error;
185}
186
187static int
188ds1339_settime(device_t dev, struct timespec *ts)
189{
190	uint8_t data[8] = { DS1339_DATE_REG };	/* Register address. */
191	struct iic_msg msgs[1] = {
192	     { DS1339_ADDR, IIC_M_WR, 8, data },
193	};
194	struct clocktime ct;
195
196	clock_ts_to_ct(ts, &ct);
197
198	data[1] = TOBCD(ct.nsec >= HALFSEC ? ct.sec + 1 : ct.sec) & 0x7f;
199	data[2] = TOBCD(ct.min) & 0x7f;
200	data[3] = TOBCD(ct.hour) & 0x3f;	/* We use 24-hours mode. */
201	data[4] = TOBCD(ct.dow + 1) & 0x07;
202	data[5] = TOBCD(ct.day) & 0x3f;
203	data[6] = TOBCD(ct.mon) & 0x1f;
204	if (ct.year >= 2000) {
205		data[6] |= DS1339_CENT_FLAG;
206		data[7] = TOBCD(ct.year - 2000);
207	} else
208		data[7] = TOBCD(ct.year - 1900);
209
210	return iicbus_transfer(dev, msgs, 1);
211}
212
213static device_method_t ds1339_methods[] = {
214	DEVMETHOD(device_probe,		ds1339_probe),
215	DEVMETHOD(device_attach,	ds1339_attach),
216
217	DEVMETHOD(clock_gettime,	ds1339_gettime),
218	DEVMETHOD(clock_settime,	ds1339_settime),
219
220	{0, 0},
221};
222
223static driver_t ds1339_driver = {
224	"ds1339",
225	ds1339_methods,
226	sizeof(struct ds1339_softc),
227};
228static devclass_t ds1339_devclass;
229
230DRIVER_MODULE(ds1339, iicbus, ds1339_driver, ds1339_devclass, 0, 0);
231MODULE_VERSION(ds1339, 1);
232MODULE_DEPEND(ds1339, iicbus, 1, 1, 1);
233