1129704Sjoerg/*-
2129704Sjoerg * Copyright (c) 2004 Joerg Wunsch
3129704Sjoerg *
4129704Sjoerg * derived from sys/i386/isa/pcf.c which is:
5129704Sjoerg *
6129704Sjoerg * Copyright (c) 1998 Nicolas Souchu, Marc Bouget
7129704Sjoerg * All rights reserved.
8129704Sjoerg *
9129704Sjoerg * Redistribution and use in source and binary forms, with or without
10129704Sjoerg * modification, are permitted provided that the following conditions
11129704Sjoerg * are met:
12129704Sjoerg * 1. Redistributions of source code must retain the above copyright
13129704Sjoerg *    notice, this list of conditions and the following disclaimer.
14129704Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
15129704Sjoerg *    notice, this list of conditions and the following disclaimer in the
16129704Sjoerg *    documentation and/or other materials provided with the distribution.
17129704Sjoerg *
18129704Sjoerg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19129704Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20129704Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21129704Sjoerg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22129704Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23129704Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24129704Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25129704Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26129704Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27129704Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28129704Sjoerg * SUCH DAMAGE.
29129704Sjoerg */
30129704Sjoerg#include <sys/cdefs.h>
31129704Sjoerg__FBSDID("$FreeBSD$");
32129704Sjoerg
33129704Sjoerg/*
34129704Sjoerg * Device specific driver for the SUNW,envctrl device found on some
35129704Sjoerg * UltraSPARC Sun systems.  This device is a Philips PCF8584 sitting
36129704Sjoerg * on the Ebus2.
37129704Sjoerg */
38129704Sjoerg
39129704Sjoerg#include <sys/param.h>
40129704Sjoerg#include <sys/bus.h>
41129704Sjoerg#include <sys/conf.h>
42129704Sjoerg#include <sys/kernel.h>
43181303Sjhb#include <sys/lock.h>
44129704Sjoerg#include <sys/malloc.h>
45130319Smarius#include <sys/module.h>
46181303Sjhb#include <sys/mutex.h>
47129704Sjoerg#include <sys/resource.h>
48181303Sjhb#include <sys/systm.h>
49129704Sjoerg#include <sys/uio.h>
50129704Sjoerg
51133589Smarius#include <dev/ofw/ofw_bus.h>
52133589Smarius
53129704Sjoerg#include <machine/bus.h>
54129704Sjoerg#include <machine/resource.h>
55129704Sjoerg
56129704Sjoerg#include <sys/rman.h>
57129704Sjoerg
58181332Sjhb#include <dev/iicbus/iicbus.h>
59129704Sjoerg#include <dev/iicbus/iiconf.h>
60129704Sjoerg#include <dev/pcf/pcfvar.h>
61129704Sjoerg#include "iicbus_if.h"
62129704Sjoerg
63129704Sjoerg#undef PCF_DEFAULT_ADDR
64129704Sjoerg#define PCF_DEFAULT_ADDR	0x55 /* SUNW,pcf default */
65129704Sjoerg
66129704Sjoergstatic int envctrl_probe(device_t);
67129704Sjoergstatic int envctrl_attach(device_t);
68129704Sjoergstatic int envctrl_detach(device_t);
69129704Sjoerg
70129704Sjoergstatic device_method_t envctrl_methods[] = {
71129704Sjoerg	/* device interface */
72129704Sjoerg	DEVMETHOD(device_probe,		envctrl_probe),
73129704Sjoerg	DEVMETHOD(device_attach,	envctrl_attach),
74129704Sjoerg	DEVMETHOD(device_detach,	envctrl_detach),
75129704Sjoerg
76129704Sjoerg	/* iicbus interface */
77129704Sjoerg	DEVMETHOD(iicbus_callback,	iicbus_null_callback),
78129704Sjoerg	DEVMETHOD(iicbus_repeated_start, pcf_repeated_start),
79129704Sjoerg	DEVMETHOD(iicbus_start,		pcf_start),
80129704Sjoerg	DEVMETHOD(iicbus_stop,		pcf_stop),
81129704Sjoerg	DEVMETHOD(iicbus_write,		pcf_write),
82129704Sjoerg	DEVMETHOD(iicbus_read,		pcf_read),
83129704Sjoerg	DEVMETHOD(iicbus_reset,		pcf_rst_card),
84129704Sjoerg	{ 0, 0 }
85129704Sjoerg};
86129704Sjoerg
87129704Sjoergstatic devclass_t envctrl_devclass;
88129704Sjoerg
89129704Sjoergstatic driver_t envctrl_driver = {
90129704Sjoerg	"envctrl",
91129704Sjoerg	envctrl_methods,
92129704Sjoerg	sizeof(struct pcf_softc),
93129704Sjoerg};
94129704Sjoerg
95129704Sjoergstatic int
96129704Sjoergenvctrl_probe(device_t dev)
97129704Sjoerg{
98129704Sjoerg
99133589Smarius	if (strcmp("SUNW,envctrl", ofw_bus_get_name(dev)) == 0) {
100129704Sjoerg		device_set_desc(dev, "EBus SUNW,envctrl");
101129704Sjoerg		return (0);
102129704Sjoerg	}
103129704Sjoerg	return (ENXIO);
104129704Sjoerg}
105129704Sjoerg
106129704Sjoergstatic int
107129704Sjoergenvctrl_attach(device_t dev)
108129704Sjoerg{
109129704Sjoerg	struct pcf_softc *sc;
110129704Sjoerg	int rv = ENXIO;
111129704Sjoerg
112129704Sjoerg	sc = DEVTOSOFTC(dev);
113181303Sjhb	mtx_init(&sc->pcf_lock, device_get_nameunit(dev), "pcf", MTX_DEF);
114129704Sjoerg
115129704Sjoerg	/* IO port is mandatory */
116146966Smarius	sc->res_ioport = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
117129704Sjoerg						&sc->rid_ioport, RF_ACTIVE);
118129704Sjoerg	if (sc->res_ioport == 0) {
119129704Sjoerg		device_printf(dev, "cannot reserve I/O port range\n");
120129704Sjoerg		goto error;
121129704Sjoerg	}
122129704Sjoerg
123129704Sjoerg	sc->pcf_flags = device_get_flags(dev);
124129704Sjoerg
125129704Sjoerg	if (!(sc->pcf_flags & IIC_POLLED)) {
126129704Sjoerg		sc->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->rid_irq,
127129704Sjoerg						     RF_ACTIVE);
128129704Sjoerg		if (sc->res_irq == 0) {
129129704Sjoerg			device_printf(dev, "can't reserve irq, polled mode.\n");
130129704Sjoerg			sc->pcf_flags |= IIC_POLLED;
131129704Sjoerg		}
132129704Sjoerg	}
133129704Sjoerg
134129704Sjoerg	/* reset the chip */
135129704Sjoerg	pcf_rst_card(dev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL);
136129704Sjoerg
137155921Sjhb	rv = bus_setup_intr(dev, sc->res_irq,
138181303Sjhb			    INTR_TYPE_NET | INTR_MPSAFE /* | INTR_ENTROPY */,
139166901Spiso			    NULL, pcf_intr, sc, &sc->intr_cookie);
140129704Sjoerg	if (rv) {
141129704Sjoerg		device_printf(dev, "could not setup IRQ\n");
142129704Sjoerg		goto error;
143129704Sjoerg	}
144129704Sjoerg
145129704Sjoerg	if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL)
146129704Sjoerg		device_printf(dev, "could not allocate iicbus instance\n");
147129704Sjoerg
148129704Sjoerg	/* probe and attach the iicbus */
149129704Sjoerg	bus_generic_attach(dev);
150129704Sjoerg
151129704Sjoerg	return (0);
152129704Sjoerg
153129704Sjoergerror:
154129704Sjoerg	if (sc->res_irq != 0) {
155129704Sjoerg		bus_release_resource(dev, SYS_RES_IRQ, sc->rid_irq,
156129704Sjoerg				     sc->res_irq);
157129704Sjoerg	}
158129704Sjoerg	if (sc->res_ioport != 0) {
159146966Smarius		bus_release_resource(dev, SYS_RES_MEMORY, sc->rid_ioport,
160129704Sjoerg				     sc->res_ioport);
161129704Sjoerg	}
162181303Sjhb	mtx_destroy(&sc->pcf_lock);
163129704Sjoerg	return (rv);
164129704Sjoerg}
165129704Sjoerg
166129704Sjoergstatic int
167129704Sjoergenvctrl_detach(device_t dev)
168129704Sjoerg{
169129704Sjoerg	struct pcf_softc *sc;
170129704Sjoerg	int rv;
171129704Sjoerg
172129704Sjoerg	sc = DEVTOSOFTC(dev);
173129704Sjoerg
174129704Sjoerg	if ((rv = bus_generic_detach(dev)) != 0)
175129704Sjoerg		return (rv);
176129704Sjoerg
177129704Sjoerg	if ((rv = device_delete_child(dev, sc->iicbus)) != 0)
178129704Sjoerg		return (rv);
179129704Sjoerg
180129704Sjoerg	if (sc->res_irq != 0) {
181155921Sjhb		bus_teardown_intr(dev, sc->res_irq, sc->intr_cookie);
182129704Sjoerg		bus_release_resource(dev, SYS_RES_IRQ, sc->rid_irq, sc->res_irq);
183129704Sjoerg	}
184129704Sjoerg
185146966Smarius	bus_release_resource(dev, SYS_RES_MEMORY, sc->rid_ioport, sc->res_ioport);
186181303Sjhb	mtx_destroy(&sc->pcf_lock);
187129704Sjoerg
188129704Sjoerg	return (0);
189129704Sjoerg}
190129704Sjoerg
191130319SmariusDRIVER_MODULE(envctrl, ebus, envctrl_driver, envctrl_devclass, 0, 0);
192181303SjhbDRIVER_MODULE(iicbus, envctrl, iicbus_driver, iicbus_devclass, 0, 0);
193