canbus.c revision 127135
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: head/sys/pc98/pc98/canbus.c 127135 2004-03-17 17:50:55Z njl $
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#include <machine/clock.h>
39
40#include <machine/bus.h>
41#include <machine/resource.h>
42#include <sys/rman.h>
43
44#include <pc98/pc98/canbus.h>
45#include <pc98/pc98/canbusvars.h>
46#include "canbus_if.h"
47
48
49#define CANBE_IO_DELAY_TIME 5000
50
51
52static MALLOC_DEFINE(M_CANBUSDEV, "canbusdev", "CanBe device");
53struct canbus_device {
54	struct resource_list cbdev_resources;
55};
56
57/* canbus softc */
58struct canbus_softc {
59	int io_delay_time;			/* CanBe I/O delay time */
60
61	struct sysctl_ctx_list canbus_sysctl_ctx;
62						/* dynamic sysctl tree */
63
64	/* index register */
65	int index_id;				/* index ID */
66	struct resource *index_res;		/* index resouce */
67	bus_space_tag_t index_tag;		/* index tag */
68	bus_space_handle_t index_handle;	/* index handle */
69
70	/* data register */
71	int data_id;				/* data ID */
72	struct resource *data_res;		/* data resouce */
73	bus_space_tag_t data_tag;		/* data tag */
74	bus_space_handle_t data_handle;		/* data handle */
75};
76
77
78/* Device interface methods */
79static void	canbus_identify(driver_t *, device_t);
80static int	canbus_probe(device_t);
81static int	canbus_attach(device_t);
82static int	canbus_detach(device_t);
83
84/* Bus interface methods */
85static int	canbus_print_child(device_t, device_t);
86static device_t	canbus_add_child(device_t, int, const char *, int);
87static struct resource *	canbus_alloc_resource(
88    device_t, device_t, int, int *, u_long, u_long, u_long, u_int);
89static int	canbus_activate_resource(
90    device_t, device_t, int, int, struct resource *);
91static int	canbus_deactivate_resource(
92    device_t, device_t, int, int, struct resource *);
93static int	canbus_release_resource(
94    device_t, device_t, int, int, struct resource *);
95static int	canbus_set_resource (
96    device_t, device_t, int, int, u_long, u_long);
97static void	canbus_delete_resource(device_t, device_t, int, int);
98
99/* canbus local function */
100static void	set_ioresource(device_t dev);
101static void	delete_ioresource(device_t dev);
102static int	alloc_ioresource(device_t);
103static void	release_ioresource(device_t);
104static int	print_all_resources(device_t);
105
106static device_method_t canbus_methods[] = {
107	/* Device interface */
108	DEVMETHOD(device_identify,	canbus_identify),
109	DEVMETHOD(device_probe,		canbus_probe),
110	DEVMETHOD(device_attach,	canbus_attach),
111	DEVMETHOD(device_detach,	canbus_detach),
112
113	/* Bus interface */
114	DEVMETHOD(bus_print_child,	canbus_print_child),
115	DEVMETHOD(bus_add_child,	canbus_add_child),
116	DEVMETHOD(bus_alloc_resource,	canbus_alloc_resource),
117	DEVMETHOD(bus_activate_resource,	canbus_activate_resource),
118	DEVMETHOD(bus_deactivate_resource,	canbus_deactivate_resource),
119	DEVMETHOD(bus_release_resource,	canbus_release_resource),
120	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
121	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
122	DEVMETHOD(bus_set_resource,	canbus_set_resource),
123	DEVMETHOD(bus_delete_resource,	canbus_delete_resource),
124
125	/* CanBe interface */
126	DEVMETHOD(canbus_read,		canbus_read),
127	DEVMETHOD(canbus_write,		canbus_write),
128	DEVMETHOD(canbus_write_multi,	canbus_write_multi),
129
130	{0, 0}
131};
132
133static driver_t canbus_driver = {
134	"canbus",
135	canbus_methods,
136	sizeof(struct canbus_softc),
137};
138
139devclass_t canbus_devclass;
140DRIVER_MODULE(canbus, nexus, canbus_driver, canbus_devclass, 0, 0);
141MODULE_VERSION(canbus, 1);
142
143
144static void
145canbus_identify(driver_t *drv, device_t parent)
146{
147	if (device_find_child(parent, "canbus", 0) == NULL) {
148		if (BUS_ADD_CHILD(parent, 33, "canbus", 0) == NULL)
149			device_printf(parent, "canbus cannot attach\n");
150	}
151}
152
153
154static int
155canbus_probe(device_t dev)
156{
157	u_int8_t flag;
158
159	set_ioresource(dev);
160	if(alloc_ioresource(dev))
161		return (ENXIO);
162	flag = canbus_read(dev, NULL, CANBE_SOUND_INTR_ADDR);
163	release_ioresource(dev);
164
165	if (bootverbose)
166		device_printf(dev, "probe flag = 0x%x\n", flag);
167
168	if (flag != CANBE_SOUND_INTR_VAL0 && flag != CANBE_SOUND_INTR_VAL1 &&
169	    flag != CANBE_SOUND_INTR_VAL2 && flag != CANBE_SOUND_INTR_VAL3) {
170		device_printf(dev, "Device Not Found\n");
171		return (ENXIO);
172	}
173	device_set_desc(dev, "CanBe I/O Bus");
174
175	return (0);
176}
177
178static int
179canbus_attach(device_t dev)
180{
181	struct canbus_softc *sc = device_get_softc(dev);
182	struct sysctl_oid *canbus_sysctl_tree;
183
184	sc->io_delay_time = CANBE_IO_DELAY_TIME;
185
186	/* I/O resource setup */
187	if(alloc_ioresource(dev))
188		return (ENXIO);
189
190	/* Dynamic sysctl tree setup */
191	sysctl_ctx_init(&sc->canbus_sysctl_ctx);
192	canbus_sysctl_tree = SYSCTL_ADD_NODE(&sc->canbus_sysctl_ctx,
193	    SYSCTL_STATIC_CHILDREN(/* tree top */), OID_AUTO,
194	    "canbus", CTLFLAG_RD, 0, "CanBe I/O Bus");
195	SYSCTL_ADD_INT(&sc->canbus_sysctl_ctx,
196	    SYSCTL_CHILDREN(canbus_sysctl_tree), OID_AUTO, "io_delay_time",
197	    CTLFLAG_RW, &sc->io_delay_time, 0, "CanBe Bus I/O delay time");
198
199	bus_generic_probe(dev);
200	bus_generic_attach(dev);
201
202	return (0);
203}
204
205
206static int
207canbus_detach(device_t dev)
208{
209	struct canbus_softc *sc = device_get_softc(dev);
210
211	/* I/O resource free */
212	release_ioresource(dev);
213	delete_ioresource(dev);
214
215	/* Dynamic sysctl tree destroy */
216	if (sysctl_ctx_free(&sc->canbus_sysctl_ctx)) {
217		device_printf(dev,
218		    "can't free this context - other oids depend on it\n");
219		return (ENOTEMPTY);
220	}
221
222	return (0);
223}
224
225
226static int
227canbus_print_child(device_t dev, device_t child)
228{
229	int     retval = 0;
230
231	retval += bus_print_child_header(dev, child);
232	retval += print_all_resources(child);
233	retval += bus_print_child_footer(dev, child);
234
235	return (retval);
236}
237
238static device_t
239canbus_add_child(device_t bus, int order, const char *name, int unit)
240{
241	device_t child;
242	struct canbus_device *cbdev;
243
244	child = device_add_child_ordered(bus, order, name, unit);
245
246	cbdev = malloc(
247	    sizeof(struct canbus_device), M_CANBUSDEV, M_NOWAIT | M_ZERO);
248	if (!cbdev)
249		return (0);
250
251	resource_list_init(&cbdev->cbdev_resources);
252	device_set_ivars(child, cbdev);
253
254	return (child);
255}
256
257static struct resource *
258canbus_alloc_resource(device_t dev, device_t child, int type,
259    int *rid, u_long start, u_long end, u_long count, u_int flags)
260{
261	return (BUS_ALLOC_RESOURCE(device_get_parent(dev),
262	    child, type, rid, start, end, count, flags));
263}
264
265static int
266canbus_activate_resource(
267    device_t dev, device_t child, int type, int rid, struct resource *res)
268{
269	return (BUS_ACTIVATE_RESOURCE(
270	    device_get_parent(dev), child, type, rid, res));
271}
272
273static int
274canbus_deactivate_resource(
275    device_t dev, device_t child, int type, int rid, struct resource *res)
276{
277	return (BUS_DEACTIVATE_RESOURCE(
278	    device_get_parent(dev), child, type, rid, res));
279}
280
281static int
282canbus_release_resource(
283    device_t dev, device_t child, int type, int rid, struct resource *res)
284{
285	return (BUS_RELEASE_RESOURCE(
286	    device_get_parent(dev), child, type, rid, res));
287}
288
289static int
290canbus_set_resource (
291    device_t dev, device_t child, int type, int rid, u_long start, u_long count)
292{
293	struct  canbus_device *cbdev =
294	    (struct canbus_device *)device_get_ivars(child);
295	struct resource_list *rl = &cbdev->cbdev_resources;
296
297	resource_list_add(rl, type, rid, start, (start + count - 1), count);
298
299	return (0);
300}
301
302static void
303canbus_delete_resource(device_t dev, device_t child, int type, int rid)
304{
305        struct  canbus_device *cbdev =
306	    (struct canbus_device *)device_get_ivars(child);
307        struct resource_list *rl = &cbdev->cbdev_resources;
308
309        resource_list_delete(rl, type, rid);
310}
311
312
313u_int8_t
314canbus_read(device_t dev, device_t child, int reg)
315{
316	struct canbus_softc *sc = device_get_softc(dev);
317
318	bus_space_write_1(sc->index_tag, sc->index_handle, 0, reg);
319	return (bus_space_read_1(sc->data_tag, sc->data_handle, 0));
320}
321
322void
323canbus_write(device_t dev, device_t child, int reg, u_int8_t val)
324{
325	struct canbus_softc *sc = device_get_softc(dev);
326
327	bus_space_write_1(sc->index_tag, sc->index_handle, 0, reg);
328	bus_space_write_1(sc->data_tag, sc->data_handle, 0, val);
329}
330
331void
332canbus_write_multi(device_t dev,
333    device_t child, int reg, const int count, const u_int8_t *vals)
334{
335	struct canbus_softc *sc = device_get_softc(dev);
336	int i;
337
338	bus_space_write_1(sc->index_tag, sc->index_handle, 0, reg);
339
340	for (i = 0; i < count; i ++) {
341		bus_space_write_1(sc->data_tag, sc->data_handle, 0, vals[i]);
342		DELAY(sc->io_delay_time);
343	}
344}
345
346void
347canbus_delay(device_t dev, device_t child)
348{
349	struct canbus_softc *sc = device_get_softc(dev);
350
351	DELAY(sc->io_delay_time);
352}
353
354
355/*
356 * canbus local function.
357 */
358
359/*
360 * CanBe I/O resource set function
361 */
362static void
363set_ioresource(device_t dev)
364{
365	struct canbus_softc *sc = device_get_softc(dev);
366
367	sc->index_id = 0;
368	sc->data_id = 1;
369
370	bus_set_resource(
371	    dev, SYS_RES_IOPORT, sc->index_id, CANBE_IOPORT_INDEX, 1);
372	bus_set_resource(
373	    dev, SYS_RES_IOPORT, sc->data_id, CANBE_IOPORT_DATA, 1);
374}
375
376/*
377 * CanBe I/O resource delete function
378 */
379static void
380delete_ioresource(device_t dev)
381{
382	struct canbus_softc *sc = device_get_softc(dev);
383
384	bus_delete_resource(dev, SYS_RES_IOPORT, sc->index_id);
385	bus_delete_resource(dev, SYS_RES_IOPORT, sc->data_id);
386}
387
388/*
389 * CanBe I/O resource alloc function
390 */
391static int
392alloc_ioresource(device_t dev)
393{
394	struct canbus_softc *sc = device_get_softc(dev);
395
396	sc->index_res = bus_alloc_resource_any(
397	    dev, SYS_RES_IOPORT, &sc->index_id, RF_ACTIVE);
398	sc->data_res = bus_alloc_resource_any(
399	    dev, SYS_RES_IOPORT, &sc->data_id, RF_ACTIVE);
400	if (sc->index_res == NULL || sc->data_res == NULL) {
401		device_printf(dev, "could not map I/O\n");
402		return (ENXIO);
403	}
404
405	sc->index_tag = rman_get_bustag(sc->index_res);
406	sc->index_handle = rman_get_bushandle(sc->index_res);
407	sc->data_tag = rman_get_bustag(sc->data_res);
408	sc->data_handle = rman_get_bushandle(sc->data_res);
409
410	return (0);
411}
412
413/*
414 * CanBe I/O resource release function
415 */
416static void
417release_ioresource(device_t dev)
418{
419	struct canbus_softc *sc = device_get_softc(dev);
420
421	bus_release_resource(dev, SYS_RES_IOPORT, sc->index_id, sc->index_res);
422	bus_release_resource(dev, SYS_RES_IOPORT, sc->data_id, sc->data_res);
423}
424
425
426static int
427print_all_resources(device_t dev)
428{
429	struct  canbus_device *cbdev =
430	    (struct canbus_device *)device_get_ivars(dev);
431	struct resource_list *rl = &cbdev->cbdev_resources;
432	int retval = 0;
433
434	if (SLIST_FIRST(rl))
435		retval += printf(" at");
436
437	retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx");
438	retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#lx");
439	retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld");
440
441	return retval;
442}
443