1234248Smarius/*- 2234248Smarius * Copyright (c) 2012 Marius Strobl <marius@FreeBSD.org> 3234248Smarius * All rights reserved. 4234248Smarius * 5234248Smarius * Redistribution and use in source and binary forms, with or without 6234248Smarius * modification, are permitted provided that the following conditions 7234248Smarius * are met: 8234248Smarius * 1. Redistributions of source code must retain the above copyright 9234248Smarius * notice, this list of conditions and the following disclaimer. 10234248Smarius * 2. Redistributions in binary form must reproduce the above copyright 11234248Smarius * notice, this list of conditions and the following disclaimer in the 12234248Smarius * documentation and/or other materials provided with the distribution. 13234248Smarius * 14234248Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15234248Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16234248Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17234248Smarius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18234248Smarius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19234248Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20234248Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21234248Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22234248Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23234248Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24234248Smarius * SUCH DAMAGE. 25234248Smarius */ 26234248Smarius 27234248Smarius#include <sys/cdefs.h> 28234248Smarius__FBSDID("$FreeBSD$"); 29234248Smarius 30234248Smarius/* 31234248Smarius * Driver for NXP PCF8563 real-time clock/calendar 32234248Smarius */ 33234248Smarius 34234248Smarius#include <sys/param.h> 35234248Smarius#include <sys/systm.h> 36234248Smarius#include <sys/bus.h> 37234248Smarius#include <sys/clock.h> 38234248Smarius#include <sys/kernel.h> 39234248Smarius#include <sys/module.h> 40234248Smarius 41234248Smarius#include <dev/iicbus/iicbus.h> 42234248Smarius#include <dev/iicbus/iiconf.h> 43234248Smarius#include <dev/iicbus/pcf8563reg.h> 44234248Smarius 45234248Smarius#include "clock_if.h" 46234248Smarius#include "iicbus_if.h" 47234248Smarius 48234248Smarius#define PCF8563_NCLOCKREGS (PCF8563_R_YEAR - PCF8563_R_CS1 + 1) 49234248Smarius 50234248Smariusstruct pcf8563_softc { 51234248Smarius uint32_t sc_flags; 52234248Smarius#define PCF8563_CPOL (1 << 0) /* PCF8563_R_MONTH_C means 19xx */ 53234248Smarius uint16_t sc_addr; /* PCF8563 slave address */ 54234248Smarius uint16_t sc_year0; /* TOD clock year 0 */ 55234248Smarius}; 56234248Smarius 57234248Smariusstatic device_attach_t pcf8563_attach; 58234248Smariusstatic device_probe_t pcf8563_probe; 59234248Smariusstatic clock_gettime_t pcf8563_gettime; 60234248Smariusstatic clock_settime_t pcf8563_settime; 61234248Smarius 62234248Smariusstatic int 63234248Smariuspcf8563_probe(device_t dev) 64234248Smarius{ 65234248Smarius 66234248Smarius device_set_desc(dev, "NXP PCF8563 RTC"); 67234248Smarius return (BUS_PROBE_NOWILDCARD); 68234248Smarius} 69234248Smarius 70234248Smariusstatic int 71234248Smariuspcf8563_attach(device_t dev) 72234248Smarius{ 73234248Smarius uint8_t reg = PCF8563_R_SECOND, val; 74234248Smarius struct iic_msg msgs[] = { 75234248Smarius { 0, IIC_M_WR, sizeof(reg), ® }, 76234248Smarius { 0, IIC_M_RD, sizeof(val), &val } 77234248Smarius }; 78234248Smarius struct pcf8563_softc *sc; 79234248Smarius int error; 80234248Smarius 81234248Smarius sc = device_get_softc(dev); 82234248Smarius sc->sc_addr = iicbus_get_addr(dev); 83234248Smarius if (sc->sc_addr == 0) 84234248Smarius sc->sc_addr = PCF8563_ADDR; 85234248Smarius 86241679Smarius /* 87241679Smarius * NB: PCF8563_R_SECOND_VL doesn't automatically clear when VDD 88241679Smarius * rises above Vlow again and needs to be cleared manually. 89241679Smarius * However, apparently this needs all of the time registers to be 90241679Smarius * set, i.e. pcf8563_settime(), and not just PCF8563_R_SECOND in 91241679Smarius * order for PCF8563_R_SECOND_VL to stick. Thus, we just issue a 92241679Smarius * warning here rather than failing with ENXIO in case it is set. 93241679Smarius * Note that pcf8563_settime() will also clear PCF8563_R_SECOND_VL 94241679Smarius * as a side-effect. 95241679Smarius */ 96234248Smarius msgs[0].slave = msgs[1].slave = sc->sc_addr; 97234248Smarius error = iicbus_transfer(device_get_parent(dev), msgs, sizeof(msgs) / 98234248Smarius sizeof(*msgs)); 99234248Smarius if (error != 0) { 100234248Smarius device_printf(dev, "%s: cannot read RTC\n", __func__); 101234248Smarius return (error); 102234248Smarius } 103241679Smarius if ((val & PCF8563_R_SECOND_VL) != 0) 104234248Smarius device_printf(dev, "%s: battery low\n", __func__); 105234248Smarius 106234248Smarius sc->sc_year0 = 1900; 107234248Smarius clock_register(dev, 1000000); /* 1 second resolution */ 108234248Smarius return (0); 109234248Smarius} 110234248Smarius 111234248Smariusstatic int 112234248Smariuspcf8563_gettime(device_t dev, struct timespec *ts) 113234248Smarius{ 114234248Smarius struct clocktime ct; 115234248Smarius uint8_t reg = PCF8563_R_SECOND, val[PCF8563_NCLOCKREGS]; 116234248Smarius struct iic_msg msgs[] = { 117234248Smarius { 0, IIC_M_WR, sizeof(reg), ® }, 118234248Smarius { 0, IIC_M_RD, PCF8563_NCLOCKREGS, &val[PCF8563_R_SECOND] } 119234248Smarius }; 120234248Smarius struct pcf8563_softc *sc; 121234248Smarius int error; 122234248Smarius 123234248Smarius sc = device_get_softc(dev); 124234248Smarius msgs[0].slave = msgs[1].slave = sc->sc_addr; 125234248Smarius error = iicbus_transfer(device_get_parent(dev), msgs, sizeof(msgs) / 126234248Smarius sizeof(*msgs)); 127234248Smarius if (error != 0) { 128234248Smarius device_printf(dev, "%s: cannot read RTC\n", __func__); 129234248Smarius return (error); 130234248Smarius } 131234248Smarius 132234248Smarius ct.nsec = 0; 133234248Smarius ct.sec = FROMBCD(val[PCF8563_R_SECOND] & PCF8563_M_SECOND); 134234248Smarius ct.min = FROMBCD(val[PCF8563_R_MINUTE] & PCF8563_M_MINUTE); 135234248Smarius ct.hour = FROMBCD(val[PCF8563_R_HOUR] & PCF8563_M_HOUR); 136234248Smarius ct.day = FROMBCD(val[PCF8563_R_DAY] & PCF8563_M_DAY); 137234248Smarius ct.dow = val[PCF8563_R_WEEKDAY] & PCF8563_M_WEEKDAY; 138234248Smarius ct.mon = FROMBCD(val[PCF8563_R_MONTH] & PCF8563_M_MONTH); 139234248Smarius ct.year = FROMBCD(val[PCF8563_R_YEAR] & PCF8563_M_YEAR); 140234248Smarius ct.year += sc->sc_year0; 141234248Smarius if (ct.year < POSIX_BASE_YEAR) 142234248Smarius ct.year += 100; /* assume [1970, 2069] */ 143234248Smarius if ((val[PCF8563_R_MONTH] & PCF8563_R_MONTH_C) != 0) { 144234248Smarius if (ct.year >= 100 + sc->sc_year0) 145234248Smarius sc->sc_flags |= PCF8563_CPOL; 146234248Smarius } else if (ct.year < 100 + sc->sc_year0) 147234248Smarius sc->sc_flags |= PCF8563_CPOL; 148234248Smarius return (clock_ct_to_ts(&ct, ts)); 149234248Smarius} 150234248Smarius 151234248Smariusstatic int 152234248Smariuspcf8563_settime(device_t dev, struct timespec *ts) 153234248Smarius{ 154234248Smarius struct clocktime ct; 155234248Smarius uint8_t val[PCF8563_NCLOCKREGS]; 156234248Smarius struct iic_msg msgs[] = { 157234248Smarius { 0, IIC_M_WR, PCF8563_NCLOCKREGS - 1, &val[PCF8563_R_CS2] } 158234248Smarius }; 159234248Smarius struct pcf8563_softc *sc; 160234248Smarius int error; 161234248Smarius 162234248Smarius sc = device_get_softc(dev); 163234248Smarius val[PCF8563_R_CS2] = PCF8563_R_SECOND; /* abuse */ 164234248Smarius /* Accuracy is only one second. */ 165234248Smarius if (ts->tv_nsec >= 500000000) 166234248Smarius ts->tv_sec++; 167234248Smarius ts->tv_nsec = 0; 168234248Smarius clock_ts_to_ct(ts, &ct); 169234248Smarius val[PCF8563_R_SECOND] = TOBCD(ct.sec); 170234248Smarius val[PCF8563_R_MINUTE] = TOBCD(ct.min); 171234248Smarius val[PCF8563_R_HOUR] = TOBCD(ct.hour); 172234248Smarius val[PCF8563_R_DAY] = TOBCD(ct.day); 173234248Smarius val[PCF8563_R_WEEKDAY] = ct.dow; 174234248Smarius val[PCF8563_R_MONTH] = TOBCD(ct.mon); 175234248Smarius val[PCF8563_R_YEAR] = TOBCD(ct.year % 100); 176234248Smarius if ((sc->sc_flags & PCF8563_CPOL) != 0) { 177234248Smarius if (ct.year >= 100 + sc->sc_year0) 178234248Smarius val[PCF8563_R_MONTH] |= PCF8563_R_MONTH_C; 179234248Smarius } else if (ct.year < 100 + sc->sc_year0) 180234248Smarius val[PCF8563_R_MONTH] |= PCF8563_R_MONTH_C; 181234248Smarius 182234248Smarius msgs[0].slave = sc->sc_addr; 183234248Smarius error = iicbus_transfer(device_get_parent(dev), msgs, sizeof(msgs) / 184234248Smarius sizeof(*msgs)); 185234248Smarius if (error != 0) 186234248Smarius device_printf(dev, "%s: cannot write RTC\n", __func__); 187234248Smarius return (error); 188234248Smarius} 189234248Smarius 190234248Smariusstatic device_method_t pcf8563_methods[] = { 191234248Smarius DEVMETHOD(device_probe, pcf8563_probe), 192234248Smarius DEVMETHOD(device_attach, pcf8563_attach), 193234248Smarius 194234248Smarius DEVMETHOD(clock_gettime, pcf8563_gettime), 195234248Smarius DEVMETHOD(clock_settime, pcf8563_settime), 196234248Smarius 197234248Smarius DEVMETHOD_END 198234248Smarius}; 199234248Smarius 200234248Smariusstatic driver_t pcf8563_driver = { 201234248Smarius "pcf8563_rtc", 202234248Smarius pcf8563_methods, 203234248Smarius sizeof(struct pcf8563_softc), 204234248Smarius}; 205234248Smarius 206234248Smariusstatic devclass_t pcf8563_devclass; 207234248Smarius 208234248SmariusDRIVER_MODULE(pcf8563, iicbus, pcf8563_driver, pcf8563_devclass, NULL, NULL); 209234248SmariusMODULE_VERSION(pcf8563, 1); 210234248SmariusMODULE_DEPEND(pcf8563, iicbus, 1, 1, 1); 211