1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (C) 2006-2008 Semihalf, Grzegorz Bernacki 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/bus.h> 36 #include <sys/clock.h> 37 #include <sys/lock.h> 38 #include <sys/mutex.h> 39 40 #include <machine/bus.h> 41 42 #include <powerpc/mpc85xx/ds1553_reg.h> 43 44 static uint8_t ds1553_direct_read(device_t, bus_size_t); 45 static void ds1553_direct_write(device_t, bus_size_t, uint8_t); 46 47 int 48 ds1553_attach(device_t dev) 49 { 50 struct ds1553_softc *sc; 51 uint8_t sec, flags; 52 53 sc = device_get_softc(dev); 54 55 if (mtx_initialized(&sc->sc_mtx) == 0) { 56 device_printf(dev, "%s: mutex not initialized\n", __func__); 57 return (ENXIO); 58 } 59 60 if (sc->sc_read == NULL) 61 sc->sc_read = ds1553_direct_read; 62 if (sc->sc_write == NULL) 63 sc->sc_write = ds1553_direct_write; 64 65 sc->year_offset = POSIX_BASE_YEAR; 66 67 mtx_lock_spin(&sc->sc_mtx); 68 69 /* Turn RTC on if it was not on */ 70 sec = (*sc->sc_read)(dev, DS1553_OFF_SECONDS); 71 if (sec & DS1553_BIT_OSC) { 72 sec &= ~(DS1553_BIT_OSC); 73 (*sc->sc_write)(dev, DS1553_OFF_SECONDS, sec); 74 } 75 76 /* Low-battery check */ 77 flags = (*sc->sc_read)(dev, DS1553_OFF_FLAGS); 78 if (flags & DS1553_BIT_BLF) 79 device_printf(dev, "voltage-low detected.\n"); 80 81 mtx_unlock_spin(&sc->sc_mtx); 82 83 return (0); 84 } 85 86 /* 87 * Get time of day and convert it to a struct timespec. 88 * Return 0 on success, an error number otherwise. 89 */ 90 int 91 ds1553_gettime(device_t dev, struct timespec *ts) 92 { 93 struct clocktime ct; 94 struct ds1553_softc *sc; 95 uint8_t control; 96 97 sc = device_get_softc(dev); 98 99 mtx_lock_spin(&sc->sc_mtx); 100 101 control = (*sc->sc_read)(dev, DS1553_OFF_CONTROL) | DS1553_BIT_READ; 102 (*sc->sc_write)(dev, DS1553_OFF_CONTROL, control); 103 104 ct.nsec = 0; 105 ct.sec = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_SECONDS) & 106 DS1553_MASK_SECONDS); 107 ct.min = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_MINUTES) & 108 DS1553_MASK_MINUTES); 109 ct.hour = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_HOURS) & 110 DS1553_MASK_HOUR); 111 ct.dow = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_DAYOFWEEK) & 112 DS1553_MASK_DAYOFWEEK) - 1; 113 ct.day = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_DATE) & 114 DS1553_MASK_DATE); 115 ct.mon = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_MONTH) & 116 DS1553_MASK_MONTH); 117 ct.year = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_YEAR)); 118 119 control &= ~DS1553_BIT_READ; 120 (*sc->sc_write)(dev, DS1553_OFF_CONTROL, control); 121 122 ct.year += sc->year_offset; 123 124 mtx_unlock_spin(&sc->sc_mtx); 125 126 return (clock_ct_to_ts(&ct, ts)); 127 } 128 129 /* 130 * Set the time of day clock based on the value of the struct timespec arg. 131 * Return 0 on success, an error number otherwise. 132 */ 133 int 134 ds1553_settime(device_t dev, struct timespec *ts) 135 { 136 struct clocktime ct; 137 struct ds1553_softc *sc; 138 uint8_t control; 139 140 sc = device_get_softc(dev); 141 bzero(&ct, sizeof(struct clocktime)); 142 143 /* Accuracy is only one second. */ 144 if (ts->tv_nsec >= 500000000) 145 ts->tv_sec++; 146 ts->tv_nsec = 0; 147 clock_ts_to_ct(ts, &ct); 148 149 ct.year -= sc->year_offset; 150 151 mtx_lock_spin(&sc->sc_mtx); 152 153 /* Halt updates to external registers */ 154 control = (*sc->sc_read)(dev, DS1553_OFF_CONTROL) | DS1553_BIT_WRITE; 155 (*sc->sc_write)(dev, DS1553_OFF_CONTROL, control); 156 157 (*sc->sc_write)(dev, DS1553_OFF_SECONDS, TOBCD(ct.sec) & 158 DS1553_MASK_SECONDS); 159 (*sc->sc_write)(dev, DS1553_OFF_MINUTES, TOBCD(ct.min) & 160 DS1553_MASK_MINUTES); 161 (*sc->sc_write)(dev, DS1553_OFF_HOURS, TOBCD(ct.hour) & 162 DS1553_MASK_HOUR); 163 (*sc->sc_write)(dev, DS1553_OFF_DAYOFWEEK, TOBCD(ct.dow + 1) & 164 DS1553_MASK_DAYOFWEEK); 165 (*sc->sc_write)(dev, DS1553_OFF_DATE, TOBCD(ct.day) & 166 DS1553_MASK_DATE); 167 (*sc->sc_write)(dev, DS1553_OFF_MONTH, TOBCD(ct.mon) & 168 DS1553_MASK_MONTH); 169 (*sc->sc_write)(dev, DS1553_OFF_YEAR, TOBCD(ct.year)); 170 171 /* Resume updates to external registers */ 172 control &= ~DS1553_BIT_WRITE; 173 (*sc->sc_write)(dev, DS1553_OFF_CONTROL, control); 174 175 mtx_unlock_spin(&sc->sc_mtx); 176 177 return (0); 178 } 179 180 static uint8_t 181 ds1553_direct_read(device_t dev, bus_size_t off) 182 { 183 struct ds1553_softc *sc; 184 185 sc = device_get_softc(dev); 186 return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, off)); 187 } 188 189 static void 190 ds1553_direct_write(device_t dev, bus_size_t off, uint8_t val) 191 { 192 struct ds1553_softc *sc; 193 194 sc = device_get_softc(dev); 195 bus_space_write_1(sc->sc_bst, sc->sc_bsh, off, val); 196 } 197