pcf_isa.c revision 256281
1192886Sedwin/*- 2192886Sedwin * Copyright (c) 2004 Joerg Wunsch 3153761Swollman * 42742Swollman * derived from sys/i386/isa/pcf.c which is: 586464Swollman * 62742Swollman * Copyright (c) 1998 Nicolas Souchu, Marc Bouget 72742Swollman * All rights reserved. 82742Swollman * 92742Swollman * Redistribution and use in source and binary forms, with or without 102742Swollman * modification, are permitted provided that the following conditions 112742Swollman * are met: 1286222Swollman * 1. Redistributions of source code must retain the above copyright 1386222Swollman * notice, this list of conditions and the following disclaimer. 142742Swollman * 2. Redistributions in binary form must reproduce the above copyright 15270817Spluknet * notice, this list of conditions and the following disclaimer in the 16270817Spluknet * documentation and/or other materials provided with the distribution. 17270817Spluknet * 18270817Spluknet * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19270817Spluknet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20270817Spluknet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21270817Spluknet * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2258787Sru * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2358787Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2458787Sru * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 252742Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 262742Swollman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 279908Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 282742Swollman * SUCH DAMAGE. 29270817Spluknet */ 30270817Spluknet#include <sys/cdefs.h> 319908Swollman__FBSDID("$FreeBSD: stable/10/sys/dev/pcf/pcf_isa.c 181332 2008-08-05 17:39:37Z jhb $"); 32169811Swollman 33169811Swollman/* 34270817Spluknet * Hardware driver for a Philips PCF8584 I2C bus controller sitting 35270817Spluknet * on a generic ISA bus. 36270817Spluknet */ 37270817Spluknet 38270817Spluknet#include <sys/param.h> 39270817Spluknet#include <sys/bus.h> 40270817Spluknet#include <sys/lock.h> 41270817Spluknet#include <sys/kernel.h> 42270817Spluknet#include <sys/module.h> 432742Swollman#include <sys/mutex.h> 44270817Spluknet#include <sys/resource.h> 45270817Spluknet#include <sys/systm.h> 46169811Swollman 47325322Sgordon#include <machine/bus.h> 48325322Sgordon#include <machine/resource.h> 49169811Swollman 509908Swollman#include <sys/rman.h> 5120094Swollman 52149514Swollman#include <isa/isareg.h> 5320094Swollman#include <isa/isavar.h> 5420094Swollman 5520094Swollman#include <dev/iicbus/iiconf.h> 5620094Swollman#include <dev/pcf/pcfvar.h> 5720094Swollman#include "iicbus_if.h" 5820094Swollman 5920094Swollman#define PCF_NAME "pcf" 6020094Swollman 6120094Swollmanstatic void pcf_isa_identify(driver_t *, device_t); 6220094Swollmanstatic int pcf_isa_probe(device_t); 63309577Sglebiusstatic int pcf_isa_attach(device_t); 64309577Sglebiusstatic int pcf_isa_detach(device_t); 65309577Sglebius 66309577Sglebiusstatic device_method_t pcf_isa_methods[] = { 67309577Sglebius /* device interface */ 68309577Sglebius DEVMETHOD(device_identify, pcf_isa_identify), 69309577Sglebius DEVMETHOD(device_probe, pcf_isa_probe), 70309577Sglebius DEVMETHOD(device_attach, pcf_isa_attach), 7120094Swollman DEVMETHOD(device_detach, pcf_isa_detach), 72270817Spluknet 73270817Spluknet /* iicbus interface */ 74270817Spluknet DEVMETHOD(iicbus_callback, iicbus_null_callback), 75270817Spluknet DEVMETHOD(iicbus_repeated_start, pcf_repeated_start), 76270817Spluknet DEVMETHOD(iicbus_start, pcf_start), 77270817Spluknet DEVMETHOD(iicbus_stop, pcf_stop), 782742Swollman DEVMETHOD(iicbus_write, pcf_write), 79270817Spluknet DEVMETHOD(iicbus_read, pcf_read), 80270817Spluknet DEVMETHOD(iicbus_reset, pcf_rst_card), 8120094Swollman { 0, 0 } 82270817Spluknet}; 83270817Spluknet 84270817Spluknetstatic devclass_t pcf_isa_devclass; 852742Swollman 869908Swollmanstatic driver_t pcf_isa_driver = { 872742Swollman PCF_NAME, 88270817Spluknet pcf_isa_methods, 89270817Spluknet sizeof(struct pcf_softc), 90270817Spluknet}; 91270817Spluknet 92270817Spluknetstatic void 93270817Spluknetpcf_isa_identify(driver_t *driver, device_t parent) 94270817Spluknet{ 95270817Spluknet BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, PCF_NAME, 0); 96270817Spluknet 97270817Spluknet return; 98270817Spluknet} 99270817Spluknet 100270817Spluknetstatic int 101270817Spluknetpcf_isa_probe(device_t dev) 102270817Spluknet{ 1032742Swollman u_long start, count; 1042742Swollman u_int rid = 0, port, error; 105270817Spluknet 106270817Spluknet /* skip PnP probes */ 107270817Spluknet if (isa_get_logicalid(dev)) 1082742Swollman return (ENXIO); 1099908Swollman 110149514Swollman /* The port address must be explicitly specified */ 111149514Swollman bus_get_resource(dev, SYS_RES_IOPORT, rid, &start, &count); 112273719Sedwin if ((error = resource_int_value(PCF_NAME, 0, "port", &port) != 0)) 113149514Swollman return (error); 114149514Swollman 1152742Swollman /* Probe is only successfull for the specified base io */ 116270817Spluknet if (port != (u_int)start) 117270817Spluknet return (ENXIO); 118270817Spluknet 119270817Spluknet device_set_desc(dev, "PCF8584 I2C bus controller"); 120270817Spluknet 121270817Spluknet return (0); 122270817Spluknet} 123270817Spluknet 124270817Spluknetstatic int 125270817Spluknetpcf_isa_attach(device_t dev) 126270817Spluknet{ 127270817Spluknet struct pcf_softc *sc; 128270817Spluknet int rv = ENXIO; 129270817Spluknet 130270817Spluknet sc = DEVTOSOFTC(dev); 131270817Spluknet mtx_init(&sc->pcf_lock, device_get_nameunit(dev), "pcf", MTX_DEF); 132270817Spluknet 133270817Spluknet /* IO port is mandatory */ 134270817Spluknet sc->res_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 1352742Swollman &sc->rid_ioport, RF_ACTIVE); 1362742Swollman if (sc->res_ioport == 0) { 137273719Sedwin device_printf(dev, "cannot reserve I/O port range\n"); 138270817Spluknet goto error; 139270817Spluknet } 140270817Spluknet 141149514Swollman sc->pcf_flags = device_get_flags(dev); 142273719Sedwin 143270817Spluknet if (!(sc->pcf_flags & IIC_POLLED)) { 144270817Spluknet sc->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->rid_irq, 145270817Spluknet RF_ACTIVE); 1462742Swollman if (sc->res_irq == 0) { 1479908Swollman device_printf(dev, "can't reserve irq, polled mode.\n"); 1482742Swollman sc->pcf_flags |= IIC_POLLED; 149270817Spluknet } 150270817Spluknet } 151270817Spluknet 152270817Spluknet /* reset the chip */ 153270817Spluknet pcf_rst_card(dev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL); 154270817Spluknet 155270817Spluknet if (sc->res_irq) { 156270817Spluknet rv = bus_setup_intr(dev, sc->res_irq, 157270817Spluknet INTR_TYPE_NET /* | INTR_ENTROPY */, 158270817Spluknet NULL, pcf_intr, sc, &sc->intr_cookie); 159270817Spluknet if (rv) { 160270817Spluknet device_printf(dev, "could not setup IRQ\n"); 161270817Spluknet goto error; 162270817Spluknet } 1632742Swollman } 1642742Swollman 165270817Spluknet if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL) 166270817Spluknet device_printf(dev, "could not allocate iicbus instance\n"); 1672742Swollman 1689908Swollman /* probe and attach the iicbus */ 1692742Swollman bus_generic_attach(dev); 170270817Spluknet 171270817Spluknet return (0); 172270817Spluknet 173270817Splukneterror: 174270817Spluknet if (sc->res_irq != 0) { 175270817Spluknet bus_release_resource(dev, SYS_RES_IRQ, sc->rid_irq, 176270817Spluknet sc->res_irq); 177270817Spluknet } 178270817Spluknet if (sc->res_ioport != 0) { 179270817Spluknet bus_release_resource(dev, SYS_RES_IOPORT, sc->rid_ioport, 180270817Spluknet sc->res_ioport); 181270817Spluknet } 182270817Spluknet mtx_destroy(&sc->pcf_lock); 183270817Spluknet return (rv); 184270817Spluknet} 185270817Spluknet 1862742Swollmanstatic int 1872742Swollmanpcf_isa_detach(device_t dev) 188270817Spluknet{ 189270817Spluknet struct pcf_softc *sc; 1902742Swollman int rv; 191270817Spluknet 192270817Spluknet sc = DEVTOSOFTC(dev); 193270817Spluknet 194270817Spluknet if ((rv = bus_generic_detach(dev)) != 0) 195270817Spluknet return (rv); 1968029Swollman 19714343Swollman if ((rv = device_delete_child(dev, sc->iicbus)) != 0) 19814343Swollman return (rv); 199270817Spluknet 200270817Spluknet if (sc->res_irq != 0) { 201270817Spluknet bus_teardown_intr(dev, sc->res_irq, sc->intr_cookie); 202270817Spluknet bus_release_resource(dev, SYS_RES_IRQ, sc->rid_irq, sc->res_irq); 203270817Spluknet } 204270817Spluknet 205270817Spluknet bus_release_resource(dev, SYS_RES_IOPORT, sc->rid_ioport, sc->res_ioport); 206270817Spluknet mtx_destroy(&sc->pcf_lock); 207270817Spluknet 208270817Spluknet return (0); 209270817Spluknet} 210270817Spluknet 211270817SpluknetDRIVER_MODULE(pcf_isa, isa, pcf_isa_driver, pcf_isa_devclass, 0, 0); 212270817Spluknet