ds133x.c revision 181602
167754Smsmith/*-
267754Smsmith * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>,
367754Smsmith *                    Rafal Jaworowski <raj@FreeBSD.org>,
491116Smsmith *                    Piotr Ziecik <kosmo@semihalf.com>.
567754Smsmith * All rights reserved.
667754Smsmith *
767754Smsmith * Redistribution and use in source and binary forms, with or without
867754Smsmith * modification, are permitted provided that the following conditions
967754Smsmith * are met:
1067754Smsmith * 1. Redistributions of source code must retain the above copyright
1167754Smsmith *    notice, this list of conditions and the following disclaimer.
1291116Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1370243Smsmith *    notice, this list of conditions and the following disclaimer in the
1467754Smsmith *    documentation and/or other materials provided with the distribution.
1567754Smsmith *
1667754Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1767754Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1867754Smsmith * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1967754Smsmith * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2067754Smsmith * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2167754Smsmith * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2267754Smsmith * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2367754Smsmith * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2467754Smsmith * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2567754Smsmith * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2667754Smsmith */
2767754Smsmith
2867754Smsmith#include <sys/cdefs.h>
2967754Smsmith__FBSDID("$FreeBSD: head/sys/dev/iicbus/ds133x.c 181602 2008-08-11 19:33:58Z raj $");
3067754Smsmith/*
3167754Smsmith * Dallas Semiconductor DS133X RTC sitting on the I2C bus.
3267754Smsmith */
3367754Smsmith#include <sys/param.h>
3467754Smsmith#include <sys/systm.h>
3567754Smsmith#include <sys/kernel.h>
3667754Smsmith#include <sys/module.h>
3767754Smsmith#include <sys/clock.h>
3867754Smsmith#include <sys/time.h>
3967754Smsmith#include <sys/bus.h>
4067754Smsmith#include <sys/resource.h>
4167754Smsmith#include <sys/rman.h>
4267754Smsmith
4367754Smsmith#include <machine/bus.h>
4467754Smsmith#include <machine/cpu.h>
4567754Smsmith#include <machine/cpufunc.h>
4667754Smsmith#include <machine/frame.h>
4767754Smsmith#include <machine/resource.h>
4867754Smsmith#include <machine/intr.h>
4967754Smsmith
5067754Smsmith#include <dev/iicbus/iicbus.h>
5167754Smsmith#include <dev/iicbus/iiconf.h>
5267754Smsmith
5367754Smsmith#include "iicbus_if.h"
5467754Smsmith#include "clock_if.h"
5567754Smsmith
5667754Smsmith#define	DS133X_ADDR		0xd0	/* slave address */
5767754Smsmith#define	DS133X_DATE_REG		0x0
5867754Smsmith#define	DS133X_CTRL_REG		0x0e
5967754Smsmith#define	DS133X_OSCD_FLAG	0x80
6067754Smsmith#define	DS133X_OSF_FLAG		0x80
6167754Smsmith
6267754Smsmith#define	DS133X_24H_FLAG		0x40	/* 24 hours mode. */
6367754Smsmith#define	DS133X_PM_FLAG		0x20	/* AM/PM bit. */
6467754Smsmith#define	DS133X_CENT_FLAG	0x80	/* Century selector. */
6567754Smsmith#define	DS133X_CENT_SHIFT	7
6667754Smsmith
6767754Smsmith#define	DS1338_REG_CLOCK_HALT	0x00
6867754Smsmith#define	DS1338_REG_CONTROL	0x07
6967754Smsmith#define	DS1338_CLOCK_HALT	(1 << 7)
7067754Smsmith#define	DS1338_OSC_STOP		(1 << 5)
7167754Smsmith
7267754Smsmith#define	DS1339_REG_CONTROL	0x0E
7367754Smsmith#define	DS1339_REG_STATUS	0x0F
7467754Smsmith#define	DS1339_OSC_STOP		(1 << 7)
7567754Smsmith#define	DS1339_ENABLE_OSC	(1 << 7)
7667754Smsmith#define	DS1339_BBSQI		(1 << 5)
7767754Smsmith
7867754Smsmith#define	HALFSEC			500000000	/* 1/2 of second. */
7967754Smsmith
8067754Smsmith#define MAX_IIC_DATA_SIZE	7
8167754Smsmith
8267754Smsmithenum {
8367754Smsmith	DS1337,
8467754Smsmith	DS1338,
8567754Smsmith	DS1339,
8667754Smsmith};
8767754Smsmith
8867754Smsmithstruct ds133x_softc {
8967754Smsmith	int		sc_type;
9067754Smsmith	device_t	sc_dev;
9167754Smsmith};
9267754Smsmith
9367754Smsmithstatic int
9467754Smsmithds133x_read(device_t dev, uint8_t address, uint8_t *data, uint8_t size)
9567754Smsmith{
9667754Smsmith	struct iic_msg msg[] = {
9767754Smsmith	    { DS133X_ADDR, IIC_M_WR, 1,	&address },
9867754Smsmith	    { DS133X_ADDR, IIC_M_RD, size, data },
9967754Smsmith	};
10067754Smsmith
10167754Smsmith	return (iicbus_transfer(dev, msg, 2));
10267754Smsmith}
10367754Smsmith
10467754Smsmithstatic int
10567754Smsmithds133x_write(device_t dev, uint8_t address, uint8_t *data, uint8_t size)
10667754Smsmith{
10767754Smsmith	uint8_t buffer[MAX_IIC_DATA_SIZE + 1];
10867754Smsmith	struct iic_msg msg[] = {
10967754Smsmith		{ DS133X_ADDR, IIC_M_WR, size + 1, buffer },
11067754Smsmith	};
11167754Smsmith
11267754Smsmith	if (size > MAX_IIC_DATA_SIZE)
11367754Smsmith		return (ENOMEM);
11467754Smsmith
11567754Smsmith	buffer[0] = address;
11667754Smsmith	memcpy(buffer + 1, data, size);
11767754Smsmith
11867754Smsmith	return (iicbus_transfer(dev, msg, 1));
11967754Smsmith}
12067754Smsmith
12167754Smsmithstatic int
12267754Smsmithds133x_detect(device_t dev, int *sc_type)
12367754Smsmith{
12467754Smsmith	int error;
12577424Smsmith	uint8_t reg, orig;
12691116Smsmith
12767754Smsmith	/*
12867754Smsmith	 * Check for DS1338. At address 0x0F this chip has RAM, however
12967754Smsmith	 * DS1337 and DS1339 have status register. Bits 6-2 in status
13067754Smsmith	 * register will be always read as 0.
13167754Smsmith	 */
13267754Smsmith
13367754Smsmith	if ((error = ds133x_read(dev, DS1339_REG_STATUS, &reg, 1)))
13477424Smsmith		return (error);
13567754Smsmith
13677424Smsmith	orig = reg;
13767754Smsmith	reg |= 0x7C;
13867754Smsmith
13967754Smsmith	if ((error = ds133x_write(dev, DS1339_REG_STATUS, &reg, 1)))
14067754Smsmith		return (error);
14167754Smsmith
14267754Smsmith	if ((error = ds133x_read(dev, DS1339_REG_STATUS, &reg, 1)))
14367754Smsmith		return (error);
14467754Smsmith
14567754Smsmith	if ((reg & 0x7C) != 0) {
14667754Smsmith		/* This is DS1338 */
14767754Smsmith
14867754Smsmith		if ((error = ds133x_write(dev, DS1339_REG_STATUS, &orig, 1)))
14967754Smsmith			return (error);
15067754Smsmith
15167754Smsmith		*sc_type = DS1338;
15267754Smsmith
15367754Smsmith		return (0);
15467754Smsmith	}
15567754Smsmith
15667754Smsmith	/*
15767754Smsmith	 * Now Check for DS1337. Bit 5 in control register of this chip will be
15867754Smsmith	 * allways read as 0. In DS1339 changing of this bit is safe until
15991116Smsmith	 * chip is powered up.
16067754Smsmith	 */
16167754Smsmith
16267754Smsmith	if ((error = ds133x_read(dev, DS1339_REG_CONTROL, &reg, 1)))
16367754Smsmith		return (error);
16467754Smsmith
16591116Smsmith	orig = reg;
16667754Smsmith	reg |= DS1339_BBSQI;
16771867Smsmith
16871867Smsmith	if ((error = ds133x_write(dev, DS1339_REG_CONTROL, &reg, 1)))
16982367Smsmith		return (error);
17067754Smsmith
17171867Smsmith	if ((error = ds133x_read(dev, DS1339_REG_CONTROL, &reg, 1)))
17277424Smsmith		return (error);
17391116Smsmith
17471867Smsmith	if ((reg & DS1339_BBSQI) != 0) {
17571867Smsmith		/* This is DS1339 */
17691116Smsmith
17791116Smsmith		if ((error = ds133x_write(dev, DS1339_REG_CONTROL, &orig, 1)))
17871867Smsmith			return (error);
17980062Smsmith
18071867Smsmith		*sc_type = DS1339;
18167754Smsmith		return (0);
18271867Smsmith	}
18367754Smsmith
18467754Smsmith	/* This is DS1337 */
18567754Smsmith	*sc_type = DS1337;
18667754Smsmith
18767754Smsmith	return (0);
18867754Smsmith}
18967754Smsmith
19067754Smsmithstatic int
19167754Smsmithds133x_init(device_t dev, uint8_t cs_reg, uint8_t cs_bit, uint8_t osf_reg,
19267754Smsmith    uint8_t osf_bit)
19367754Smsmith{
19467754Smsmith	int error;
19567754Smsmith	uint8_t reg;
19677424Smsmith
19777424Smsmith	if ((error = ds133x_read(dev, cs_reg, &reg, 1)))
19877424Smsmith		return (error);
19977424Smsmith
20077424Smsmith	if (reg & cs_bit) {	/* If clock is stopped - start it */
20167754Smsmith		reg &= ~cs_bit;
20277424Smsmith		if ((error = ds133x_write(dev, cs_reg, &reg, 1)))
20367754Smsmith			return (error);
20467754Smsmith	}
20567754Smsmith
20667754Smsmith	if ((error = ds133x_read(dev, osf_reg, &reg, 1)))
20767754Smsmith		return (error);
20867754Smsmith
20977424Smsmith	if (reg & osf_bit) {	/* Clear oscillator stop flag */
21077424Smsmith		device_printf(dev, "RTC oscillator was stopped. Check system"
21177424Smsmith		    " time and RTC battery.\n");
21277424Smsmith		reg &= ~osf_bit;
21367754Smsmith		if ((error = ds133x_write(dev, osf_reg, &reg, 1)))
21467754Smsmith			return (error);
21567754Smsmith	}
21667754Smsmith
21767754Smsmith	return (0);
21867754Smsmith}
21967754Smsmith
22067754Smsmithstatic int
22167754Smsmithds133x_probe(device_t dev)
22283174Smsmith{
22382367Smsmith	struct ds133x_softc *sc;
22487031Smsmith	int error;
22567754Smsmith
22667754Smsmith	sc = device_get_softc(dev);
22767754Smsmith
22867754Smsmith	if ((error = ds133x_detect(dev, &sc->sc_type)))
22967754Smsmith		return (error);
23067754Smsmith
23167754Smsmith	switch (sc->sc_type) {
23267754Smsmith	case DS1337:
23367754Smsmith		device_set_desc(dev, "Dallas Semiconductor DS1337 RTC");
23467754Smsmith		break;
23567754Smsmith	case DS1338:
23667754Smsmith		device_set_desc(dev, "Dallas Semiconductor DS1338 RTC");
23767754Smsmith		break;
23867754Smsmith	case DS1339:
23967754Smsmith		device_set_desc(dev, "Dallas Semiconductor DS1339 RTC");
24067754Smsmith		break;
24167754Smsmith	default:
24267754Smsmith		break;
24367754Smsmith	}
24467754Smsmith
24567754Smsmith	return (0);
24667754Smsmith}
24767754Smsmith
24882367Smsmithstatic int
24987031Smsmithds133x_attach(device_t dev)
25067754Smsmith{
25167754Smsmith	struct ds133x_softc *sc = device_get_softc(dev);
25267754Smsmith
25367754Smsmith	sc->sc_dev = dev;
25467754Smsmith
25567754Smsmith	if (sc->sc_type == DS1338)
25667754Smsmith		ds133x_init(dev, DS1338_REG_CLOCK_HALT, DS1338_CLOCK_HALT,
25767754Smsmith		    DS1338_REG_CONTROL, DS1338_OSC_STOP);
25867754Smsmith	else
25967754Smsmith		ds133x_init(dev, DS1339_REG_CONTROL, DS1339_ENABLE_OSC,
26077424Smsmith		    DS1339_REG_STATUS, DS1339_OSC_STOP);
26167754Smsmith
26277424Smsmith	clock_register(dev, 1000000);
26367754Smsmith
26467754Smsmith	return (0);
26567754Smsmith}
26667754Smsmith
26767754Smsmithstatic uint8_t
26867754Smsmithds133x_get_hours(uint8_t val)
26967754Smsmith{
27067754Smsmith	uint8_t ret;
27167754Smsmith
27267754Smsmith	if (!(val & DS133X_24H_FLAG))
27367754Smsmith		ret = FROMBCD(val & 0x3f);
27467754Smsmith	else if (!(val & DS133X_PM_FLAG))
27567754Smsmith		ret = FROMBCD(val & 0x1f);
27667754Smsmith	else
27767754Smsmith		ret = FROMBCD(val & 0x1f) + 12;
27867754Smsmith
27967754Smsmith	return (ret);
28069450Smsmith}
28167754Smsmith
28267754Smsmithstatic int
28367754Smsmithds133x_gettime(device_t dev, struct timespec *ts)
28491116Smsmith{
28567754Smsmith	struct ds133x_softc *sc = device_get_softc(dev);
28667754Smsmith	struct clocktime ct;
28767754Smsmith	uint8_t date[7];
28867754Smsmith	int error;
28967754Smsmith
29067754Smsmith	error = ds133x_read(dev, DS133X_DATE_REG, date, 7);
29191116Smsmith	if (error == 0) {
29267754Smsmith		ct.nsec = 0;
29367754Smsmith		ct.sec = FROMBCD(date[0] & 0x7f);
29491116Smsmith		ct.min = FROMBCD(date[1] & 0x7f);
29567754Smsmith		ct.hour = ds133x_get_hours(date[2]);
29667754Smsmith		ct.dow = FROMBCD(date[3] & 0x07) - 1;
29767754Smsmith		ct.day = FROMBCD(date[4] & 0x3f);
29867754Smsmith		ct.mon = FROMBCD(date[5] & 0x1f);
29967754Smsmith
30077424Smsmith		if (sc->sc_type == DS1338)
30167754Smsmith			ct.year = 2000 + FROMBCD(date[6]);
30267754Smsmith		else
30367754Smsmith			ct.year = 1900 + FROMBCD(date[6]) +
30467754Smsmith			    ((date[5] & DS133X_CENT_FLAG) >> DS133X_CENT_SHIFT) * 100;
30582367Smsmith
30687031Smsmith		error = clock_ct_to_ts(&ct, ts);
30767754Smsmith	}
30867754Smsmith
30967754Smsmith	return (error);
31067754Smsmith}
31183174Smsmith
31287031Smsmithstatic int
31367754Smsmithds133x_settime(device_t dev, struct timespec *ts)
31467754Smsmith{
31567754Smsmith	struct ds133x_softc *sc = device_get_softc(dev);
31667754Smsmith	struct clocktime ct;
31767754Smsmith	uint8_t date[7];
31867754Smsmith
31967754Smsmith	clock_ts_to_ct(ts, &ct);
32087031Smsmith
32167754Smsmith	date[0] = TOBCD(ct.nsec >= HALFSEC ? ct.sec + 1 : ct.sec) & 0x7f;
32267754Smsmith	date[1] = TOBCD(ct.min) & 0x7f;
32367754Smsmith	date[2] = TOBCD(ct.hour) & 0x3f;	/* We use 24-hours mode. */
32467754Smsmith	date[3] = TOBCD(ct.dow + 1) & 0x07;
32567754Smsmith	date[4] = TOBCD(ct.day) & 0x3f;
32667754Smsmith	date[5] = TOBCD(ct.mon) & 0x1f;
32767754Smsmith	if (sc->sc_type == DS1338)
32891116Smsmith		date[6] = TOBCD(ct.year - 2000);
32987031Smsmith	else if (ct.year >= 2000) {
33091116Smsmith		date[5] |= DS133X_CENT_FLAG;
33187031Smsmith		date[6] = TOBCD(ct.year - 2000);
33287031Smsmith	} else
33367754Smsmith		date[6] = TOBCD(ct.year - 1900);
33467754Smsmith
33567754Smsmith	return (ds133x_write(dev, DS133X_DATE_REG, date, 7));
33667754Smsmith}
33767754Smsmith
33867754Smsmithstatic device_method_t ds133x_methods[] = {
33967754Smsmith	DEVMETHOD(device_probe,		ds133x_probe),
34067754Smsmith	DEVMETHOD(device_attach,	ds133x_attach),
34167754Smsmith
34267754Smsmith	DEVMETHOD(clock_gettime,	ds133x_gettime),
34367754Smsmith	DEVMETHOD(clock_settime,	ds133x_settime),
34491116Smsmith
34567754Smsmith	{0, 0},
34667754Smsmith};
34767754Smsmith
34867754Smsmithstatic driver_t ds133x_driver = {
34967754Smsmith	"rtc",
35067754Smsmith	ds133x_methods,
35167754Smsmith	sizeof(struct ds133x_softc),
35267754Smsmith};
35367754Smsmith
35467754Smsmithstatic devclass_t ds133x_devclass;
35567754Smsmith
35667754SmsmithDRIVER_MODULE(ds133x, iicbus, ds133x_driver, ds133x_devclass, 0, 0);
35767754SmsmithMODULE_VERSION(ds133x, 1);
35867754SmsmithMODULE_DEPEND(ds133x, iicbus, 1, 1, 1);
35977424Smsmith