ds1553_core.c revision 194632
1254885Sdumbbell/*- 2254885Sdumbbell * Copyright (C) 2006-2008 Semihalf, Grzegorz Bernacki 3254885Sdumbbell * All rights reserved. 4254885Sdumbbell * 5254885Sdumbbell * Redistribution and use in source and binary forms, with or without 6254885Sdumbbell * modification, are permitted provided that the following conditions 7254885Sdumbbell * are met: 8254885Sdumbbell * 1. Redistributions of source code must retain the above copyright 9254885Sdumbbell * notice, this list of conditions and the following disclaimer. 10254885Sdumbbell * 2. Redistributions in binary form must reproduce the above copyright 11254885Sdumbbell * notice, this list of conditions and the following disclaimer in the 12254885Sdumbbell * documentation and/or other materials provided with the distribution. 13254885Sdumbbell * 14254885Sdumbbell * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15254885Sdumbbell * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16254885Sdumbbell * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17254885Sdumbbell * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18254885Sdumbbell * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19254885Sdumbbell * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20254885Sdumbbell * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21254885Sdumbbell * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22254885Sdumbbell * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23254885Sdumbbell * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24254885Sdumbbell * SUCH DAMAGE. 25254885Sdumbbell * 26254885Sdumbbell */ 27254885Sdumbbell 28254885Sdumbbell#include <sys/cdefs.h> 29254885Sdumbbell__FBSDID("$FreeBSD: head/sys/powerpc/mpc85xx/ds1553_core.c 194632 2009-06-22 15:48:47Z raj $"); 30254885Sdumbbell 31254885Sdumbbell#include <sys/param.h> 32254885Sdumbbell#include <sys/systm.h> 33254885Sdumbbell#include <sys/bus.h> 34254885Sdumbbell#include <sys/clock.h> 35254885Sdumbbell#include <sys/lock.h> 36254885Sdumbbell#include <sys/mutex.h> 37254885Sdumbbell 38254885Sdumbbell#include <machine/bus.h> 39254885Sdumbbell 40254885Sdumbbell#include <powerpc/mpc85xx/ds1553_reg.h> 41254885Sdumbbell 42254885Sdumbbellstatic uint8_t ds1553_direct_read(device_t, bus_size_t); 43254885Sdumbbellstatic void ds1553_direct_write(device_t, bus_size_t, uint8_t); 44254885Sdumbbell 45254885Sdumbbellint 46254885Sdumbbellds1553_attach(device_t dev) 47254885Sdumbbell{ 48254885Sdumbbell struct ds1553_softc *sc; 49254885Sdumbbell uint8_t sec, flags; 50254885Sdumbbell 51254885Sdumbbell sc = device_get_softc(dev); 52254885Sdumbbell 53254885Sdumbbell if (mtx_initialized(&sc->sc_mtx) == 0) { 54254885Sdumbbell device_printf(dev, "%s: mutex not initialized\n", __func__); 55254885Sdumbbell return (ENXIO); 56 } 57 58 if (sc->sc_read == NULL) 59 sc->sc_read = ds1553_direct_read; 60 if (sc->sc_write == NULL) 61 sc->sc_write = ds1553_direct_write; 62 63 sc->year_offset = POSIX_BASE_YEAR; 64 65 mtx_lock_spin(&sc->sc_mtx); 66 67 /* Turn RTC on if it was not on */ 68 sec = (*sc->sc_read)(dev, DS1553_OFF_SECONDS); 69 if (sec & DS1553_BIT_OSC) { 70 sec &= ~(DS1553_BIT_OSC); 71 (*sc->sc_write)(dev, DS1553_OFF_SECONDS, sec); 72 } 73 74 /* Low-battery check */ 75 flags = (*sc->sc_read)(dev, DS1553_OFF_FLAGS); 76 if (flags & DS1553_BIT_BLF) 77 device_printf(dev, "voltage-low detected.\n"); 78 79 mtx_unlock_spin(&sc->sc_mtx); 80 81 return (0); 82} 83 84/* 85 * Get time of day and convert it to a struct timespec. 86 * Return 0 on success, an error number otherwise. 87 */ 88int 89ds1553_gettime(device_t dev, struct timespec *ts) 90{ 91 struct clocktime ct; 92 struct ds1553_softc *sc; 93 uint8_t control; 94 95 sc = device_get_softc(dev); 96 97 mtx_lock_spin(&sc->sc_mtx); 98 99 control = (*sc->sc_read)(dev, DS1553_OFF_CONTROL) | DS1553_BIT_READ; 100 (*sc->sc_write)(dev, DS1553_OFF_CONTROL, control); 101 102 ct.nsec = 0; 103 ct.sec = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_SECONDS) & 104 DS1553_MASK_SECONDS); 105 ct.min = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_MINUTES) & 106 DS1553_MASK_MINUTES); 107 ct.hour = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_HOURS) & 108 DS1553_MASK_HOUR); 109 ct.dow = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_DAYOFWEEK) & 110 DS1553_MASK_DAYOFWEEK) - 1; 111 ct.day = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_DATE) & 112 DS1553_MASK_DATE); 113 ct.mon = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_MONTH) & 114 DS1553_MASK_MONTH); 115 ct.year = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_YEAR)); 116 117 control &= ~DS1553_BIT_READ; 118 (*sc->sc_write)(dev, DS1553_OFF_CONTROL, control); 119 120 ct.year += sc->year_offset; 121 122 mtx_unlock_spin(&sc->sc_mtx); 123 124 return (clock_ct_to_ts(&ct, ts)); 125} 126 127/* 128 * Set the time of day clock based on the value of the struct timespec arg. 129 * Return 0 on success, an error number otherwise. 130 */ 131int 132ds1553_settime(device_t dev, struct timespec *ts) 133{ 134 struct clocktime ct; 135 struct ds1553_softc *sc; 136 uint8_t control; 137 138 sc = device_get_softc(dev); 139 bzero(&ct, sizeof(struct clocktime)); 140 141 /* Accuracy is only one second. */ 142 if (ts->tv_nsec >= 500000000) 143 ts->tv_sec++; 144 ts->tv_nsec = 0; 145 clock_ts_to_ct(ts, &ct); 146 147 ct.year -= sc->year_offset; 148 149 mtx_lock_spin(&sc->sc_mtx); 150 151 /* Halt updates to external registers */ 152 control = (*sc->sc_read)(dev, DS1553_OFF_CONTROL) | DS1553_BIT_WRITE; 153 (*sc->sc_write)(dev, DS1553_OFF_CONTROL, control); 154 155 (*sc->sc_write)(dev, DS1553_OFF_SECONDS, TOBCD(ct.sec) & 156 DS1553_MASK_SECONDS); 157 (*sc->sc_write)(dev, DS1553_OFF_MINUTES, TOBCD(ct.min) & 158 DS1553_MASK_MINUTES); 159 (*sc->sc_write)(dev, DS1553_OFF_HOURS, TOBCD(ct.hour) & 160 DS1553_MASK_HOUR); 161 (*sc->sc_write)(dev, DS1553_OFF_DAYOFWEEK, TOBCD(ct.dow + 1) & 162 DS1553_MASK_DAYOFWEEK); 163 (*sc->sc_write)(dev, DS1553_OFF_DATE, TOBCD(ct.day) & 164 DS1553_MASK_DATE); 165 (*sc->sc_write)(dev, DS1553_OFF_MONTH, TOBCD(ct.mon) & 166 DS1553_MASK_MONTH); 167 (*sc->sc_write)(dev, DS1553_OFF_YEAR, TOBCD(ct.year)); 168 169 /* Resume updates to external registers */ 170 control &= ~DS1553_BIT_WRITE; 171 (*sc->sc_write)(dev, DS1553_OFF_CONTROL, control); 172 173 mtx_unlock_spin(&sc->sc_mtx); 174 175 return (0); 176} 177 178static uint8_t 179ds1553_direct_read(device_t dev, bus_size_t off) 180{ 181 struct ds1553_softc *sc; 182 183 sc = device_get_softc(dev); 184 return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, off)); 185} 186 187static void 188ds1553_direct_write(device_t dev, bus_size_t off, uint8_t val) 189{ 190 struct ds1553_softc *sc; 191 192 sc = device_get_softc(dev); 193 bus_space_write_1(sc->sc_bst, sc->sc_bsh, off, val); 194} 195