ds133x.c revision 180811
1168404Spjd/*-
2168404Spjd * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>,
3168404Spjd *                    Rafal Jaworowski <raj@FreeBSD.org>.
4168404Spjd * All rights reserved.
5168404Spjd *
6168404Spjd * Redistribution and use in source and binary forms, with or without
7168404Spjd * modification, are permitted provided that the following conditions
8168404Spjd * are met:
9168404Spjd * 1. Redistributions of source code must retain the above copyright
10168404Spjd *    notice, this list of conditions and the following disclaimer.
11168404Spjd * 2. Redistributions in binary form must reproduce the above copyright
12168404Spjd *    notice, this list of conditions and the following disclaimer in the
13168404Spjd *    documentation and/or other materials provided with the distribution.
14168404Spjd *
15168404Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16168404Spjd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17168404Spjd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18168404Spjd * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19168404Spjd * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20168404Spjd * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21168404Spjd * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22168404Spjd * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23168404Spjd * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24168404Spjd * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25168404Spjd */
26168404Spjd
27168404Spjd#include <sys/cdefs.h>
28168404Spjd__FBSDID("$FreeBSD: head/sys/dev/iicbus/ds1339.c 180811 2008-07-25 19:34:44Z stas $");
29168404Spjd/*
30169303Spjd * Dallas Semiconductor DS1339 RTC sitting on the I2C bus.
31168404Spjd */
32168404Spjd#include <sys/param.h>
33168404Spjd#include <sys/systm.h>
34168404Spjd#include <sys/kernel.h>
35168404Spjd#include <sys/module.h>
36169303Spjd#include <sys/clock.h>
37168404Spjd#include <sys/time.h>
38168404Spjd#include <sys/bus.h>
39168404Spjd#include <sys/resource.h>
40168404Spjd#include <sys/rman.h>
41168404Spjd
42168404Spjd#include <machine/bus.h>
43168404Spjd#include <machine/cpu.h>
44168404Spjd#include <machine/cpufunc.h>
45168404Spjd#include <machine/frame.h>
46168404Spjd#include <machine/resource.h>
47168404Spjd#include <machine/intr.h>
48168404Spjd
49168404Spjd#include <dev/iicbus/iicbus.h>
50168404Spjd#include <dev/iicbus/iiconf.h>
51168404Spjd
52168404Spjd#include "iicbus_if.h"
53168404Spjd#include "clock_if.h"
54168404Spjd
55168404Spjd#define	DS1339_ADDR		0xd0	/* slave address */
56168404Spjd#define	DS1339_DATE_REG		0x0
57168404Spjd#define	DS1339_CTRL_REG		0x0e
58168404Spjd#define	DS1339_OSCD_FLAG	0x80
59168404Spjd#define	DS1339_OSF_FLAG		0x80
60168404Spjd
61168404Spjd#define	DS1339_24H_FLAG		0x40	/* 24 hours mode. */
62168404Spjd#define	DS1339_PM_FLAG		0x20	/* AM/PM bit. */
63168404Spjd#define	DS1339_CENT_FLAG	0x80	/* Century selector. */
64168404Spjd#define	DS1339_CENT_SHIFT	7
65168404Spjd
66168404Spjd#define	HALFSEC	500000000	/* 1/2 of second. */
67168404Spjd
68168404Spjdstruct ds1339_softc {
69168404Spjd	device_t		sc_dev;
70168404Spjd};
71168404Spjd
72168404Spjdstatic int
73168404Spjdds1339_probe(device_t dev)
74168404Spjd{
75168404Spjd	struct ds1339_softc *sc;
76168404Spjd	int addr;
77168404Spjd
78168404Spjd	sc = device_get_softc(dev);
79168404Spjd	iicbus_get_addr(dev, &addr);
80168404Spjd	if (addr != DS1339_ADDR) {
81168404Spjd		if (bootverbose)
82168404Spjd			device_printf(dev, "fixed I2C slave address should "
83168404Spjd			    "be 0x%.2x instead of 0x%.2x.\n", DS1339_ADDR,
84168404Spjd			    addr);
85168404Spjd	}
86168404Spjd	device_set_desc(dev, "Dallas Semiconductor DS1339 RTC");
87168404Spjd	return (0);
88169303Spjd}
89169303Spjd
90169087Spjdstatic int
91168404Spjdds1339_attach(device_t dev)
92168404Spjd{
93168404Spjd	struct ds1339_softc *sc = device_get_softc(dev);
94168404Spjd	int error;
95168404Spjd
96169087Spjd	sc->sc_dev = dev;
97168404Spjd
98168404Spjd	uint8_t addr[1] = { DS1339_CTRL_REG };
99185029Spjd	uint8_t rdata[2];
100185029Spjd	uint8_t wrdata[3];
101185029Spjd	struct iic_msg rmsgs[2] = {
102168404Spjd	     { DS1339_ADDR, IIC_M_WR, 1, addr },
103168404Spjd	     { DS1339_ADDR, IIC_M_RD, 2, rdata },
104168404Spjd	};
105168404Spjd	struct iic_msg wrmsgs[1] = {
106168404Spjd	     { DS1339_ADDR, IIC_M_WR, 3, wrdata },
107168404Spjd	};
108168404Spjd
109168404Spjd	/* Read control and status registers. */
110168404Spjd	error = iicbus_transfer(dev, rmsgs, 2);
111168404Spjd	if (error != 0) {
112168404Spjd		device_printf(dev, "could not read control registers.\n");
113168404Spjd		return (ENXIO);
114168404Spjd	}
115169303Spjd
116169303Spjd	if ((rdata[0] & DS1339_OSCD_FLAG) != 0)
117169303Spjd		rdata[0] &= ~DS1339_OSCD_FLAG;	/* Enable oscillator
118169303Spjd						 * if disabled.
119169303Spjd						 */
120168404Spjd	if ((rdata[1] & DS1339_OSF_FLAG) != 0)
121168404Spjd		rdata[1] &= ~DS1339_OSF_FLAG;	/* Clear oscillator stop flag */
122168404Spjd
123168404Spjd	/* Write modified registers back. */
124168404Spjd	wrdata[0] = DS1339_CTRL_REG;
125168404Spjd	wrdata[1] = rdata[0];
126168404Spjd	wrdata[2] = rdata[1];
127168404Spjd
128168404Spjd	goto out;
129168404Spjd
130168404Spjd	error = iicbus_transfer(dev, wrmsgs, 1);
131168404Spjd	if (error != 0) {
132168404Spjd		device_printf(dev, "could not write control registers.\n");
133168404Spjd		return (ENXIO);
134168404Spjd	}
135168404Spjd
136168404Spjdout:
137168404Spjd
138168404Spjd	clock_register(dev, 1000000);
139168404Spjd	return (0);
140168404Spjd}
141168404Spjd
142168404Spjdstatic uint8_t
143168404Spjdds1339_get_hours(uint8_t val)
144168404Spjd{
145168404Spjd	uint8_t ret;
146168404Spjd
147168404Spjd	if (!(val & DS1339_24H_FLAG))
148168404Spjd		ret = FROMBCD(val & 0x3f);
149168404Spjd	else if (!(val & DS1339_PM_FLAG))
150168404Spjd		ret = FROMBCD(val & 0x1f);
151168404Spjd	else
152168404Spjd		ret = FROMBCD(val & 0x1f) + 12;
153168404Spjd
154168404Spjd	return (ret);
155168404Spjd}
156168404Spjd
157168404Spjdstatic int
158168404Spjdds1339_gettime(device_t dev, struct timespec *ts)
159168404Spjd{
160168404Spjd	uint8_t addr[1] = { DS1339_DATE_REG };
161168404Spjd	uint8_t date[7];
162168404Spjd	struct iic_msg msgs[2] = {
163168404Spjd	     { DS1339_ADDR, IIC_M_WR, 1, addr },
164168404Spjd	     { DS1339_ADDR, IIC_M_RD, 7, date },
165168404Spjd	};
166168404Spjd	struct clocktime ct;
167168404Spjd	int error;
168168404Spjd
169168404Spjd	error = iicbus_transfer(dev, msgs, 2);
170168404Spjd	if (error == 0) {
171168404Spjd		ct.nsec = 0;
172168404Spjd		ct.sec = FROMBCD(date[0] & 0x7f);
173168404Spjd		ct.min = FROMBCD(date[1] & 0x7f);
174168404Spjd		ct.hour = ds1339_get_hours(date[2]);
175168404Spjd		ct.dow = FROMBCD(date[3] & 0x07) - 1;
176168404Spjd		ct.day = FROMBCD(date[4] & 0x3f);
177168404Spjd		ct.mon = FROMBCD(date[5] & 0x1f);
178168404Spjd		ct.year = 1900 + FROMBCD(date[6]) +
179168404Spjd		    ((date[5] & DS1339_CENT_FLAG) >> DS1339_CENT_SHIFT) * 100;
180168404Spjd
181168404Spjd		error = clock_ct_to_ts(&ct, ts);
182168404Spjd	}
183168404Spjd
184168404Spjd	return error;
185168404Spjd}
186168404Spjd
187168404Spjdstatic int
188168404Spjdds1339_settime(device_t dev, struct timespec *ts)
189168404Spjd{
190168404Spjd	uint8_t data[8] = { DS1339_DATE_REG };	/* Register address. */
191168404Spjd	struct iic_msg msgs[1] = {
192168404Spjd	     { DS1339_ADDR, IIC_M_WR, 8, data },
193168404Spjd	};
194168404Spjd	struct clocktime ct;
195168404Spjd
196168404Spjd	clock_ts_to_ct(ts, &ct);
197196457Spjd
198196457Spjd	data[1] = TOBCD(ct.nsec >= HALFSEC ? ct.sec + 1 : ct.sec) & 0x7f;
199196457Spjd	data[2] = TOBCD(ct.min) & 0x7f;
200196457Spjd	data[3] = TOBCD(ct.hour) & 0x3f;	/* We use 24-hours mode. */
201168404Spjd	data[4] = TOBCD(ct.dow + 1) & 0x07;
202168404Spjd	data[5] = TOBCD(ct.day) & 0x3f;
203168404Spjd	data[6] = TOBCD(ct.mon) & 0x1f;
204168404Spjd	if (ct.year >= 2000) {
205168404Spjd		data[6] |= DS1339_CENT_FLAG;
206168404Spjd		data[7] = TOBCD(ct.year - 2000);
207168404Spjd	} else
208168404Spjd		data[7] = TOBCD(ct.year - 1900);
209168404Spjd
210196458Spjd	return iicbus_transfer(dev, msgs, 1);
211168404Spjd}
212168404Spjd
213168404Spjdstatic device_method_t ds1339_methods[] = {
214168404Spjd	DEVMETHOD(device_probe,		ds1339_probe),
215168404Spjd	DEVMETHOD(device_attach,	ds1339_attach),
216168404Spjd
217168404Spjd	DEVMETHOD(clock_gettime,	ds1339_gettime),
218168404Spjd	DEVMETHOD(clock_settime,	ds1339_settime),
219168404Spjd
220168404Spjd	{0, 0},
221168404Spjd};
222168404Spjd
223168404Spjdstatic driver_t ds1339_driver = {
224168404Spjd	"ds1339",
225168404Spjd	ds1339_methods,
226168404Spjd	sizeof(struct ds1339_softc),
227168404Spjd};
228168404Spjdstatic devclass_t ds1339_devclass;
229168404Spjd
230168404SpjdDRIVER_MODULE(ds1339, iicbus, ds1339_driver, ds1339_devclass, 0, 0);
231168404SpjdMODULE_VERSION(ds1339, 1);
232185029SpjdMODULE_DEPEND(ds1339, iicbus, 1, 1, 1);
233168404Spjd