1/*-
2 * Copyright (c) 2000 KIYOHARA Takashi <kiyohara@kk.iij4u.ne.jp>
3 * Copyright (c) 2000 Takanori Watanabe <takawata@jp.FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD$
28 */
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/bus.h>
33#include <sys/kernel.h>
34#include <sys/malloc.h>
35#include <sys/module.h>
36#include <sys/sysctl.h>
37
38
39#include <machine/bus.h>
40#include <machine/resource.h>
41#include <sys/rman.h>
42
43#include <pc98/pc98/canbus.h>
44#include <pc98/pc98/canbusvars.h>
45#include "canbus_if.h"
46
47
48#define CANBE_IO_DELAY_TIME 5000
49
50
51static MALLOC_DEFINE(M_CANBUSDEV, "canbusdev", "CanBe device");
52struct canbus_device {
53	struct resource_list cbdev_resources;
54};
55
56/* canbus softc */
57struct canbus_softc {
58	int io_delay_time;			/* CanBe I/O delay time */
59
60	struct sysctl_ctx_list canbus_sysctl_ctx;
61						/* dynamic sysctl tree */
62
63	/* index register */
64	int index_id;				/* index ID */
65	struct resource *index_res;		/* index resource */
66	bus_space_tag_t index_tag;		/* index tag */
67	bus_space_handle_t index_handle;	/* index handle */
68
69	/* data register */
70	int data_id;				/* data ID */
71	struct resource *data_res;		/* data resource */
72	bus_space_tag_t data_tag;		/* data tag */
73	bus_space_handle_t data_handle;		/* data handle */
74};
75
76
77/* Device interface methods */
78static void	canbus_identify(driver_t *, device_t);
79static int	canbus_probe(device_t);
80static int	canbus_attach(device_t);
81static int	canbus_detach(device_t);
82
83/* Bus interface methods */
84static int	canbus_print_child(device_t, device_t);
85static device_t	canbus_add_child(device_t, u_int, const char *, int);
86static struct resource *	canbus_alloc_resource(
87    device_t, device_t, int, int *, rman_res_t, rman_res_t, rman_res_t, u_int);
88static int	canbus_activate_resource(
89    device_t, device_t, int, int, struct resource *);
90static int	canbus_deactivate_resource(
91    device_t, device_t, int, int, struct resource *);
92static int	canbus_release_resource(
93    device_t, device_t, int, int, struct resource *);
94static int	canbus_set_resource (
95    device_t, device_t, int, int, rman_res_t, rman_res_t);
96static void	canbus_delete_resource(device_t, device_t, int, int);
97
98/* canbus local function */
99static void	set_ioresource(device_t dev);
100static void	delete_ioresource(device_t dev);
101static int	alloc_ioresource(device_t);
102static void	release_ioresource(device_t);
103static int	print_all_resources(device_t);
104
105static device_method_t canbus_methods[] = {
106	/* Device interface */
107	DEVMETHOD(device_identify,	canbus_identify),
108	DEVMETHOD(device_probe,		canbus_probe),
109	DEVMETHOD(device_attach,	canbus_attach),
110	DEVMETHOD(device_detach,	canbus_detach),
111
112	/* Bus interface */
113	DEVMETHOD(bus_print_child,	canbus_print_child),
114	DEVMETHOD(bus_add_child,	canbus_add_child),
115	DEVMETHOD(bus_alloc_resource,	canbus_alloc_resource),
116	DEVMETHOD(bus_activate_resource,	canbus_activate_resource),
117	DEVMETHOD(bus_deactivate_resource,	canbus_deactivate_resource),
118	DEVMETHOD(bus_release_resource,	canbus_release_resource),
119	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
120	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
121	DEVMETHOD(bus_set_resource,	canbus_set_resource),
122	DEVMETHOD(bus_delete_resource,	canbus_delete_resource),
123
124	/* CanBe interface */
125	DEVMETHOD(canbus_read,		canbus_read),
126	DEVMETHOD(canbus_write,		canbus_write),
127	DEVMETHOD(canbus_write_multi,	canbus_write_multi),
128
129	{0, 0}
130};
131
132static driver_t canbus_driver = {
133	"canbus",
134	canbus_methods,
135	sizeof(struct canbus_softc),
136};
137
138devclass_t canbus_devclass;
139DRIVER_MODULE(canbus, nexus, canbus_driver, canbus_devclass, 0, 0);
140MODULE_VERSION(canbus, 1);
141
142
143static void
144canbus_identify(driver_t *drv, device_t parent)
145{
146	if (device_find_child(parent, "canbus", 0) == NULL) {
147		if (BUS_ADD_CHILD(parent, 33, "canbus", 0) == NULL)
148			device_printf(parent, "canbus cannot attach\n");
149	}
150}
151
152
153static int
154canbus_probe(device_t dev)
155{
156	u_int8_t flag;
157
158	set_ioresource(dev);
159	if(alloc_ioresource(dev))
160		return (ENXIO);
161	flag = canbus_read(dev, NULL, CANBE_SOUND_INTR_ADDR);
162	release_ioresource(dev);
163
164	if (bootverbose)
165		device_printf(dev, "probe flag = 0x%x\n", flag);
166
167	if (flag != CANBE_SOUND_INTR_VAL0 && flag != CANBE_SOUND_INTR_VAL1 &&
168	    flag != CANBE_SOUND_INTR_VAL2 && flag != CANBE_SOUND_INTR_VAL3) {
169		device_printf(dev, "Device Not Found\n");
170		return (ENXIO);
171	}
172	device_set_desc(dev, "CanBe I/O Bus");
173
174	return (0);
175}
176
177static int
178canbus_attach(device_t dev)
179{
180	struct canbus_softc *sc = device_get_softc(dev);
181	struct sysctl_oid *canbus_sysctl_tree;
182
183	sc->io_delay_time = CANBE_IO_DELAY_TIME;
184
185	/* I/O resource setup */
186	if(alloc_ioresource(dev))
187		return (ENXIO);
188
189	/* Dynamic sysctl tree setup */
190	sysctl_ctx_init(&sc->canbus_sysctl_ctx);
191	canbus_sysctl_tree = SYSCTL_ADD_ROOT_NODE(&sc->canbus_sysctl_ctx,
192	    OID_AUTO, "canbus", CTLFLAG_RD, 0, "CanBe I/O Bus");
193	SYSCTL_ADD_INT(&sc->canbus_sysctl_ctx,
194	    SYSCTL_CHILDREN(canbus_sysctl_tree), OID_AUTO, "io_delay_time",
195	    CTLFLAG_RW, &sc->io_delay_time, 0, "CanBe Bus I/O delay time");
196
197	bus_generic_probe(dev);
198	bus_generic_attach(dev);
199
200	return (0);
201}
202
203
204static int
205canbus_detach(device_t dev)
206{
207	struct canbus_softc *sc = device_get_softc(dev);
208
209	/* I/O resource free */
210	release_ioresource(dev);
211	delete_ioresource(dev);
212
213	/* Dynamic sysctl tree destroy */
214	if (sysctl_ctx_free(&sc->canbus_sysctl_ctx)) {
215		device_printf(dev,
216		    "can't free this context - other oids depend on it\n");
217		return (ENOTEMPTY);
218	}
219
220	return (0);
221}
222
223
224static int
225canbus_print_child(device_t dev, device_t child)
226{
227	int     retval = 0;
228
229	retval += bus_print_child_header(dev, child);
230	retval += print_all_resources(child);
231	retval += bus_print_child_footer(dev, child);
232
233	return (retval);
234}
235
236static device_t
237canbus_add_child(device_t bus, u_int order, const char *name, int unit)
238{
239	device_t child;
240	struct canbus_device *cbdev;
241
242	child = device_add_child_ordered(bus, order, name, unit);
243
244	cbdev = malloc(
245	    sizeof(struct canbus_device), M_CANBUSDEV, M_NOWAIT | M_ZERO);
246	if (!cbdev)
247		return (0);
248
249	resource_list_init(&cbdev->cbdev_resources);
250	device_set_ivars(child, cbdev);
251
252	return (child);
253}
254
255static struct resource *
256canbus_alloc_resource(device_t dev, device_t child, int type,
257    int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
258{
259	return (BUS_ALLOC_RESOURCE(device_get_parent(dev),
260	    child, type, rid, start, end, count, flags));
261}
262
263static int
264canbus_activate_resource(
265    device_t dev, device_t child, int type, int rid, struct resource *res)
266{
267	return (BUS_ACTIVATE_RESOURCE(
268	    device_get_parent(dev), child, type, rid, res));
269}
270
271static int
272canbus_deactivate_resource(
273    device_t dev, device_t child, int type, int rid, struct resource *res)
274{
275	return (BUS_DEACTIVATE_RESOURCE(
276	    device_get_parent(dev), child, type, rid, res));
277}
278
279static int
280canbus_release_resource(
281    device_t dev, device_t child, int type, int rid, struct resource *res)
282{
283	return (BUS_RELEASE_RESOURCE(
284	    device_get_parent(dev), child, type, rid, res));
285}
286
287static int
288canbus_set_resource (
289    device_t dev, device_t child, int type, int rid, rman_res_t start,
290    rman_res_t count)
291{
292	struct  canbus_device *cbdev =
293	    (struct canbus_device *)device_get_ivars(child);
294	struct resource_list *rl = &cbdev->cbdev_resources;
295
296	resource_list_add(rl, type, rid, start, (start + count - 1), count);
297
298	return (0);
299}
300
301static void
302canbus_delete_resource(device_t dev, device_t child, int type, int rid)
303{
304        struct  canbus_device *cbdev =
305	    (struct canbus_device *)device_get_ivars(child);
306        struct resource_list *rl = &cbdev->cbdev_resources;
307
308        resource_list_delete(rl, type, rid);
309}
310
311
312u_int8_t
313canbus_read(device_t dev, device_t child, int reg)
314{
315	struct canbus_softc *sc = device_get_softc(dev);
316
317	bus_space_write_1(sc->index_tag, sc->index_handle, 0, reg);
318	return (bus_space_read_1(sc->data_tag, sc->data_handle, 0));
319}
320
321void
322canbus_write(device_t dev, device_t child, int reg, u_int8_t val)
323{
324	struct canbus_softc *sc = device_get_softc(dev);
325
326	bus_space_write_1(sc->index_tag, sc->index_handle, 0, reg);
327	bus_space_write_1(sc->data_tag, sc->data_handle, 0, val);
328}
329
330void
331canbus_write_multi(device_t dev,
332    device_t child, int reg, const int count, const u_int8_t *vals)
333{
334	struct canbus_softc *sc = device_get_softc(dev);
335	int i;
336
337	bus_space_write_1(sc->index_tag, sc->index_handle, 0, reg);
338
339	for (i = 0; i < count; i ++) {
340		bus_space_write_1(sc->data_tag, sc->data_handle, 0, vals[i]);
341		DELAY(sc->io_delay_time);
342	}
343}
344
345void
346canbus_delay(device_t dev, device_t child)
347{
348	struct canbus_softc *sc = device_get_softc(dev);
349
350	DELAY(sc->io_delay_time);
351}
352
353
354/*
355 * canbus local function.
356 */
357
358/*
359 * CanBe I/O resource set function
360 */
361static void
362set_ioresource(device_t dev)
363{
364	struct canbus_softc *sc = device_get_softc(dev);
365
366	sc->index_id = 0;
367	sc->data_id = 1;
368
369	bus_set_resource(
370	    dev, SYS_RES_IOPORT, sc->index_id, CANBE_IOPORT_INDEX, 1);
371	bus_set_resource(
372	    dev, SYS_RES_IOPORT, sc->data_id, CANBE_IOPORT_DATA, 1);
373}
374
375/*
376 * CanBe I/O resource delete function
377 */
378static void
379delete_ioresource(device_t dev)
380{
381	struct canbus_softc *sc = device_get_softc(dev);
382
383	bus_delete_resource(dev, SYS_RES_IOPORT, sc->index_id);
384	bus_delete_resource(dev, SYS_RES_IOPORT, sc->data_id);
385}
386
387/*
388 * CanBe I/O resource alloc function
389 */
390static int
391alloc_ioresource(device_t dev)
392{
393	struct canbus_softc *sc = device_get_softc(dev);
394
395	sc->index_res = bus_alloc_resource_any(
396	    dev, SYS_RES_IOPORT, &sc->index_id, RF_ACTIVE);
397	sc->data_res = bus_alloc_resource_any(
398	    dev, SYS_RES_IOPORT, &sc->data_id, RF_ACTIVE);
399	if (sc->index_res == NULL || sc->data_res == NULL) {
400		device_printf(dev, "could not map I/O\n");
401		return (ENXIO);
402	}
403
404	sc->index_tag = rman_get_bustag(sc->index_res);
405	sc->index_handle = rman_get_bushandle(sc->index_res);
406	sc->data_tag = rman_get_bustag(sc->data_res);
407	sc->data_handle = rman_get_bushandle(sc->data_res);
408
409	return (0);
410}
411
412/*
413 * CanBe I/O resource release function
414 */
415static void
416release_ioresource(device_t dev)
417{
418	struct canbus_softc *sc = device_get_softc(dev);
419
420	bus_release_resource(dev, SYS_RES_IOPORT, sc->index_id, sc->index_res);
421	bus_release_resource(dev, SYS_RES_IOPORT, sc->data_id, sc->data_res);
422}
423
424
425static int
426print_all_resources(device_t dev)
427{
428	struct  canbus_device *cbdev =
429	    (struct canbus_device *)device_get_ivars(dev);
430	struct resource_list *rl = &cbdev->cbdev_resources;
431	int retval = 0;
432
433	if (STAILQ_FIRST(rl))
434		retval += printf(" at");
435
436	retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#jx");
437	retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#jx");
438	retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd");
439
440	return retval;
441}
442