]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/pcf/pcf_isa.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / pcf / pcf_isa.c
1 /*-
2  * Copyright (c) 2004 Joerg Wunsch
3  *
4  * derived from sys/i386/isa/pcf.c which is:
5  *
6  * Copyright (c) 1998 Nicolas Souchu, Marc Bouget
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
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 devclass_t pcf_isa_devclass;
85
86 static driver_t pcf_isa_driver = {
87         PCF_NAME,
88         pcf_isa_methods,
89         sizeof(struct pcf_softc),
90 };
91
92 static void
93 pcf_isa_identify(driver_t *driver, device_t parent)
94 {
95         BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, PCF_NAME, 0);
96
97         return;
98 }
99
100 static int
101 pcf_isa_probe(device_t dev)
102 {
103         u_long          start, count;
104         u_int           rid = 0, port, error;
105
106         /* skip PnP probes */
107         if (isa_get_logicalid(dev))
108                 return (ENXIO);
109
110         /* The port address must be explicitly specified */
111         bus_get_resource(dev, SYS_RES_IOPORT, rid, &start, &count);
112         if ((error = resource_int_value(PCF_NAME, 0, "port", &port) != 0))
113                 return (error);
114
115         /* Probe is only successfull for the specified base io */
116         if (port != (u_int)start)
117                 return (ENXIO);
118
119         device_set_desc(dev, "PCF8584 I2C bus controller");
120
121         return (0);
122 }
123
124 static int
125 pcf_isa_attach(device_t dev)
126 {
127         struct pcf_softc *sc;
128         int rv = ENXIO;
129
130         sc = DEVTOSOFTC(dev);
131         mtx_init(&sc->pcf_lock, device_get_nameunit(dev), "pcf", MTX_DEF);
132
133         /* IO port is mandatory */
134         sc->res_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
135                                                 &sc->rid_ioport, RF_ACTIVE);
136         if (sc->res_ioport == 0) {
137                 device_printf(dev, "cannot reserve I/O port range\n");
138                 goto error;
139         }
140
141         sc->pcf_flags = device_get_flags(dev);
142
143         if (!(sc->pcf_flags & IIC_POLLED)) {
144                 sc->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->rid_irq,
145                                                      RF_ACTIVE);
146                 if (sc->res_irq == 0) {
147                         device_printf(dev, "can't reserve irq, polled mode.\n");
148                         sc->pcf_flags |= IIC_POLLED;
149                 }
150         }
151
152         /* reset the chip */
153         pcf_rst_card(dev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL);
154
155         if (sc->res_irq) {
156                 rv = bus_setup_intr(dev, sc->res_irq,
157                                     INTR_TYPE_NET /* | INTR_ENTROPY */,
158                                     NULL, pcf_intr, sc, &sc->intr_cookie);
159                 if (rv) {
160                         device_printf(dev, "could not setup IRQ\n");
161                         goto error;
162                 }
163         }
164
165         if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL)
166                 device_printf(dev, "could not allocate iicbus instance\n");
167
168         /* probe and attach the iicbus */
169         bus_generic_attach(dev);
170
171         return (0);
172
173 error:
174         if (sc->res_irq != 0) {
175                 bus_release_resource(dev, SYS_RES_IRQ, sc->rid_irq,
176                                      sc->res_irq);
177         }
178         if (sc->res_ioport != 0) {
179                 bus_release_resource(dev, SYS_RES_IOPORT, sc->rid_ioport,
180                                      sc->res_ioport);
181         }
182         mtx_destroy(&sc->pcf_lock);
183         return (rv);
184 }
185
186 static int
187 pcf_isa_detach(device_t dev)
188 {
189         struct pcf_softc *sc;
190         int rv;
191
192         sc = DEVTOSOFTC(dev);
193
194         if ((rv = bus_generic_detach(dev)) != 0)
195                 return (rv);
196
197         if ((rv = device_delete_child(dev, sc->iicbus)) != 0)
198                 return (rv);
199
200         if (sc->res_irq != 0) {
201                 bus_teardown_intr(dev, sc->res_irq, sc->intr_cookie);
202                 bus_release_resource(dev, SYS_RES_IRQ, sc->rid_irq, sc->res_irq);
203         }
204
205         bus_release_resource(dev, SYS_RES_IOPORT, sc->rid_ioport, sc->res_ioport);
206         mtx_destroy(&sc->pcf_lock);
207
208         return (0);
209 }
210
211 DRIVER_MODULE(pcf_isa, isa, pcf_isa_driver, pcf_isa_devclass, 0, 0);