xref: /freebsd/sys/dev/pcf/pcf_isa.c (revision 7899f917b1c0ea178f1d2be0cfb452086d079d23)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2004 Joerg Wunsch
5  *
6  * derived from sys/i386/isa/pcf.c which is:
7  *
8  * Copyright (c) 1998 Nicolas Souchu, Marc Bouget
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 #include <sys/cdefs.h>
33 /*
34  * Hardware driver for a Philips PCF8584 I2C bus controller sitting
35  * on a generic ISA bus.
36  */
37 
38 #include <sys/param.h>
39 #include <sys/bus.h>
40 #include <sys/lock.h>
41 #include <sys/kernel.h>
42 #include <sys/module.h>
43 #include <sys/mutex.h>
44 #include <sys/resource.h>
45 #include <sys/systm.h>
46 
47 #include <machine/bus.h>
48 #include <machine/resource.h>
49 
50 #include <sys/rman.h>
51 
52 #include <isa/isareg.h>
53 #include <isa/isavar.h>
54 
55 #include <dev/iicbus/iiconf.h>
56 #include <dev/pcf/pcfvar.h>
57 #include "iicbus_if.h"
58 
59 #define	PCF_NAME	"pcf"
60 
61 static void pcf_isa_identify(driver_t *, device_t);
62 static int pcf_isa_probe(device_t);
63 static int pcf_isa_attach(device_t);
64 static int pcf_isa_detach(device_t);
65 
66 static device_method_t pcf_isa_methods[] = {
67 	/* device interface */
68 	DEVMETHOD(device_identify,	pcf_isa_identify),
69 	DEVMETHOD(device_probe,		pcf_isa_probe),
70 	DEVMETHOD(device_attach,	pcf_isa_attach),
71 	DEVMETHOD(device_detach,	pcf_isa_detach),
72 
73 	/* iicbus interface */
74 	DEVMETHOD(iicbus_callback,	iicbus_null_callback),
75 	DEVMETHOD(iicbus_repeated_start, pcf_repeated_start),
76 	DEVMETHOD(iicbus_start,		pcf_start),
77 	DEVMETHOD(iicbus_stop,		pcf_stop),
78 	DEVMETHOD(iicbus_write,		pcf_write),
79 	DEVMETHOD(iicbus_read,		pcf_read),
80 	DEVMETHOD(iicbus_reset,		pcf_rst_card),
81 	{ 0, 0 }
82 };
83 
84 static driver_t pcf_isa_driver = {
85 	PCF_NAME,
86 	pcf_isa_methods,
87 	sizeof(struct pcf_softc),
88 };
89 
90 static void
91 pcf_isa_identify(driver_t *driver, device_t parent)
92 {
93 	BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, PCF_NAME, 0);
94 
95 	return;
96 }
97 
98 static int
99 pcf_isa_probe(device_t dev)
100 {
101 	rman_res_t	start, count;
102 	u_int		rid = 0, port, error;
103 
104 	/* skip PnP probes */
105 	if (isa_get_logicalid(dev))
106 		return (ENXIO);
107 
108 	/* The port address must be explicitly specified */
109 	bus_get_resource(dev, SYS_RES_IOPORT, rid, &start, &count);
110 	if ((error = resource_int_value(PCF_NAME, 0, "port", &port)) != 0)
111 		return (error);
112 
113 	/* Probe is only successful for the specified base io */
114 	if (port != (u_int)start)
115 		return (ENXIO);
116 
117 	device_set_desc(dev, "PCF8584 I2C bus controller");
118 
119 	return (0);
120 }
121 
122 static int
123 pcf_isa_attach(device_t dev)
124 {
125 	struct pcf_softc *sc;
126 	int rv = ENXIO;
127 
128 	sc = DEVTOSOFTC(dev);
129 	mtx_init(&sc->pcf_lock, device_get_nameunit(dev), "pcf", MTX_DEF);
130 
131 	/* IO port is mandatory */
132 	sc->res_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
133 						&sc->rid_ioport, RF_ACTIVE);
134 	if (sc->res_ioport == 0) {
135 		device_printf(dev, "cannot reserve I/O port range\n");
136 		goto error;
137 	}
138 
139 	sc->pcf_flags = device_get_flags(dev);
140 
141 	if (!(sc->pcf_flags & IIC_POLLED)) {
142 		sc->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->rid_irq,
143 						     RF_ACTIVE);
144 		if (sc->res_irq == 0) {
145 			device_printf(dev, "can't reserve irq, polled mode.\n");
146 			sc->pcf_flags |= IIC_POLLED;
147 		}
148 	}
149 
150 	/* reset the chip */
151 	pcf_rst_card(dev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL);
152 
153 	if (sc->res_irq) {
154 		rv = bus_setup_intr(dev, sc->res_irq,
155 				    INTR_TYPE_NET /* | INTR_ENTROPY */,
156 				    NULL, pcf_intr, sc, &sc->intr_cookie);
157 		if (rv) {
158 			device_printf(dev, "could not setup IRQ\n");
159 			goto error;
160 		}
161 	}
162 
163 	if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL)
164 		device_printf(dev, "could not allocate iicbus instance\n");
165 
166 	/* probe and attach the iicbus */
167 	bus_generic_attach(dev);
168 
169 	return (0);
170 
171 error:
172 	if (sc->res_irq != 0) {
173 		bus_release_resource(dev, SYS_RES_IRQ, sc->rid_irq,
174 				     sc->res_irq);
175 	}
176 	if (sc->res_ioport != 0) {
177 		bus_release_resource(dev, SYS_RES_IOPORT, sc->rid_ioport,
178 				     sc->res_ioport);
179 	}
180 	mtx_destroy(&sc->pcf_lock);
181 	return (rv);
182 }
183 
184 static int
185 pcf_isa_detach(device_t dev)
186 {
187 	struct pcf_softc *sc;
188 	int rv;
189 
190 	sc = DEVTOSOFTC(dev);
191 
192 	if ((rv = bus_generic_detach(dev)) != 0)
193 		return (rv);
194 
195 	if ((rv = device_delete_child(dev, sc->iicbus)) != 0)
196 		return (rv);
197 
198 	if (sc->res_irq != 0) {
199 		bus_teardown_intr(dev, sc->res_irq, sc->intr_cookie);
200 		bus_release_resource(dev, SYS_RES_IRQ, sc->rid_irq, sc->res_irq);
201 	}
202 
203 	bus_release_resource(dev, SYS_RES_IOPORT, sc->rid_ioport, sc->res_ioport);
204 	mtx_destroy(&sc->pcf_lock);
205 
206 	return (0);
207 }
208 
209 DRIVER_MODULE(pcf_isa, isa, pcf_isa_driver, 0, 0);
210