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