1a5261280SConstantine A. Murenin /* $NetBSD: wbsio.c,v 1.1 2010/02/21 05:16:29 cnst Exp $ */ 2a5261280SConstantine A. Murenin /* $OpenBSD: wbsio.c,v 1.5 2009/03/29 21:53:52 sthen Exp $ */ 3a5261280SConstantine A. Murenin /* 4a5261280SConstantine A. Murenin * Copyright (c) 2008 Mark Kettenis <kettenis@openbsd.org> 5f81520edSConstantine A. Murenin * Copyright (c) 2010 Constantine A. Murenin <cnst++@dragonflybsd.org> 6a5261280SConstantine A. Murenin * 7a5261280SConstantine A. Murenin * Permission to use, copy, modify, and distribute this software for any 8a5261280SConstantine A. Murenin * purpose with or without fee is hereby granted, provided that the above 9a5261280SConstantine A. Murenin * copyright notice and this permission notice appear in all copies. 10a5261280SConstantine A. Murenin * 11a5261280SConstantine A. Murenin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12a5261280SConstantine A. Murenin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13a5261280SConstantine A. Murenin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14a5261280SConstantine A. Murenin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15a5261280SConstantine A. Murenin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16a5261280SConstantine A. Murenin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17a5261280SConstantine A. Murenin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18a5261280SConstantine A. Murenin */ 19a5261280SConstantine A. Murenin 20a5261280SConstantine A. Murenin /* 21a5261280SConstantine A. Murenin * Winbond LPC Super I/O driver. 22a5261280SConstantine A. Murenin */ 23a5261280SConstantine A. Murenin 24a5261280SConstantine A. Murenin #include <sys/param.h> 25f81520edSConstantine A. Murenin #include <sys/bus.h> 26a5261280SConstantine A. Murenin #include <sys/kernel.h> 27f81520edSConstantine A. Murenin #include <sys/module.h> 28f81520edSConstantine A. Murenin #include <sys/rman.h> 29a5261280SConstantine A. Murenin #include <sys/systm.h> 30a5261280SConstantine A. Murenin 31f81520edSConstantine A. Murenin #include <bus/isa/isavar.h> 32a5261280SConstantine A. Murenin 33a5261280SConstantine A. Murenin /* ISA bus registers */ 34a5261280SConstantine A. Murenin #define WBSIO_INDEX 0x00 /* Configuration Index Register */ 35a5261280SConstantine A. Murenin #define WBSIO_DATA 0x01 /* Configuration Data Register */ 36a5261280SConstantine A. Murenin 37a5261280SConstantine A. Murenin #define WBSIO_IOSIZE 0x02 /* ISA I/O space size */ 38a5261280SConstantine A. Murenin 39a5261280SConstantine A. Murenin #define WBSIO_CONF_EN_MAGIC 0x87 /* enable configuration mode */ 40a5261280SConstantine A. Murenin #define WBSIO_CONF_DS_MAGIC 0xaa /* disable configuration mode */ 41a5261280SConstantine A. Murenin 42a5261280SConstantine A. Murenin /* Configuration Space Registers */ 43a5261280SConstantine A. Murenin #define WBSIO_LDN 0x07 /* Logical Device Number */ 44a5261280SConstantine A. Murenin #define WBSIO_ID 0x20 /* Device ID */ 45a5261280SConstantine A. Murenin #define WBSIO_REV 0x21 /* Device Revision */ 46a5261280SConstantine A. Murenin 47a5261280SConstantine A. Murenin #define WBSIO_ID_W83627HF 0x52 48a5261280SConstantine A. Murenin #define WBSIO_ID_W83627THF 0x82 49a5261280SConstantine A. Murenin #define WBSIO_ID_W83627EHF 0x88 50a5261280SConstantine A. Murenin #define WBSIO_ID_W83627DHG 0xa0 51a5261280SConstantine A. Murenin #define WBSIO_ID_W83627SF 0x59 52a5261280SConstantine A. Murenin #define WBSIO_ID_W83637HF 0x70 531c486d4bSConstantine A. Murenin #define WBSIO_ID_W83667HG 0xa5 54a5261280SConstantine A. Murenin #define WBSIO_ID_W83697HF 0x60 55a5261280SConstantine A. Murenin 56a5261280SConstantine A. Murenin /* Logical Device Number (LDN) Assignments */ 57a5261280SConstantine A. Murenin #define WBSIO_LDN_HM 0x0b 58a5261280SConstantine A. Murenin 59a5261280SConstantine A. Murenin /* Hardware Monitor Control Registers (LDN B) */ 60a5261280SConstantine A. Murenin #define WBSIO_HM_ADDR_MSB 0x60 /* Address [15:8] */ 61a5261280SConstantine A. Murenin #define WBSIO_HM_ADDR_LSB 0x61 /* Address [7:0] */ 62a5261280SConstantine A. Murenin 63a5261280SConstantine A. Murenin struct wbsio_softc { 64f81520edSConstantine A. Murenin struct device *sc_dev; 65f81520edSConstantine A. Murenin 66f81520edSConstantine A. Murenin struct resource *sc_iores; 67f81520edSConstantine A. Murenin int sc_iorid; 68a5261280SConstantine A. Murenin 69a5261280SConstantine A. Murenin bus_space_tag_t sc_iot; 70a5261280SConstantine A. Murenin bus_space_handle_t sc_ioh; 71a5261280SConstantine A. Murenin }; 72a5261280SConstantine A. Murenin 73*fa130c02SConstantine A. Murenin static void wbsio_identify(driver_t *, struct device *); 74f81520edSConstantine A. Murenin static int wbsio_probe(struct device *); 75f81520edSConstantine A. Murenin static int wbsio_attach(struct device *); 76f81520edSConstantine A. Murenin static int wbsio_detach(struct device *); 77a5261280SConstantine A. Murenin 78f81520edSConstantine A. Murenin static device_method_t wbsio_methods[] = { 79*fa130c02SConstantine A. Murenin DEVMETHOD(device_identify, wbsio_identify), 80f81520edSConstantine A. Murenin DEVMETHOD(device_probe, wbsio_probe), 81f81520edSConstantine A. Murenin DEVMETHOD(device_attach, wbsio_attach), 82f81520edSConstantine A. Murenin DEVMETHOD(device_detach, wbsio_detach), 83f81520edSConstantine A. Murenin 84f81520edSConstantine A. Murenin { NULL, NULL} 85f81520edSConstantine A. Murenin }; 86f81520edSConstantine A. Murenin 87f81520edSConstantine A. Murenin static driver_t wbsio_driver = { 88f81520edSConstantine A. Murenin "wbsio", 89f81520edSConstantine A. Murenin wbsio_methods, 90f81520edSConstantine A. Murenin sizeof(struct wbsio_softc) 91f81520edSConstantine A. Murenin }; 92f81520edSConstantine A. Murenin 93f81520edSConstantine A. Murenin static devclass_t wbsio_devclass; 94f81520edSConstantine A. Murenin 95f81520edSConstantine A. Murenin DRIVER_MODULE(wbsio, isa, wbsio_driver, wbsio_devclass, NULL, NULL); 96f81520edSConstantine A. Murenin 97a5261280SConstantine A. Murenin 98a5261280SConstantine A. Murenin static __inline void 99a5261280SConstantine A. Murenin wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh) 100a5261280SConstantine A. Murenin { 101a5261280SConstantine A. Murenin bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 102a5261280SConstantine A. Murenin bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 103a5261280SConstantine A. Murenin } 104a5261280SConstantine A. Murenin 105a5261280SConstantine A. Murenin static __inline void 106a5261280SConstantine A. Murenin wbsio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh) 107a5261280SConstantine A. Murenin { 108a5261280SConstantine A. Murenin bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_DS_MAGIC); 109a5261280SConstantine A. Murenin } 110a5261280SConstantine A. Murenin 111a5261280SConstantine A. Murenin static __inline u_int8_t 112a5261280SConstantine A. Murenin wbsio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index) 113a5261280SConstantine A. Murenin { 114a5261280SConstantine A. Murenin bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 115a5261280SConstantine A. Murenin return (bus_space_read_1(iot, ioh, WBSIO_DATA)); 116a5261280SConstantine A. Murenin } 117a5261280SConstantine A. Murenin 118a5261280SConstantine A. Murenin static __inline void 119a5261280SConstantine A. Murenin wbsio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index, 120a5261280SConstantine A. Murenin u_int8_t data) 121a5261280SConstantine A. Murenin { 122a5261280SConstantine A. Murenin bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 123a5261280SConstantine A. Murenin bus_space_write_1(iot, ioh, WBSIO_DATA, data); 124a5261280SConstantine A. Murenin } 125a5261280SConstantine A. Murenin 126*fa130c02SConstantine A. Murenin static void 127*fa130c02SConstantine A. Murenin wbsio_identify(driver_t *driver, struct device *parent) 128*fa130c02SConstantine A. Murenin { 129*fa130c02SConstantine A. Murenin #ifdef KLD_MODULE 130*fa130c02SConstantine A. Murenin struct device *child[2]; 131*fa130c02SConstantine A. Murenin const int port[2] = { 0x2e, 0x4e }; 132*fa130c02SConstantine A. Murenin 133*fa130c02SConstantine A. Murenin for (int i = 0; i < 2; i++) { 134*fa130c02SConstantine A. Murenin child[i] = device_find_child(parent, driver->name, i); 135*fa130c02SConstantine A. Murenin if (child[i] == NULL) { 136*fa130c02SConstantine A. Murenin child[i] = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP, 137*fa130c02SConstantine A. Murenin driver->name, i); 138*fa130c02SConstantine A. Murenin if (child[i] == NULL) { 139*fa130c02SConstantine A. Murenin kprintf("%s: cannot add child[%i]\n", 140*fa130c02SConstantine A. Murenin __func__, i); 141*fa130c02SConstantine A. Murenin continue; 142*fa130c02SConstantine A. Murenin } 143*fa130c02SConstantine A. Murenin } else 144*fa130c02SConstantine A. Murenin continue; 145*fa130c02SConstantine A. Murenin if (bus_set_resource(child[i], SYS_RES_IOPORT, 0, 146*fa130c02SConstantine A. Murenin port[i], WBSIO_IOSIZE)) 147*fa130c02SConstantine A. Murenin kprintf("%s: cannot set resource for child[%i]\n", 148*fa130c02SConstantine A. Murenin __func__, i); 149*fa130c02SConstantine A. Murenin } 150*fa130c02SConstantine A. Murenin #endif 151*fa130c02SConstantine A. Murenin } 152*fa130c02SConstantine A. Murenin 153f81520edSConstantine A. Murenin static int 154f81520edSConstantine A. Murenin wbsio_probe(struct device *dev) 155a5261280SConstantine A. Murenin { 156f81520edSConstantine A. Murenin struct resource *iores; 157f81520edSConstantine A. Murenin int iorid = 0; 158a5261280SConstantine A. Murenin bus_space_tag_t iot; 159a5261280SConstantine A. Murenin bus_space_handle_t ioh; 160f81520edSConstantine A. Murenin uint8_t reg_id, reg_rev; 161f81520edSConstantine A. Murenin const char *desc = NULL; 162f81520edSConstantine A. Murenin char fulldesc[64]; 163a5261280SConstantine A. Murenin 164a5261280SConstantine A. Murenin /* Match by device ID */ 165f81520edSConstantine A. Murenin 166f81520edSConstantine A. Murenin iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid, 167f81520edSConstantine A. Murenin 0ul, ~0ul, WBSIO_IOSIZE, 168f81520edSConstantine A. Murenin RF_ACTIVE); 169f81520edSConstantine A. Murenin if (iores == NULL) 170f81520edSConstantine A. Murenin return ENXIO; 171f81520edSConstantine A. Murenin iot = rman_get_bustag(iores); 172f81520edSConstantine A. Murenin ioh = rman_get_bushandle(iores); 173f81520edSConstantine A. Murenin 174a5261280SConstantine A. Murenin wbsio_conf_enable(iot, ioh); 175a5261280SConstantine A. Murenin /* Read device ID */ 176f81520edSConstantine A. Murenin reg_id = wbsio_conf_read(iot, ioh, WBSIO_ID); 177f81520edSConstantine A. Murenin /* Read device revision */ 178f81520edSConstantine A. Murenin reg_rev = wbsio_conf_read(iot, ioh, WBSIO_REV); 179f81520edSConstantine A. Murenin wbsio_conf_disable(iot, ioh); 180f81520edSConstantine A. Murenin bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores); 181f81520edSConstantine A. Murenin 182f81520edSConstantine A. Murenin switch (reg_id) { 183a5261280SConstantine A. Murenin case WBSIO_ID_W83627HF: 184a5261280SConstantine A. Murenin desc = "W83627HF"; 185a5261280SConstantine A. Murenin break; 186a5261280SConstantine A. Murenin case WBSIO_ID_W83627THF: 187a5261280SConstantine A. Murenin desc = "W83627THF"; 188a5261280SConstantine A. Murenin break; 189a5261280SConstantine A. Murenin case WBSIO_ID_W83627EHF: 190a5261280SConstantine A. Murenin desc = "W83627EHF"; 191a5261280SConstantine A. Murenin break; 192a5261280SConstantine A. Murenin case WBSIO_ID_W83627DHG: 193a5261280SConstantine A. Murenin desc = "W83627DHG"; 194a5261280SConstantine A. Murenin break; 195a5261280SConstantine A. Murenin case WBSIO_ID_W83637HF: 196a5261280SConstantine A. Murenin desc = "W83637HF"; 197a5261280SConstantine A. Murenin break; 1981c486d4bSConstantine A. Murenin case WBSIO_ID_W83667HG: 1991c486d4bSConstantine A. Murenin desc = "W83667HG"; 2001c486d4bSConstantine A. Murenin break; 201a5261280SConstantine A. Murenin case WBSIO_ID_W83697HF: 202a5261280SConstantine A. Murenin desc = "W83697HF"; 203a5261280SConstantine A. Murenin break; 204a5261280SConstantine A. Murenin } 205a5261280SConstantine A. Murenin 206f81520edSConstantine A. Murenin if (desc == NULL) 207f81520edSConstantine A. Murenin return ENXIO; 208a5261280SConstantine A. Murenin 209f81520edSConstantine A. Murenin ksnprintf(fulldesc, sizeof(fulldesc), 210f81520edSConstantine A. Murenin "Winbond LPC Super I/O %s rev 0x%02x", desc, reg_rev); 211f81520edSConstantine A. Murenin device_set_desc_copy(dev, fulldesc); 212f81520edSConstantine A. Murenin return 0; 213f81520edSConstantine A. Murenin } 214f81520edSConstantine A. Murenin 215f81520edSConstantine A. Murenin static int 216f81520edSConstantine A. Murenin wbsio_attach(struct device *dev) 217f81520edSConstantine A. Murenin { 218f81520edSConstantine A. Murenin struct wbsio_softc *sc = device_get_softc(dev); 219f81520edSConstantine A. Murenin uint8_t reg0, reg1; 220f81520edSConstantine A. Murenin uint16_t iobase; 221f81520edSConstantine A. Murenin struct device *parent = device_get_parent(dev); 222f81520edSConstantine A. Murenin struct device *child; 223f81520edSConstantine A. Murenin struct devclass *c_dc; 224f81520edSConstantine A. Murenin int c_maxunit; 225f81520edSConstantine A. Murenin 226f81520edSConstantine A. Murenin /* Map ISA I/O space */ 227f81520edSConstantine A. Murenin sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid, 228f81520edSConstantine A. Murenin 0ul, ~0ul, WBSIO_IOSIZE, 229f81520edSConstantine A. Murenin RF_ACTIVE); 230f81520edSConstantine A. Murenin if (sc->sc_iores == NULL) { 231f81520edSConstantine A. Murenin device_printf(dev, "can't map i/o space\n"); 232f81520edSConstantine A. Murenin return ENXIO; 233f81520edSConstantine A. Murenin } 234f81520edSConstantine A. Murenin sc->sc_iot = rman_get_bustag(sc->sc_iores); 235f81520edSConstantine A. Murenin sc->sc_ioh = rman_get_bushandle(sc->sc_iores); 236f81520edSConstantine A. Murenin 237f81520edSConstantine A. Murenin /* Enter configuration mode */ 238f81520edSConstantine A. Murenin wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); 239a5261280SConstantine A. Murenin 240a5261280SConstantine A. Murenin /* Select HM logical device */ 241a5261280SConstantine A. Murenin wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM); 242a5261280SConstantine A. Murenin 243a5261280SConstantine A. Murenin /* 244a5261280SConstantine A. Murenin * The address should be 8-byte aligned, but it seems some 245a5261280SConstantine A. Murenin * BIOSes ignore this. They get away with it, because 246a5261280SConstantine A. Murenin * Apparently the hardware simply ignores the lower three 247a5261280SConstantine A. Murenin * bits. We do the same here. 248a5261280SConstantine A. Murenin */ 249a5261280SConstantine A. Murenin reg0 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_LSB); 250a5261280SConstantine A. Murenin reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB); 251a5261280SConstantine A. Murenin iobase = (reg1 << 8) | (reg0 & ~0x7); 252f81520edSConstantine A. Murenin device_printf(dev, "hardware monitor iobase is 0x%x\n", iobase); 253a5261280SConstantine A. Murenin 254a5261280SConstantine A. Murenin /* Escape from configuration mode */ 255a5261280SConstantine A. Murenin wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); 256a5261280SConstantine A. Murenin 257f81520edSConstantine A. Murenin if (iobase == 0) { 258f81520edSConstantine A. Murenin device_printf(dev, "no hardware monitor configured\n"); 259f81520edSConstantine A. Murenin return 0; 260a5261280SConstantine A. Murenin } 261a5261280SConstantine A. Murenin 262f81520edSConstantine A. Murenin child = NULL; 263f81520edSConstantine A. Murenin c_dc = devclass_find("lm"); 264f81520edSConstantine A. Murenin if (c_dc == NULL) { 265f81520edSConstantine A. Murenin device_printf(dev, "lm devclass not found\n"); 266f81520edSConstantine A. Murenin return ENXIO; 267f81520edSConstantine A. Murenin } 268f81520edSConstantine A. Murenin c_maxunit = devclass_get_maxunit(c_dc); 269f81520edSConstantine A. Murenin for (int u = 0; u < c_maxunit; u++) { 270f81520edSConstantine A. Murenin child = devclass_get_device(c_dc, u); 271f81520edSConstantine A. Murenin if (child == NULL) 272f81520edSConstantine A. Murenin continue; 273f81520edSConstantine A. Murenin if (isa_get_port(child) == iobase) { 274f81520edSConstantine A. Murenin if (device_is_attached(child)) { 275f81520edSConstantine A. Murenin device_printf(dev, 276f81520edSConstantine A. Murenin "%s is already attached at 0x%x\n", 277f81520edSConstantine A. Murenin device_get_nameunit(child), iobase); 278f81520edSConstantine A. Murenin return 0; 279f81520edSConstantine A. Murenin } 280f81520edSConstantine A. Murenin break; 281f81520edSConstantine A. Murenin } 282647ed4b2SConstantine A. Murenin if (device_is_attached(child)) { 283647ed4b2SConstantine A. Murenin child = NULL; 284f81520edSConstantine A. Murenin continue; 285647ed4b2SConstantine A. Murenin } 286f81520edSConstantine A. Murenin device_printf(dev, 287f81520edSConstantine A. Murenin "found unused %s at 0x%x with state %i, reusing at 0x%x\n", 288f81520edSConstantine A. Murenin device_get_nameunit(child), isa_get_port(child), 289f81520edSConstantine A. Murenin device_get_state(child), iobase); 290f81520edSConstantine A. Murenin break; 291f81520edSConstantine A. Murenin } 292f81520edSConstantine A. Murenin if (child == NULL) 293f81520edSConstantine A. Murenin child = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP, 294f81520edSConstantine A. Murenin "lm", -1); 295f81520edSConstantine A. Murenin // child = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP, 296f81520edSConstantine A. Murenin // "lm", 3 + device_get_unit(dev)); 297f81520edSConstantine A. Murenin if (child == NULL) { 298f81520edSConstantine A. Murenin device_printf(dev, "cannot add child\n"); 299f81520edSConstantine A. Murenin return ENXIO; 300f81520edSConstantine A. Murenin } 301f81520edSConstantine A. Murenin if (bus_set_resource(child, SYS_RES_IOPORT, 0, iobase, 8)) { 302f81520edSConstantine A. Murenin device_printf(dev, "cannot set resource\n"); 303f81520edSConstantine A. Murenin return ENXIO; 304f81520edSConstantine A. Murenin } 305f81520edSConstantine A. Murenin return device_probe_and_attach(child); 306f81520edSConstantine A. Murenin } 307a5261280SConstantine A. Murenin 308f81520edSConstantine A. Murenin static int 309f81520edSConstantine A. Murenin wbsio_detach(struct device *dev) 310f81520edSConstantine A. Murenin { 311f81520edSConstantine A. Murenin struct wbsio_softc *sc = device_get_softc(dev); 312f81520edSConstantine A. Murenin 313f81520edSConstantine A. Murenin return bus_release_resource(dev, SYS_RES_IOPORT, 314f81520edSConstantine A. Murenin sc->sc_iorid, sc->sc_iores); 315a5261280SConstantine A. Murenin } 316