1180811Sstas/*-
2180811Sstas * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>,
3181602Sraj *                    Rafal Jaworowski <raj@FreeBSD.org>,
4181602Sraj *                    Piotr Ziecik <kosmo@semihalf.com>.
5180811Sstas * All rights reserved.
6180811Sstas *
7180811Sstas * Redistribution and use in source and binary forms, with or without
8180811Sstas * modification, are permitted provided that the following conditions
9180811Sstas * are met:
10180811Sstas * 1. Redistributions of source code must retain the above copyright
11180811Sstas *    notice, this list of conditions and the following disclaimer.
12180811Sstas * 2. Redistributions in binary form must reproduce the above copyright
13180811Sstas *    notice, this list of conditions and the following disclaimer in the
14180811Sstas *    documentation and/or other materials provided with the distribution.
15180811Sstas *
16180811Sstas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17180811Sstas * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18180811Sstas * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19180811Sstas * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20180811Sstas * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21180811Sstas * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22180811Sstas * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23180811Sstas * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24180811Sstas * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25180811Sstas * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26180811Sstas */
27180811Sstas
28180811Sstas#include <sys/cdefs.h>
29180811Sstas__FBSDID("$FreeBSD$");
30180811Sstas/*
31181602Sraj * Dallas Semiconductor DS133X RTC sitting on the I2C bus.
32180811Sstas */
33180811Sstas#include <sys/param.h>
34180811Sstas#include <sys/systm.h>
35180811Sstas#include <sys/kernel.h>
36180811Sstas#include <sys/module.h>
37180811Sstas#include <sys/clock.h>
38180811Sstas#include <sys/time.h>
39180811Sstas#include <sys/bus.h>
40180811Sstas#include <sys/resource.h>
41180811Sstas#include <sys/rman.h>
42180811Sstas
43180811Sstas#include <dev/iicbus/iicbus.h>
44180811Sstas#include <dev/iicbus/iiconf.h>
45180811Sstas
46180811Sstas#include "iicbus_if.h"
47180811Sstas#include "clock_if.h"
48180811Sstas
49191369Sstas#define DS133X_DEVNAME		"ds133x_rtc"
50182870Sraj
51181602Sraj#define	DS133X_ADDR		0xd0	/* slave address */
52181602Sraj#define	DS133X_DATE_REG		0x0
53181602Sraj#define	DS133X_CTRL_REG		0x0e
54181602Sraj#define	DS133X_OSCD_FLAG	0x80
55181602Sraj#define	DS133X_OSF_FLAG		0x80
56180811Sstas
57181602Sraj#define	DS133X_24H_FLAG		0x40	/* 24 hours mode. */
58181602Sraj#define	DS133X_PM_FLAG		0x20	/* AM/PM bit. */
59181602Sraj#define	DS133X_CENT_FLAG	0x80	/* Century selector. */
60181602Sraj#define	DS133X_CENT_SHIFT	7
61180811Sstas
62181602Sraj#define	DS1338_REG_CLOCK_HALT	0x00
63181602Sraj#define	DS1338_REG_CONTROL	0x07
64181602Sraj#define	DS1338_CLOCK_HALT	(1 << 7)
65181602Sraj#define	DS1338_OSC_STOP		(1 << 5)
66180811Sstas
67181602Sraj#define	DS1339_REG_CONTROL	0x0E
68181602Sraj#define	DS1339_REG_STATUS	0x0F
69181602Sraj#define	DS1339_OSC_STOP		(1 << 7)
70181602Sraj#define	DS1339_ENABLE_OSC	(1 << 7)
71181602Sraj#define	DS1339_BBSQI		(1 << 5)
72181602Sraj
73181602Sraj#define	HALFSEC			500000000	/* 1/2 of second. */
74181602Sraj
75181602Sraj#define MAX_IIC_DATA_SIZE	7
76181602Sraj
77181602Srajenum {
78181602Sraj	DS1337,
79181602Sraj	DS1338,
80181602Sraj	DS1339,
81180811Sstas};
82180811Sstas
83181602Srajstruct ds133x_softc {
84181602Sraj	int		sc_type;
85181602Sraj	device_t	sc_dev;
86181602Sraj};
87181602Sraj
88180811Sstasstatic int
89181602Srajds133x_read(device_t dev, uint8_t address, uint8_t *data, uint8_t size)
90180811Sstas{
91181602Sraj	struct iic_msg msg[] = {
92181602Sraj	    { DS133X_ADDR, IIC_M_WR, 1,	&address },
93181602Sraj	    { DS133X_ADDR, IIC_M_RD, size, data },
94181602Sraj	};
95180811Sstas
96181602Sraj	return (iicbus_transfer(dev, msg, 2));
97181602Sraj}
98181602Sraj
99181602Srajstatic int
100181602Srajds133x_write(device_t dev, uint8_t address, uint8_t *data, uint8_t size)
101181602Sraj{
102181602Sraj	uint8_t buffer[MAX_IIC_DATA_SIZE + 1];
103181602Sraj	struct iic_msg msg[] = {
104181602Sraj		{ DS133X_ADDR, IIC_M_WR, size + 1, buffer },
105181602Sraj	};
106181602Sraj
107181602Sraj	if (size > MAX_IIC_DATA_SIZE)
108181602Sraj		return (ENOMEM);
109181602Sraj
110181602Sraj	buffer[0] = address;
111181602Sraj	memcpy(buffer + 1, data, size);
112181602Sraj
113181602Sraj	return (iicbus_transfer(dev, msg, 1));
114181602Sraj}
115181602Sraj
116181602Srajstatic int
117181602Srajds133x_detect(device_t dev, int *sc_type)
118181602Sraj{
119181602Sraj	int error;
120181602Sraj	uint8_t reg, orig;
121181602Sraj
122181602Sraj	/*
123181602Sraj	 * Check for DS1338. At address 0x0F this chip has RAM, however
124181602Sraj	 * DS1337 and DS1339 have status register. Bits 6-2 in status
125181602Sraj	 * register will be always read as 0.
126181602Sraj	 */
127181602Sraj
128181602Sraj	if ((error = ds133x_read(dev, DS1339_REG_STATUS, &reg, 1)))
129181602Sraj		return (error);
130181602Sraj
131181602Sraj	orig = reg;
132181602Sraj	reg |= 0x7C;
133181602Sraj
134181602Sraj	if ((error = ds133x_write(dev, DS1339_REG_STATUS, &reg, 1)))
135181602Sraj		return (error);
136181602Sraj
137181602Sraj	if ((error = ds133x_read(dev, DS1339_REG_STATUS, &reg, 1)))
138181602Sraj		return (error);
139181602Sraj
140181602Sraj	if ((reg & 0x7C) != 0) {
141181602Sraj		/* This is DS1338 */
142181602Sraj
143181602Sraj		if ((error = ds133x_write(dev, DS1339_REG_STATUS, &orig, 1)))
144181602Sraj			return (error);
145181602Sraj
146181602Sraj		*sc_type = DS1338;
147181602Sraj
148181602Sraj		return (0);
149180811Sstas	}
150181602Sraj
151181602Sraj	/*
152181602Sraj	 * Now Check for DS1337. Bit 5 in control register of this chip will be
153181602Sraj	 * allways read as 0. In DS1339 changing of this bit is safe until
154181602Sraj	 * chip is powered up.
155181602Sraj	 */
156181602Sraj
157181602Sraj	if ((error = ds133x_read(dev, DS1339_REG_CONTROL, &reg, 1)))
158181602Sraj		return (error);
159181602Sraj
160181602Sraj	orig = reg;
161181602Sraj	reg |= DS1339_BBSQI;
162181602Sraj
163181602Sraj	if ((error = ds133x_write(dev, DS1339_REG_CONTROL, &reg, 1)))
164181602Sraj		return (error);
165181602Sraj
166181602Sraj	if ((error = ds133x_read(dev, DS1339_REG_CONTROL, &reg, 1)))
167181602Sraj		return (error);
168181602Sraj
169181602Sraj	if ((reg & DS1339_BBSQI) != 0) {
170181602Sraj		/* This is DS1339 */
171181602Sraj
172181602Sraj		if ((error = ds133x_write(dev, DS1339_REG_CONTROL, &orig, 1)))
173181602Sraj			return (error);
174181602Sraj
175181602Sraj		*sc_type = DS1339;
176181602Sraj		return (0);
177181602Sraj	}
178181602Sraj
179181602Sraj	/* This is DS1337 */
180181602Sraj	*sc_type = DS1337;
181181602Sraj
182180811Sstas	return (0);
183180811Sstas}
184180811Sstas
185180811Sstasstatic int
186181602Srajds133x_init(device_t dev, uint8_t cs_reg, uint8_t cs_bit, uint8_t osf_reg,
187181602Sraj    uint8_t osf_bit)
188180811Sstas{
189180811Sstas	int error;
190181602Sraj	uint8_t reg;
191180811Sstas
192181602Sraj	if ((error = ds133x_read(dev, cs_reg, &reg, 1)))
193181602Sraj		return (error);
194180811Sstas
195181602Sraj	if (reg & cs_bit) {	/* If clock is stopped - start it */
196181602Sraj		reg &= ~cs_bit;
197181602Sraj		if ((error = ds133x_write(dev, cs_reg, &reg, 1)))
198181602Sraj			return (error);
199181602Sraj	}
200180811Sstas
201181602Sraj	if ((error = ds133x_read(dev, osf_reg, &reg, 1)))
202181602Sraj		return (error);
203181602Sraj
204181602Sraj	if (reg & osf_bit) {	/* Clear oscillator stop flag */
205181602Sraj		device_printf(dev, "RTC oscillator was stopped. Check system"
206181602Sraj		    " time and RTC battery.\n");
207181602Sraj		reg &= ~osf_bit;
208181602Sraj		if ((error = ds133x_write(dev, osf_reg, &reg, 1)))
209181602Sraj			return (error);
210180811Sstas	}
211180811Sstas
212181602Sraj	return (0);
213181602Sraj}
214180811Sstas
215182870Sraj
216182870Srajstatic void
217182870Srajds133x_identify(driver_t *driver, device_t parent)
218182870Sraj{
219182870Sraj
220182870Sraj	if (device_find_child(parent, DS133X_DEVNAME, -1) == NULL)
221182870Sraj		BUS_ADD_CHILD(parent, 0, DS133X_DEVNAME, -1);
222182870Sraj}
223182870Sraj
224181602Srajstatic int
225181602Srajds133x_probe(device_t dev)
226181602Sraj{
227181602Sraj	struct ds133x_softc *sc;
228181602Sraj	int error;
229180811Sstas
230181602Sraj	sc = device_get_softc(dev);
231180811Sstas
232181602Sraj	if ((error = ds133x_detect(dev, &sc->sc_type)))
233181602Sraj		return (error);
234181602Sraj
235181602Sraj	switch (sc->sc_type) {
236181602Sraj	case DS1337:
237181602Sraj		device_set_desc(dev, "Dallas Semiconductor DS1337 RTC");
238181602Sraj		break;
239181602Sraj	case DS1338:
240181602Sraj		device_set_desc(dev, "Dallas Semiconductor DS1338 RTC");
241181602Sraj		break;
242181602Sraj	case DS1339:
243181602Sraj		device_set_desc(dev, "Dallas Semiconductor DS1339 RTC");
244181602Sraj		break;
245181602Sraj	default:
246181602Sraj		break;
247180811Sstas	}
248180811Sstas
249181602Sraj	return (0);
250181602Sraj}
251180811Sstas
252181602Srajstatic int
253181602Srajds133x_attach(device_t dev)
254181602Sraj{
255181602Sraj	struct ds133x_softc *sc = device_get_softc(dev);
256181602Sraj
257181602Sraj	sc->sc_dev = dev;
258181602Sraj
259181602Sraj	if (sc->sc_type == DS1338)
260181602Sraj		ds133x_init(dev, DS1338_REG_CLOCK_HALT, DS1338_CLOCK_HALT,
261181602Sraj		    DS1338_REG_CONTROL, DS1338_OSC_STOP);
262181602Sraj	else
263181602Sraj		ds133x_init(dev, DS1339_REG_CONTROL, DS1339_ENABLE_OSC,
264181602Sraj		    DS1339_REG_STATUS, DS1339_OSC_STOP);
265181602Sraj
266180811Sstas	clock_register(dev, 1000000);
267181602Sraj
268180811Sstas	return (0);
269180811Sstas}
270180811Sstas
271180811Sstasstatic uint8_t
272181602Srajds133x_get_hours(uint8_t val)
273180811Sstas{
274180811Sstas	uint8_t ret;
275180811Sstas
276181602Sraj	if (!(val & DS133X_24H_FLAG))
277180811Sstas		ret = FROMBCD(val & 0x3f);
278181602Sraj	else if (!(val & DS133X_PM_FLAG))
279180811Sstas		ret = FROMBCD(val & 0x1f);
280180811Sstas	else
281180811Sstas		ret = FROMBCD(val & 0x1f) + 12;
282180811Sstas
283180811Sstas	return (ret);
284180811Sstas}
285180811Sstas
286180811Sstasstatic int
287181602Srajds133x_gettime(device_t dev, struct timespec *ts)
288180811Sstas{
289181602Sraj	struct ds133x_softc *sc = device_get_softc(dev);
290181602Sraj	struct clocktime ct;
291180811Sstas	uint8_t date[7];
292180811Sstas	int error;
293180811Sstas
294181602Sraj	error = ds133x_read(dev, DS133X_DATE_REG, date, 7);
295180811Sstas	if (error == 0) {
296180811Sstas		ct.nsec = 0;
297180811Sstas		ct.sec = FROMBCD(date[0] & 0x7f);
298180811Sstas		ct.min = FROMBCD(date[1] & 0x7f);
299181602Sraj		ct.hour = ds133x_get_hours(date[2]);
300180811Sstas		ct.dow = FROMBCD(date[3] & 0x07) - 1;
301180811Sstas		ct.day = FROMBCD(date[4] & 0x3f);
302180811Sstas		ct.mon = FROMBCD(date[5] & 0x1f);
303180811Sstas
304181602Sraj		if (sc->sc_type == DS1338)
305181602Sraj			ct.year = 2000 + FROMBCD(date[6]);
306181602Sraj		else
307181602Sraj			ct.year = 1900 + FROMBCD(date[6]) +
308181602Sraj			    ((date[5] & DS133X_CENT_FLAG) >> DS133X_CENT_SHIFT) * 100;
309181602Sraj
310180811Sstas		error = clock_ct_to_ts(&ct, ts);
311180811Sstas	}
312180811Sstas
313181602Sraj	return (error);
314180811Sstas}
315180811Sstas
316180811Sstasstatic int
317181602Srajds133x_settime(device_t dev, struct timespec *ts)
318180811Sstas{
319181602Sraj	struct ds133x_softc *sc = device_get_softc(dev);
320180811Sstas	struct clocktime ct;
321181602Sraj	uint8_t date[7];
322180811Sstas
323180811Sstas	clock_ts_to_ct(ts, &ct);
324180811Sstas
325181602Sraj	date[0] = TOBCD(ct.nsec >= HALFSEC ? ct.sec + 1 : ct.sec) & 0x7f;
326181602Sraj	date[1] = TOBCD(ct.min) & 0x7f;
327181602Sraj	date[2] = TOBCD(ct.hour) & 0x3f;	/* We use 24-hours mode. */
328181602Sraj	date[3] = TOBCD(ct.dow + 1) & 0x07;
329181602Sraj	date[4] = TOBCD(ct.day) & 0x3f;
330181602Sraj	date[5] = TOBCD(ct.mon) & 0x1f;
331181602Sraj	if (sc->sc_type == DS1338)
332181602Sraj		date[6] = TOBCD(ct.year - 2000);
333181602Sraj	else if (ct.year >= 2000) {
334181602Sraj		date[5] |= DS133X_CENT_FLAG;
335181602Sraj		date[6] = TOBCD(ct.year - 2000);
336180811Sstas	} else
337181602Sraj		date[6] = TOBCD(ct.year - 1900);
338180811Sstas
339181602Sraj	return (ds133x_write(dev, DS133X_DATE_REG, date, 7));
340180811Sstas}
341180811Sstas
342181602Srajstatic device_method_t ds133x_methods[] = {
343182870Sraj	DEVMETHOD(device_identify,	ds133x_identify),
344181602Sraj	DEVMETHOD(device_probe,		ds133x_probe),
345181602Sraj	DEVMETHOD(device_attach,	ds133x_attach),
346180811Sstas
347181602Sraj	DEVMETHOD(clock_gettime,	ds133x_gettime),
348181602Sraj	DEVMETHOD(clock_settime,	ds133x_settime),
349180811Sstas
350246128Ssbz	DEVMETHOD_END
351180811Sstas};
352180811Sstas
353181602Srajstatic driver_t ds133x_driver = {
354182870Sraj	DS133X_DEVNAME,
355181602Sraj	ds133x_methods,
356181602Sraj	sizeof(struct ds133x_softc),
357180811Sstas};
358180811Sstas
359181602Srajstatic devclass_t ds133x_devclass;
360181602Sraj
361181602SrajDRIVER_MODULE(ds133x, iicbus, ds133x_driver, ds133x_devclass, 0, 0);
362181602SrajMODULE_VERSION(ds133x, 1);
363181602SrajMODULE_DEPEND(ds133x, iicbus, 1, 1, 1);
364