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