1 /* $NetBSD: mkclock_isa.c,v 1.15 2014/11/20 16:34:25 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Klaus J. Klein. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Mostek MK48T18 time-of-day chip attachment to ISA bus, using two 34 * 8-bit ports for address selection and one 8-bit port for data. 35 */ 36 37 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 38 __KERNEL_RCSID(0, "$NetBSD: mkclock_isa.c,v 1.15 2014/11/20 16:34:25 christos Exp $"); 39 40 #include <sys/param.h> 41 #include <sys/kernel.h> 42 #include <sys/systm.h> 43 #include <sys/device.h> 44 45 #include <sys/bus.h> 46 47 #include <dev/clock_subr.h> 48 #include <dev/ic/mk48txxreg.h> 49 #include <dev/ic/mk48txxvar.h> 50 51 #include <dev/isa/isavar.h> 52 53 54 /* Offsets of registers into ISA I/O space */ 55 #define MKCLOCK_STB0 0 /* Address low */ 56 #define MKCLOCK_STB1 1 /* Address high */ 57 #define MKCLOCK_DATA 3 /* Data port */ 58 59 #define MKCLOCK_NPORTS (MKCLOCK_DATA - MKCLOCK_STB0 + 1) 60 61 62 /* Autoconfiguration interface */ 63 int mkclock_isa_match(device_t, cfdata_t, void *); 64 void mkclock_isa_attach(device_t, device_t, void *); 65 66 CFATTACH_DECL_NEW(mkclock_isa, sizeof(struct mk48txx_softc), 67 mkclock_isa_match, mkclock_isa_attach, NULL, NULL); 68 69 70 /* mk48txx interface */ 71 uint8_t mkclock_isa_nvrd(struct mk48txx_softc *, int); 72 void mkclock_isa_nvwr(struct mk48txx_softc *, int, uint8_t); 73 74 75 int 76 mkclock_isa_match(device_t parent, cfdata_t cf, void *aux) 77 { 78 struct isa_attach_args *ia = aux; 79 struct mk48txx_softc mk48txx, *sc; 80 uint8_t csr, ocsr; 81 unsigned int t1, t2; 82 int found; 83 84 found = 0; 85 86 if (ia->ia_nio < 1 || 87 (ia->ia_io[0].ir_addr != ISA_UNKNOWN_PORT && 88 ia->ia_io[0].ir_addr != 0x74)) 89 return (0); 90 91 if (ia->ia_niomem > 0 && 92 (ia->ia_iomem[0].ir_addr != ISA_UNKNOWN_IOMEM)) 93 return (0); 94 95 if (ia->ia_nirq > 0 && 96 (ia->ia_irq[0].ir_irq != ISA_UNKNOWN_IRQ)) 97 return (0); 98 99 if (ia->ia_ndrq > 0 && 100 (ia->ia_drq[0].ir_drq != ISA_UNKNOWN_DRQ)) 101 return (0); 102 103 /* 104 * Map I/O space, then try to determine if it's really there. 105 */ 106 sc = &mk48txx; 107 sc->sc_bst = ia->ia_iot; 108 if (bus_space_map(sc->sc_bst, 0x74, MKCLOCK_NPORTS, 0, &sc->sc_bsh)) 109 return (0); 110 111 /* Supposedly no control bits are set after POST; check for this. */ 112 ocsr = mkclock_isa_nvrd(sc, MK48T18_CLKOFF + MK48TXX_ICSR); 113 if (ocsr != 0) 114 goto unmap; 115 116 /* Set clock data to read mode, prohibiting updates from clock. */ 117 csr = ocsr | MK48TXX_CSR_READ; 118 mkclock_isa_nvwr(sc, MK48T18_CLKOFF + MK48TXX_ICSR, csr); 119 /* Compare. */ 120 if (mkclock_isa_nvrd(sc, MK48T18_CLKOFF + MK48TXX_ICSR) != csr) 121 goto restore; 122 123 /* Read from the seconds counter. */ 124 t1 = bcdtobin(mkclock_isa_nvrd(sc, MK48T18_CLKOFF + MK48TXX_ISEC)); 125 if (t1 > 59) 126 goto restore; 127 128 /* Make it tick again, wait, then look again. */ 129 mkclock_isa_nvwr(sc, MK48T18_CLKOFF + MK48TXX_ICSR, ocsr); 130 DELAY(1100000); 131 mkclock_isa_nvwr(sc, MK48T18_CLKOFF + MK48TXX_ICSR, csr); 132 t2 = bcdtobin(mkclock_isa_nvrd(sc, MK48T18_CLKOFF + MK48TXX_ISEC)); 133 if (t2 > 59) 134 goto restore; 135 136 /* If [1,2) seconds have passed since, call it a clock. */ 137 if ((t1 + 1) % 60 == t2 || (t1 + 2) % 60 == t2) 138 found = 1; 139 140 restore: 141 mkclock_isa_nvwr(sc, MK48T18_CLKOFF + MK48TXX_ICSR, ocsr); 142 unmap: 143 bus_space_unmap(sc->sc_bst, sc->sc_bsh, MKCLOCK_NPORTS); 144 145 if (found) { 146 ia->ia_nio = 1; 147 ia->ia_io[0].ir_addr = 0x74; 148 ia->ia_io[0].ir_size = MKCLOCK_NPORTS; 149 150 ia->ia_niomem = 0; 151 ia->ia_nirq = 0; 152 ia->ia_ndrq = 0; 153 } 154 155 return (found); 156 } 157 158 void 159 mkclock_isa_attach(device_t parent, device_t self, void *aux) 160 { 161 struct mk48txx_softc *sc = device_private(self); 162 struct isa_attach_args *ia = aux; 163 164 sc->sc_dev = self; 165 166 /* Map I/O space. */ 167 sc->sc_bst = ia->ia_iot; 168 if (bus_space_map(sc->sc_bst, ia->ia_io[0].ir_addr, 169 ia->ia_io[0].ir_size, 0, &sc->sc_bsh)) 170 panic("mkclock_isa_attach: couldn't map clock I/O space"); 171 172 /* Attach to MI mk48txx driver. */ 173 sc->sc_model = "mk48t18"; 174 sc->sc_year0 = 1968; 175 sc->sc_nvrd = mkclock_isa_nvrd; 176 sc->sc_nvwr = mkclock_isa_nvwr; 177 178 mk48txx_attach(sc); 179 180 aprint_normal(" Timekeeper NVRAM/RTC\n"); 181 } 182 183 /* 184 * Bus access methods for MI mk48txx driver. 185 */ 186 uint8_t 187 mkclock_isa_nvrd(struct mk48txx_softc *sc, int off) 188 { 189 bus_space_tag_t iot; 190 bus_space_handle_t ioh; 191 uint8_t datum; 192 int s; 193 194 iot = sc->sc_bst; 195 ioh = sc->sc_bsh; 196 197 s = splclock(); 198 bus_space_write_1(iot, ioh, MKCLOCK_STB0, off & 0xff); 199 bus_space_write_1(iot, ioh, MKCLOCK_STB1, off >> 8); 200 datum = bus_space_read_1(iot, ioh, MKCLOCK_DATA); 201 splx(s); 202 203 return (datum); 204 } 205 206 void 207 mkclock_isa_nvwr(struct mk48txx_softc *sc, int off, uint8_t datum) 208 { 209 bus_space_tag_t iot; 210 bus_space_handle_t ioh; 211 int s; 212 213 iot = sc->sc_bst; 214 ioh = sc->sc_bsh; 215 216 s = splclock(); 217 bus_space_write_1(iot, ioh, MKCLOCK_STB0, off & 0xff); 218 bus_space_write_1(iot, ioh, MKCLOCK_STB1, off >> 8); 219 bus_space_write_1(iot, ioh, MKCLOCK_DATA, datum); 220 splx(s); 221 } 222