1164420Ssam/*-
2164420Ssam * Copyright (c) 2006 Sam Leffler.  All rights reserved.
3164420Ssam *
4164420Ssam * Redistribution and use in source and binary forms, with or without
5164420Ssam * modification, are permitted provided that the following conditions
6164420Ssam * are met:
7164420Ssam * 1. Redistributions of source code must retain the above copyright
8164420Ssam *    notice, this list of conditions and the following disclaimer.
9164420Ssam * 2. Redistributions in binary form must reproduce the above copyright
10164420Ssam *    notice, this list of conditions and the following disclaimer in the
11164420Ssam *    documentation and/or other materials provided with the distribution.
12164420Ssam *
13164420Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14164420Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15164420Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16164420Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17164420Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18164420Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19164420Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20164420Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21164420Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22164420Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23164420Ssam */
24164420Ssam
25164420Ssam#include <sys/cdefs.h>
26164420Ssam__FBSDID("$FreeBSD$");
27164420Ssam/*
28164420Ssam * Dallas Semiconductor DS1672 RTC sitting on the I2C bus.
29164420Ssam */
30164420Ssam#include <sys/param.h>
31164420Ssam#include <sys/systm.h>
32164420Ssam#include <sys/kernel.h>
33164420Ssam#include <sys/module.h>
34164420Ssam#include <sys/clock.h>
35164420Ssam#include <sys/time.h>
36164420Ssam#include <sys/bus.h>
37164420Ssam#include <sys/resource.h>
38164420Ssam#include <sys/rman.h>
39164420Ssam
40164420Ssam#include <dev/iicbus/iiconf.h>
41164420Ssam
42164420Ssam#include "iicbus_if.h"
43164420Ssam#include "clock_if.h"
44164420Ssam
45164420Ssam#define	IIC_M_WR	0	/* write operation */
46164420Ssam
47164420Ssam#define	DS1672_ADDR	0xd0	/* slave address */
48164420Ssam
49164420Ssam#define	DS1672_COUNTER	0	/* counter (bytes 0-3) */
50164420Ssam#define	DS1672_CTRL	4	/* control (1 byte) */
51164420Ssam#define	DS1672_TRICKLE	5	/* trickle charger (1 byte) */
52164420Ssam
53191322Sstas#define	DS1672_CTRL_EOSC	(1 << 7)	/* Stop/start flag. */
54191322Sstas
55164420Ssam#define NANOSEC		1000000000
56164420Ssam
57191322Sstas#define	MAX_IIC_DATA_SIZE	4
58191322Sstas
59164420Ssamstruct ds1672_softc {
60164420Ssam	device_t		sc_dev;
61164420Ssam};
62164420Ssam
63164420Ssamstatic int
64164420Ssamds1672_probe(device_t dev)
65164420Ssam{
66164420Ssam	/* XXX really probe? */
67164420Ssam	device_set_desc(dev, "Dallas Semiconductor DS1672 RTC");
68186833Snwhitehorn	return (BUS_PROBE_NOWILDCARD);
69164420Ssam}
70164420Ssam
71164420Ssamstatic int
72191322Sstasds1672_read(device_t dev, uint8_t addr, uint8_t *data, uint8_t size)
73191322Sstas{
74191322Sstas	struct iic_msg msgs[2] = {
75191322Sstas	     { DS1672_ADDR, IIC_M_WR, 1, &addr },
76191322Sstas	     { DS1672_ADDR, IIC_M_RD, size, data }
77191322Sstas	};
78191322Sstas
79191322Sstas	return (iicbus_transfer(dev, msgs, 2));
80191322Sstas}
81191322Sstas
82191322Sstasstatic int
83191322Sstasds1672_write(device_t dev, uint8_t addr, uint8_t *data, uint8_t size)
84191322Sstas{
85191322Sstas	uint8_t buffer[MAX_IIC_DATA_SIZE + 1];
86191322Sstas	struct iic_msg msgs[1] = {
87191322Sstas	     { DS1672_ADDR, IIC_M_WR, size + 1, buffer },
88191322Sstas	};
89191322Sstas
90191322Sstas	if (size > MAX_IIC_DATA_SIZE)
91191322Sstas		return (ENOMEM);
92191322Sstas	/* NB: register pointer precedes actual data */
93191322Sstas	buffer[0] = addr;
94191322Sstas	memcpy(buffer + 1, data, size);
95191322Sstas	return (iicbus_transfer(dev, msgs, 1));
96191322Sstas}
97191322Sstas
98191322Sstasstatic int
99191322Sstasds1672_init(device_t dev)
100191322Sstas{
101191322Sstas	uint8_t ctrl;
102191322Sstas	int error;
103191322Sstas
104191322Sstas	error = ds1672_read(dev, DS1672_CTRL, &ctrl, 1);
105191322Sstas	if (error)
106191322Sstas		return (error);
107191322Sstas
108191322Sstas	/*
109191322Sstas	 * Check if oscialltor is not runned.
110191322Sstas	 */
111191322Sstas	if (ctrl & DS1672_CTRL_EOSC) {
112191322Sstas		device_printf(dev, "RTC oscillator was stopped. Check system"
113191322Sstas		    " time and RTC battery.\n");
114191322Sstas		ctrl &= ~DS1672_CTRL_EOSC;	/* Start oscillator. */
115191322Sstas		error = ds1672_write(dev, DS1672_CTRL, &ctrl, 1);
116191322Sstas	}
117191322Sstas	return (error);
118191322Sstas}
119191322Sstas
120191322Sstasstatic int
121164420Ssamds1672_attach(device_t dev)
122164420Ssam{
123164420Ssam	struct ds1672_softc *sc = device_get_softc(dev);
124191322Sstas	int error;
125164420Ssam
126164420Ssam	sc->sc_dev = dev;
127191322Sstas	error = ds1672_init(dev);
128191322Sstas	if (error)
129191322Sstas		return (error);
130164420Ssam	clock_register(dev, 1000);
131164420Ssam	return (0);
132164420Ssam}
133164420Ssam
134164420Ssamstatic int
135164420Ssamds1672_gettime(device_t dev, struct timespec *ts)
136164420Ssam{
137164420Ssam	uint8_t secs[4];
138164420Ssam	int error;
139164420Ssam
140191322Sstas	error = ds1672_read(dev, DS1672_COUNTER, secs, 4);
141164420Ssam	if (error == 0) {
142164420Ssam		/* counter has seconds since epoch */
143164420Ssam		ts->tv_sec = (secs[3] << 24) | (secs[2] << 16)
144164420Ssam			   | (secs[1] <<  8) | (secs[0] <<  0);
145164420Ssam		ts->tv_nsec = NANOSEC / 2;
146164420Ssam	}
147191322Sstas	return (error);
148164420Ssam}
149164420Ssam
150164420Ssamstatic int
151164420Ssamds1672_settime(device_t dev, struct timespec *ts)
152164420Ssam{
153191322Sstas	uint8_t data[4];
154164420Ssam
155191322Sstas	data[0] = (ts->tv_sec >> 0) & 0xff;
156191322Sstas	data[1] = (ts->tv_sec >> 8) & 0xff;
157191322Sstas	data[2] = (ts->tv_sec >> 16) & 0xff;
158191322Sstas	data[3] = (ts->tv_sec >> 24) & 0xff;
159164420Ssam
160191322Sstas	return (ds1672_write(dev, DS1672_COUNTER, data, 4));
161164420Ssam}
162164420Ssam
163164420Ssamstatic device_method_t ds1672_methods[] = {
164164420Ssam	DEVMETHOD(device_probe,		ds1672_probe),
165164420Ssam	DEVMETHOD(device_attach,	ds1672_attach),
166164420Ssam
167164420Ssam	DEVMETHOD(clock_gettime,	ds1672_gettime),
168164420Ssam	DEVMETHOD(clock_settime,	ds1672_settime),
169164420Ssam
170246128Ssbz	DEVMETHOD_END
171164420Ssam};
172164420Ssam
173164420Ssamstatic driver_t ds1672_driver = {
174191369Sstas	"ds1672_rtc",
175164420Ssam	ds1672_methods,
176164420Ssam	sizeof(struct ds1672_softc),
177164420Ssam};
178164420Ssamstatic devclass_t ds1672_devclass;
179164420Ssam
180164420SsamDRIVER_MODULE(ds1672, iicbus, ds1672_driver, ds1672_devclass, 0, 0);
181164420SsamMODULE_VERSION(ds1672, 1);
182164420SsamMODULE_DEPEND(ds1672, iicbus, 1, 1, 1);
183