mk48txx.c revision 201371
139217Sgibbs/*- 239217Sgibbs * Copyright (c) 2000 The NetBSD Foundation, Inc. 339217Sgibbs * All rights reserved. 439217Sgibbs * 539217Sgibbs * This code is derived from software contributed to The NetBSD Foundation 655945Sgibbs * by Paul Kranenburg. 755945Sgibbs * 855945Sgibbs * Redistribution and use in source and binary forms, with or without 955945Sgibbs * modification, are permitted provided that the following conditions 1055945Sgibbs * are met: 1155945Sgibbs * 1. Redistributions of source code must retain the above copyright 1255945Sgibbs * notice, this list of conditions and the following disclaimer. 1355945Sgibbs * 2. Redistributions in binary form must reproduce the above copyright 1455945Sgibbs * notice, this list of conditions and the following disclaimer in the 1555945Sgibbs * documentation and/or other materials provided with the distribution. 1657679Sgibbs * 1739217Sgibbs * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 1839217Sgibbs * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 1957679Sgibbs * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2057679Sgibbs * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2157679Sgibbs * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2257679Sgibbs * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2357679Sgibbs * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2457679Sgibbs * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2539217Sgibbs * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2639217Sgibbs * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2739217Sgibbs * POSSIBILITY OF SUCH DAMAGE. 2857679Sgibbs * 2957679Sgibbs * $NetBSD: mk48txx.c,v 1.25 2008/04/28 20:23:50 martin Exp $ 3055945Sgibbs */ 3139217Sgibbs 3239217Sgibbs#include <sys/cdefs.h> 3339217Sgibbs__FBSDID("$FreeBSD: head/sys/dev/mk48txx/mk48txx.c 201371 2010-01-01 22:47:53Z marius $"); 3439217Sgibbs 3539217Sgibbs/* 3639217Sgibbs * Mostek MK48T02, MK48T08, MK48T18, MK48T59 time-of-day chip subroutines 3739217Sgibbs */ 3839217Sgibbs 3939217Sgibbs#include <sys/param.h> 4039217Sgibbs#include <sys/systm.h> 4139217Sgibbs#include <sys/bus.h> 4239217Sgibbs#include <sys/clock.h> 4339217Sgibbs#include <sys/eventhandler.h> 4455945Sgibbs#include <sys/lock.h> 4539217Sgibbs#include <sys/mutex.h> 4639217Sgibbs#include <sys/rman.h> 4739217Sgibbs#include <sys/watchdog.h> 4839217Sgibbs 4939217Sgibbs#include <machine/bus.h> 5039217Sgibbs 5139217Sgibbs#include <dev/mk48txx/mk48txxreg.h> 5239217Sgibbs#include <dev/mk48txx/mk48txxvar.h> 5339217Sgibbs 5439217Sgibbs#include "clock_if.h" 5539217Sgibbs 5639217Sgibbsstatic uint8_t mk48txx_def_nvrd(device_t dev, int off); 5739217Sgibbsstatic void mk48txx_def_nvwr(device_t dev, int off, uint8_t v); 5839217Sgibbsstatic void mk48txx_watchdog(void *arg, u_int cmd, int *error); 5939217Sgibbs 6050477Speterstatic const struct { 6139217Sgibbs const char *name; 6239217Sgibbs bus_size_t nvramsz; 6339217Sgibbs bus_size_t clkoff; 6439217Sgibbs u_int flags; 6539217Sgibbs#define MK48TXX_EXT_REGISTERS 1 /* Has extended register set. */ 6639217Sgibbs} const mk48txx_models[] = { 6739217Sgibbs { "mk48t02", MK48T02_CLKSZ, MK48T02_CLKOFF, 0 }, 6839217Sgibbs { "mk48t08", MK48T08_CLKSZ, MK48T08_CLKOFF, 0 }, 6939217Sgibbs { "mk48t18", MK48T18_CLKSZ, MK48T18_CLKOFF, 0 }, 7039217Sgibbs { "mk48t59", MK48T59_CLKSZ, MK48T59_CLKOFF, MK48TXX_EXT_REGISTERS }, 7139217Sgibbs}; 7239217Sgibbs 7339217Sgibbsint 7439217Sgibbsmk48txx_attach(device_t dev) 7539217Sgibbs{ 7639217Sgibbs struct mk48txx_softc *sc; 7739217Sgibbs int i; 7839217Sgibbs uint8_t wday; 7939217Sgibbs 8055945Sgibbs sc = device_get_softc(dev); 8139217Sgibbs 8239217Sgibbs if (mtx_initialized(&sc->sc_mtx) == 0) { 8339217Sgibbs device_printf(dev, "%s: mutex not initialized\n", __func__); 8439217Sgibbs return (ENXIO); 8539217Sgibbs } 8639217Sgibbs 8741771Sdillon device_printf(dev, "model %s", sc->sc_model); 8839217Sgibbs i = sizeof(mk48txx_models) / sizeof(mk48txx_models[0]); 8939217Sgibbs while (--i >= 0) { 9039217Sgibbs if (strcmp(sc->sc_model, mk48txx_models[i].name) == 0) { 9139217Sgibbs break; 9239217Sgibbs } 9339217Sgibbs } 9442012Sgibbs if (i < 0) { 9542012Sgibbs device_printf(dev, " (unsupported)\n"); 9642012Sgibbs return (ENXIO); 9739217Sgibbs } 9839217Sgibbs printf("\n"); 9939217Sgibbs sc->sc_nvramsz = mk48txx_models[i].nvramsz; 10039217Sgibbs sc->sc_clkoffset = mk48txx_models[i].clkoff; 10139217Sgibbs 10239217Sgibbs if (sc->sc_nvrd == NULL) 10339217Sgibbs sc->sc_nvrd = mk48txx_def_nvrd; 10439217Sgibbs if (sc->sc_nvwr == NULL) 10539217Sgibbs sc->sc_nvwr = mk48txx_def_nvwr; 10646024Speter 10739217Sgibbs if (mk48txx_models[i].flags & MK48TXX_EXT_REGISTERS) { 10841771Sdillon mtx_lock(&sc->sc_mtx); 10939217Sgibbs if ((*sc->sc_nvrd)(dev, sc->sc_clkoffset + MK48TXX_FLAGS) & 11039217Sgibbs MK48TXX_FLAGS_BL) { 11155945Sgibbs mtx_unlock(&sc->sc_mtx); 11255945Sgibbs device_printf(dev, "%s: battery low\n", __func__); 11355945Sgibbs return (ENXIO); 11439217Sgibbs } 11539217Sgibbs mtx_unlock(&sc->sc_mtx); 11639217Sgibbs } 11739217Sgibbs 11839217Sgibbs if (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) { 11955945Sgibbs /* 12039217Sgibbs * Use MK48TXX_WDAY_CB instead of manually adjusting the 12155945Sgibbs * century. 12255945Sgibbs */ 12355945Sgibbs if (!(mk48txx_models[i].flags & MK48TXX_EXT_REGISTERS)) { 12455945Sgibbs device_printf(dev, "%s: no century bit\n", __func__); 12555945Sgibbs return (ENXIO); 12639217Sgibbs } else { 12739217Sgibbs mtx_lock(&sc->sc_mtx); 12839217Sgibbs wday = (*sc->sc_nvrd) 12939217Sgibbs (dev, sc->sc_clkoffset + MK48TXX_IWDAY); 13039217Sgibbs wday |= MK48TXX_WDAY_CEB; 13139217Sgibbs (*sc->sc_nvwr) 13239217Sgibbs (dev, sc->sc_clkoffset + MK48TXX_IWDAY, wday); 13339217Sgibbs mtx_unlock(&sc->sc_mtx); 13439217Sgibbs } 13539217Sgibbs } 13639217Sgibbs 13739217Sgibbs clock_register(dev, 1000000); /* 1 second resolution */ 13839217Sgibbs 13939217Sgibbs if ((sc->sc_flag & MK48TXX_WDOG_REGISTER) && 14039217Sgibbs (mk48txx_models[i].flags & MK48TXX_EXT_REGISTERS)) { 14139217Sgibbs sc->sc_wet = EVENTHANDLER_REGISTER(watchdog_list, 14239217Sgibbs mk48txx_watchdog, dev, 0); 14339217Sgibbs device_printf(dev, 14439217Sgibbs "watchdog registered, timeout interval max. 128 sec\n"); 14539217Sgibbs } 14639217Sgibbs 14739217Sgibbs return (0); 14839217Sgibbs} 14939217Sgibbs 15039217Sgibbs/* 15139217Sgibbs * Get time-of-day and convert to a `struct timespec' 15239217Sgibbs * Return 0 on success; an error number otherwise. 15339217Sgibbs */ 15439217Sgibbsint 15539217Sgibbsmk48txx_gettime(device_t dev, struct timespec *ts) 15639217Sgibbs{ 15739217Sgibbs struct mk48txx_softc *sc; 15839217Sgibbs bus_size_t clkoff; 15939217Sgibbs struct clocktime ct; 16039217Sgibbs int year; 16139217Sgibbs uint8_t csr; 16239217Sgibbs 16339217Sgibbs sc = device_get_softc(dev); 16439217Sgibbs clkoff = sc->sc_clkoffset; 16539217Sgibbs 16639217Sgibbs mtx_lock(&sc->sc_mtx); 16739217Sgibbs /* enable read (stop time) */ 16839217Sgibbs csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR); 16939217Sgibbs csr |= MK48TXX_CSR_READ; 17039217Sgibbs (*sc->sc_nvwr)(dev, clkoff + MK48TXX_ICSR, csr); 17139217Sgibbs 17239217Sgibbs#define FROMREG(reg, mask) ((*sc->sc_nvrd)(dev, clkoff + (reg)) & (mask)) 17339217Sgibbs 17439217Sgibbs ct.nsec = 0; 17539217Sgibbs ct.sec = FROMBCD(FROMREG(MK48TXX_ISEC, MK48TXX_SEC_MASK)); 17639217Sgibbs ct.min = FROMBCD(FROMREG(MK48TXX_IMIN, MK48TXX_MIN_MASK)); 17739217Sgibbs ct.hour = FROMBCD(FROMREG(MK48TXX_IHOUR, MK48TXX_HOUR_MASK)); 17839217Sgibbs ct.day = FROMBCD(FROMREG(MK48TXX_IDAY, MK48TXX_DAY_MASK)); 17939217Sgibbs#if 0 18049860Sgibbs /* Map dow from 1 - 7 to 0 - 6; FROMBCD() isn't necessary here. */ 18139217Sgibbs ct.dow = FROMREG(MK48TXX_IWDAY, MK48TXX_WDAY_MASK) - 1; 18239217Sgibbs#else 18339217Sgibbs /* 18439217Sgibbs * Set dow = -1 because some drivers (for example the NetBSD and 18539217Sgibbs * OpenBSD mk48txx(4)) don't set it correctly. 18639217Sgibbs */ 18739217Sgibbs ct.dow = -1; 18839217Sgibbs#endif 18939217Sgibbs ct.mon = FROMBCD(FROMREG(MK48TXX_IMON, MK48TXX_MON_MASK)); 19039217Sgibbs year = FROMBCD(FROMREG(MK48TXX_IYEAR, MK48TXX_YEAR_MASK)); 19139217Sgibbs year += sc->sc_year0; 19239217Sgibbs if (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) 19339217Sgibbs year += (FROMREG(MK48TXX_IWDAY, MK48TXX_WDAY_CB) >> 19439217Sgibbs MK48TXX_WDAY_CB_SHIFT) * 100; 19539217Sgibbs else if (year < POSIX_BASE_YEAR) 19639217Sgibbs year += 100; 19739217Sgibbs 19839217Sgibbs#undef FROMREG 19939217Sgibbs 20039217Sgibbs ct.year = year; 20139217Sgibbs 20239217Sgibbs /* time wears on */ 20339217Sgibbs csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR); 20439217Sgibbs csr &= ~MK48TXX_CSR_READ; 20539217Sgibbs (*sc->sc_nvwr)(dev, clkoff + MK48TXX_ICSR, csr); 20639217Sgibbs mtx_unlock(&sc->sc_mtx); 20739217Sgibbs 20839217Sgibbs return (clock_ct_to_ts(&ct, ts)); 20939217Sgibbs} 21039217Sgibbs 21139217Sgibbs/* 21239217Sgibbs * Set the time-of-day clock based on the value of the `struct timespec' arg. 21339217Sgibbs * Return 0 on success; an error number otherwise. 21439217Sgibbs */ 21539217Sgibbsint 21639217Sgibbsmk48txx_settime(device_t dev, struct timespec *ts) 21739217Sgibbs{ 21839217Sgibbs struct mk48txx_softc *sc; 21939217Sgibbs bus_size_t clkoff; 22039217Sgibbs struct clocktime ct; 22139217Sgibbs uint8_t csr; 22239217Sgibbs int cent, year; 22339217Sgibbs 22439217Sgibbs sc = device_get_softc(dev); 22539217Sgibbs clkoff = sc->sc_clkoffset; 22639217Sgibbs 22739217Sgibbs /* Accuracy is only one second. */ 22839217Sgibbs if (ts->tv_nsec >= 500000000) 22939217Sgibbs ts->tv_sec++; 23039217Sgibbs ts->tv_nsec = 0; 23139217Sgibbs clock_ts_to_ct(ts, &ct); 23239217Sgibbs 23339217Sgibbs mtx_lock(&sc->sc_mtx); 23439217Sgibbs /* enable write */ 23539217Sgibbs csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR); 23639217Sgibbs csr |= MK48TXX_CSR_WRITE; 23739217Sgibbs (*sc->sc_nvwr)(dev, clkoff + MK48TXX_ICSR, csr); 23839217Sgibbs 23939217Sgibbs#define TOREG(reg, mask, val) \ 24039217Sgibbs ((*sc->sc_nvwr)(dev, clkoff + (reg), \ 24139217Sgibbs ((*sc->sc_nvrd)(dev, clkoff + (reg)) & ~(mask)) | \ 24239217Sgibbs ((val) & (mask)))) 24339217Sgibbs 24439217Sgibbs TOREG(MK48TXX_ISEC, MK48TXX_SEC_MASK, TOBCD(ct.sec)); 24539217Sgibbs TOREG(MK48TXX_IMIN, MK48TXX_MIN_MASK, TOBCD(ct.min)); 24639217Sgibbs TOREG(MK48TXX_IHOUR, MK48TXX_HOUR_MASK, TOBCD(ct.hour)); 24739217Sgibbs /* Map dow from 0 - 6 to 1 - 7; TOBCD() isn't necessary here. */ 24855945Sgibbs TOREG(MK48TXX_IWDAY, MK48TXX_WDAY_MASK, ct.dow + 1); 24939217Sgibbs TOREG(MK48TXX_IDAY, MK48TXX_DAY_MASK, TOBCD(ct.day)); 25039217Sgibbs TOREG(MK48TXX_IMON, MK48TXX_MON_MASK, TOBCD(ct.mon)); 25139217Sgibbs 25239217Sgibbs year = ct.year - sc->sc_year0; 25339217Sgibbs if (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) { 25439217Sgibbs cent = year / 100; 25539217Sgibbs TOREG(MK48TXX_IWDAY, MK48TXX_WDAY_CB, 25639217Sgibbs cent << MK48TXX_WDAY_CB_SHIFT); 25739217Sgibbs year -= cent * 100; 25839217Sgibbs } else if (year > 99) 25939217Sgibbs year -= 100; 26039217Sgibbs TOREG(MK48TXX_IYEAR, MK48TXX_YEAR_MASK, TOBCD(year)); 26139217Sgibbs 26239217Sgibbs#undef TOREG 26339217Sgibbs 26441591Sarchie /* load them up */ 26541591Sarchie csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR); 26641591Sarchie csr &= ~MK48TXX_CSR_WRITE; 26741591Sarchie (*sc->sc_nvwr)(dev, clkoff + MK48TXX_ICSR, csr); 26841591Sarchie mtx_unlock(&sc->sc_mtx); 26941591Sarchie return (0); 27041591Sarchie} 27139217Sgibbs 27239217Sgibbsstatic uint8_t 27339217Sgibbsmk48txx_def_nvrd(device_t dev, int off) 27439217Sgibbs{ 27539217Sgibbs struct mk48txx_softc *sc; 27639217Sgibbs 27739217Sgibbs sc = device_get_softc(dev); 27839217Sgibbs return (bus_read_1(sc->sc_res, off)); 27939217Sgibbs} 28039217Sgibbs 28139217Sgibbsstatic void 28239217Sgibbsmk48txx_def_nvwr(device_t dev, int off, uint8_t v) 28339217Sgibbs{ 28439217Sgibbs struct mk48txx_softc *sc; 28539217Sgibbs 28639217Sgibbs sc = device_get_softc(dev); 287 bus_write_1(sc->sc_res, off, v); 288} 289 290static void 291mk48txx_watchdog(void *arg, u_int cmd, int *error) 292{ 293 device_t dev; 294 struct mk48txx_softc *sc; 295 uint8_t t, wdog; 296 297 dev = arg; 298 sc = device_get_softc(dev); 299 300 t = cmd & WD_INTERVAL; 301 if (t >= 26 && t <= 37) { 302 wdog = 0; 303 if (t <= WD_TO_2SEC) { 304 wdog |= MK48TXX_WDOG_RB_1_16; 305 t -= 26; 306 } else if (t <= WD_TO_8SEC) { 307 wdog |= MK48TXX_WDOG_RB_1_4; 308 t -= WD_TO_250MS; 309 } else if (t <= WD_TO_32SEC) { 310 wdog |= MK48TXX_WDOG_RB_1; 311 t -= WD_TO_1SEC; 312 } else { 313 wdog |= MK48TXX_WDOG_RB_4; 314 t -= WD_TO_4SEC; 315 } 316 wdog |= (min(1 << t, 317 MK48TXX_WDOG_BMB_MASK >> MK48TXX_WDOG_BMB_SHIFT)) << 318 MK48TXX_WDOG_BMB_SHIFT; 319 if (sc->sc_flag & MK48TXX_WDOG_ENABLE_WDS) 320 wdog |= MK48TXX_WDOG_WDS; 321 *error = 0; 322 } else { 323 wdog = 0; 324 } 325 mtx_lock(&sc->sc_mtx); 326 (*sc->sc_nvwr)(dev, sc->sc_clkoffset + MK48TXX_WDOG, wdog); 327 mtx_unlock(&sc->sc_mtx); 328} 329