Initial import from FreeBSD RELENG_4:
[games.git] / sys / bus / canbus / canbus.c
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: src/sys/pc98/pc98/canbus.c,v 1.3.2.1 2003/02/10 13:11:51 nyan Exp $
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
52 static MALLOC_DEFINE(M_CANBUSDEV, "canbusdev", "CanBe device");
53 struct canbus_device {
54         struct resource_list cbdev_resources;
55 };
56
57 /* canbus softc */
58 struct 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 */
79 static void     canbus_identify(driver_t *, device_t);
80 static int      canbus_probe(device_t);
81 static int      canbus_attach(device_t);
82 static int      canbus_detach(device_t);
83
84 /* Bus interface methods */
85 static int      canbus_print_child(device_t, device_t);
86 static device_t canbus_add_child(device_t, int, const char *, int);
87 static struct resource *        canbus_alloc_resource(
88     device_t, device_t, int, int *, u_long, u_long, u_long, u_int);
89 static int      canbus_activate_resource(
90     device_t, device_t, int, int, struct resource *);
91 static int      canbus_deactivate_resource(
92     device_t, device_t, int, int, struct resource *);
93 static int      canbus_release_resource(
94     device_t, device_t, int, int, struct resource *);
95 static int      canbus_set_resource (
96     device_t, device_t, int, int, u_long, u_long);
97 static void     canbus_delete_resource(device_t, device_t, int, int);
98
99 /* canbus local function */
100 static void     set_ioresource(device_t dev);
101 static void     delete_ioresource(device_t dev);
102 static int      alloc_ioresource(device_t);
103 static void     release_ioresource(device_t);
104 static int      print_all_resources(device_t);
105
106 static 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
133 static driver_t canbus_driver = {
134         "canbus",
135         canbus_methods,
136         sizeof(struct canbus_softc),
137 };
138
139 devclass_t canbus_devclass;
140 DRIVER_MODULE(canbus, nexus, canbus_driver, canbus_devclass, 0, 0);
141 MODULE_VERSION(canbus, 1);
142
143
144 static void
145 canbus_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
154 static int
155 canbus_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
178 static int
179 canbus_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
206 static int
207 canbus_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
226 static int
227 canbus_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
238 static device_t
239 canbus_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
257 static struct resource *
258 canbus_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
265 static int
266 canbus_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
273 static int
274 canbus_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
281 static int
282 canbus_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
289 static int
290 canbus_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
302 static void
303 canbus_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
313 u_int8_t
314 canbus_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
322 void
323 canbus_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
331 void
332 canbus_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
346 void
347 canbus_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  */
362 static void
363 set_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  */
379 static void
380 delete_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  */
391 static int
392 alloc_ioresource(device_t dev)
393 {
394         struct canbus_softc *sc = device_get_softc(dev);
395
396         sc->index_res = bus_alloc_resource(
397             dev, SYS_RES_IOPORT, &sc->index_id, 0ul, ~0ul, 1, RF_ACTIVE);
398         sc->data_res = bus_alloc_resource(
399             dev, SYS_RES_IOPORT, &sc->data_id, 0ul, ~0ul, 1, 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  */
416 static void
417 release_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
426 static int
427 print_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 }