ds133x.c revision 180811
1/*- 2 * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>, 3 * Rafal Jaworowski <raj@FreeBSD.org>. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/dev/iicbus/ds1339.c 180811 2008-07-25 19:34:44Z stas $"); 29/* 30 * Dallas Semiconductor DS1339 RTC sitting on the I2C bus. 31 */ 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/kernel.h> 35#include <sys/module.h> 36#include <sys/clock.h> 37#include <sys/time.h> 38#include <sys/bus.h> 39#include <sys/resource.h> 40#include <sys/rman.h> 41 42#include <machine/bus.h> 43#include <machine/cpu.h> 44#include <machine/cpufunc.h> 45#include <machine/frame.h> 46#include <machine/resource.h> 47#include <machine/intr.h> 48 49#include <dev/iicbus/iicbus.h> 50#include <dev/iicbus/iiconf.h> 51 52#include "iicbus_if.h" 53#include "clock_if.h" 54 55#define DS1339_ADDR 0xd0 /* slave address */ 56#define DS1339_DATE_REG 0x0 57#define DS1339_CTRL_REG 0x0e 58#define DS1339_OSCD_FLAG 0x80 59#define DS1339_OSF_FLAG 0x80 60 61#define DS1339_24H_FLAG 0x40 /* 24 hours mode. */ 62#define DS1339_PM_FLAG 0x20 /* AM/PM bit. */ 63#define DS1339_CENT_FLAG 0x80 /* Century selector. */ 64#define DS1339_CENT_SHIFT 7 65 66#define HALFSEC 500000000 /* 1/2 of second. */ 67 68struct ds1339_softc { 69 device_t sc_dev; 70}; 71 72static int 73ds1339_probe(device_t dev) 74{ 75 struct ds1339_softc *sc; 76 int addr; 77 78 sc = device_get_softc(dev); 79 iicbus_get_addr(dev, &addr); 80 if (addr != DS1339_ADDR) { 81 if (bootverbose) 82 device_printf(dev, "fixed I2C slave address should " 83 "be 0x%.2x instead of 0x%.2x.\n", DS1339_ADDR, 84 addr); 85 } 86 device_set_desc(dev, "Dallas Semiconductor DS1339 RTC"); 87 return (0); 88} 89 90static int 91ds1339_attach(device_t dev) 92{ 93 struct ds1339_softc *sc = device_get_softc(dev); 94 int error; 95 96 sc->sc_dev = dev; 97 98 uint8_t addr[1] = { DS1339_CTRL_REG }; 99 uint8_t rdata[2]; 100 uint8_t wrdata[3]; 101 struct iic_msg rmsgs[2] = { 102 { DS1339_ADDR, IIC_M_WR, 1, addr }, 103 { DS1339_ADDR, IIC_M_RD, 2, rdata }, 104 }; 105 struct iic_msg wrmsgs[1] = { 106 { DS1339_ADDR, IIC_M_WR, 3, wrdata }, 107 }; 108 109 /* Read control and status registers. */ 110 error = iicbus_transfer(dev, rmsgs, 2); 111 if (error != 0) { 112 device_printf(dev, "could not read control registers.\n"); 113 return (ENXIO); 114 } 115 116 if ((rdata[0] & DS1339_OSCD_FLAG) != 0) 117 rdata[0] &= ~DS1339_OSCD_FLAG; /* Enable oscillator 118 * if disabled. 119 */ 120 if ((rdata[1] & DS1339_OSF_FLAG) != 0) 121 rdata[1] &= ~DS1339_OSF_FLAG; /* Clear oscillator stop flag */ 122 123 /* Write modified registers back. */ 124 wrdata[0] = DS1339_CTRL_REG; 125 wrdata[1] = rdata[0]; 126 wrdata[2] = rdata[1]; 127 128 goto out; 129 130 error = iicbus_transfer(dev, wrmsgs, 1); 131 if (error != 0) { 132 device_printf(dev, "could not write control registers.\n"); 133 return (ENXIO); 134 } 135 136out: 137 138 clock_register(dev, 1000000); 139 return (0); 140} 141 142static uint8_t 143ds1339_get_hours(uint8_t val) 144{ 145 uint8_t ret; 146 147 if (!(val & DS1339_24H_FLAG)) 148 ret = FROMBCD(val & 0x3f); 149 else if (!(val & DS1339_PM_FLAG)) 150 ret = FROMBCD(val & 0x1f); 151 else 152 ret = FROMBCD(val & 0x1f) + 12; 153 154 return (ret); 155} 156 157static int 158ds1339_gettime(device_t dev, struct timespec *ts) 159{ 160 uint8_t addr[1] = { DS1339_DATE_REG }; 161 uint8_t date[7]; 162 struct iic_msg msgs[2] = { 163 { DS1339_ADDR, IIC_M_WR, 1, addr }, 164 { DS1339_ADDR, IIC_M_RD, 7, date }, 165 }; 166 struct clocktime ct; 167 int error; 168 169 error = iicbus_transfer(dev, msgs, 2); 170 if (error == 0) { 171 ct.nsec = 0; 172 ct.sec = FROMBCD(date[0] & 0x7f); 173 ct.min = FROMBCD(date[1] & 0x7f); 174 ct.hour = ds1339_get_hours(date[2]); 175 ct.dow = FROMBCD(date[3] & 0x07) - 1; 176 ct.day = FROMBCD(date[4] & 0x3f); 177 ct.mon = FROMBCD(date[5] & 0x1f); 178 ct.year = 1900 + FROMBCD(date[6]) + 179 ((date[5] & DS1339_CENT_FLAG) >> DS1339_CENT_SHIFT) * 100; 180 181 error = clock_ct_to_ts(&ct, ts); 182 } 183 184 return error; 185} 186 187static int 188ds1339_settime(device_t dev, struct timespec *ts) 189{ 190 uint8_t data[8] = { DS1339_DATE_REG }; /* Register address. */ 191 struct iic_msg msgs[1] = { 192 { DS1339_ADDR, IIC_M_WR, 8, data }, 193 }; 194 struct clocktime ct; 195 196 clock_ts_to_ct(ts, &ct); 197 198 data[1] = TOBCD(ct.nsec >= HALFSEC ? ct.sec + 1 : ct.sec) & 0x7f; 199 data[2] = TOBCD(ct.min) & 0x7f; 200 data[3] = TOBCD(ct.hour) & 0x3f; /* We use 24-hours mode. */ 201 data[4] = TOBCD(ct.dow + 1) & 0x07; 202 data[5] = TOBCD(ct.day) & 0x3f; 203 data[6] = TOBCD(ct.mon) & 0x1f; 204 if (ct.year >= 2000) { 205 data[6] |= DS1339_CENT_FLAG; 206 data[7] = TOBCD(ct.year - 2000); 207 } else 208 data[7] = TOBCD(ct.year - 1900); 209 210 return iicbus_transfer(dev, msgs, 1); 211} 212 213static device_method_t ds1339_methods[] = { 214 DEVMETHOD(device_probe, ds1339_probe), 215 DEVMETHOD(device_attach, ds1339_attach), 216 217 DEVMETHOD(clock_gettime, ds1339_gettime), 218 DEVMETHOD(clock_settime, ds1339_settime), 219 220 {0, 0}, 221}; 222 223static driver_t ds1339_driver = { 224 "ds1339", 225 ds1339_methods, 226 sizeof(struct ds1339_softc), 227}; 228static devclass_t ds1339_devclass; 229 230DRIVER_MODULE(ds1339, iicbus, ds1339_driver, ds1339_devclass, 0, 0); 231MODULE_VERSION(ds1339, 1); 232MODULE_DEPEND(ds1339, iicbus, 1, 1, 1); 233