1*c7fb772bSthorpej /* $NetBSD: ppbus_conf.c,v 1.23 2021/08/07 16:19:15 thorpej Exp $ */
2f7820334Sbjh21
3e23cd1a7Sjdolecek /*-
4e23cd1a7Sjdolecek * Copyright (c) 1997, 1998, 1999 Nicolas Souchu
5e23cd1a7Sjdolecek * All rights reserved.
6e23cd1a7Sjdolecek *
7e23cd1a7Sjdolecek * Redistribution and use in source and binary forms, with or without
8e23cd1a7Sjdolecek * modification, are permitted provided that the following conditions
9e23cd1a7Sjdolecek * are met:
10e23cd1a7Sjdolecek * 1. Redistributions of source code must retain the above copyright
11e23cd1a7Sjdolecek * notice, this list of conditions and the following disclaimer.
12e23cd1a7Sjdolecek * 2. Redistributions in binary form must reproduce the above copyright
13e23cd1a7Sjdolecek * notice, this list of conditions and the following disclaimer in the
14e23cd1a7Sjdolecek * documentation and/or other materials provided with the distribution.
15e23cd1a7Sjdolecek *
16e23cd1a7Sjdolecek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17e23cd1a7Sjdolecek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18e23cd1a7Sjdolecek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19e23cd1a7Sjdolecek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20e23cd1a7Sjdolecek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21e23cd1a7Sjdolecek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22e23cd1a7Sjdolecek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23e23cd1a7Sjdolecek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24e23cd1a7Sjdolecek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25e23cd1a7Sjdolecek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26e23cd1a7Sjdolecek * SUCH DAMAGE.
27e23cd1a7Sjdolecek *
281c34707aSbjh21 * FreeBSD: src/sys/dev/ppbus/ppbconf.c,v 1.17.2.1 2000/05/24 00:20:57 n_hibma Exp
29e23cd1a7Sjdolecek *
30e23cd1a7Sjdolecek */
311c34707aSbjh21
321c34707aSbjh21 #include <sys/cdefs.h>
33*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: ppbus_conf.c,v 1.23 2021/08/07 16:19:15 thorpej Exp $");
341c34707aSbjh21
35e23cd1a7Sjdolecek #include "opt_ppbus.h"
36f5e874fbSjdolecek #include "opt_ppbus_1284.h"
37e23cd1a7Sjdolecek
38b574865eScegger #include "gpio.h"
39b574865eScegger
40e23cd1a7Sjdolecek #include <sys/param.h>
41e23cd1a7Sjdolecek #include <sys/systm.h>
42e23cd1a7Sjdolecek #include <sys/kernel.h>
43e23cd1a7Sjdolecek #include <sys/device.h>
44e23cd1a7Sjdolecek #include <sys/proc.h>
45e23cd1a7Sjdolecek
46e23cd1a7Sjdolecek #include <dev/ppbus/ppbus_1284.h>
47e23cd1a7Sjdolecek #include <dev/ppbus/ppbus_base.h>
48e23cd1a7Sjdolecek #include <dev/ppbus/ppbus_conf.h>
49e23cd1a7Sjdolecek #include <dev/ppbus/ppbus_device.h>
50e23cd1a7Sjdolecek #include <dev/ppbus/ppbus_var.h>
51e23cd1a7Sjdolecek
52e23cd1a7Sjdolecek /* Probe, attach, and detach functions for ppbus. */
53b849cd90Scegger static int ppbus_probe(device_t, cfdata_t, void *);
54b849cd90Scegger static void ppbus_attach(device_t, device_t, void *);
558b43bcb6Sdyoung static void ppbus_childdet(device_t, device_t);
56b849cd90Scegger static int ppbus_detach(device_t, int);
57e23cd1a7Sjdolecek
58e23cd1a7Sjdolecek /* Utility function prototypes */
59b849cd90Scegger static int ppbus_search_children(device_t, cfdata_t,
6046ed4b50Sdrochner const int *, void *);
61e23cd1a7Sjdolecek
62e23cd1a7Sjdolecek
638b43bcb6Sdyoung CFATTACH_DECL2_NEW(ppbus, sizeof(struct ppbus_softc), ppbus_probe, ppbus_attach,
648b43bcb6Sdyoung ppbus_detach, NULL, NULL, ppbus_childdet);
65e23cd1a7Sjdolecek
66e23cd1a7Sjdolecek /* Probe function for ppbus. */
67e23cd1a7Sjdolecek static int
ppbus_probe(device_t parent,cfdata_t cf,void * aux)68b849cd90Scegger ppbus_probe(device_t parent, cfdata_t cf, void *aux)
69e23cd1a7Sjdolecek {
70e23cd1a7Sjdolecek struct parport_adapter *sc_link = aux;
71e23cd1a7Sjdolecek
72e23cd1a7Sjdolecek /* Check adapter for consistency */
73e23cd1a7Sjdolecek if (
74e23cd1a7Sjdolecek /* Required methods for all parports */
75e23cd1a7Sjdolecek sc_link->parport_io == NULL ||
76e23cd1a7Sjdolecek sc_link->parport_exec_microseq == NULL ||
77e23cd1a7Sjdolecek sc_link->parport_setmode == NULL ||
78e23cd1a7Sjdolecek sc_link->parport_getmode == NULL ||
79e23cd1a7Sjdolecek sc_link->parport_read == NULL ||
80e23cd1a7Sjdolecek sc_link->parport_write == NULL ||
81e23cd1a7Sjdolecek sc_link->parport_read_ivar == NULL ||
82e23cd1a7Sjdolecek sc_link->parport_write_ivar == NULL ||
83e23cd1a7Sjdolecek /* Methods which conditional exist based on capabilities */
84e23cd1a7Sjdolecek ((sc_link->capabilities & PPBUS_HAS_EPP) &&
85e23cd1a7Sjdolecek (sc_link->parport_reset_epp_timeout == NULL)) ||
86e23cd1a7Sjdolecek ((sc_link->capabilities & PPBUS_HAS_ECP) &&
87e23cd1a7Sjdolecek (sc_link->parport_ecp_sync == NULL)) ||
88e23cd1a7Sjdolecek ((sc_link->capabilities & PPBUS_HAS_DMA) &&
89e23cd1a7Sjdolecek (sc_link->parport_dma_malloc == NULL ||
90e23cd1a7Sjdolecek sc_link->parport_dma_free == NULL)) ||
91e23cd1a7Sjdolecek ((sc_link->capabilities & PPBUS_HAS_INTR) &&
92e23cd1a7Sjdolecek (sc_link->parport_add_handler == NULL ||
93e23cd1a7Sjdolecek sc_link->parport_remove_handler == NULL))
94e23cd1a7Sjdolecek ) {
95e23cd1a7Sjdolecek
96e23cd1a7Sjdolecek #ifdef PPBUS_DEBUG
97e23cd1a7Sjdolecek printf("%s(%s): parport_adaptor is incomplete. Child device "
981b044f41Scegger "probe failed.\n", __func__, device_xname(parent));
99e23cd1a7Sjdolecek #endif
100e23cd1a7Sjdolecek return 0;
101bbd13d13Sdrochner } else {
102e23cd1a7Sjdolecek return 1;
103e23cd1a7Sjdolecek }
104e23cd1a7Sjdolecek }
105e23cd1a7Sjdolecek
106e23cd1a7Sjdolecek /* Attach function for ppbus. */
107e23cd1a7Sjdolecek static void
ppbus_attach(device_t parent,device_t self,void * aux)108b849cd90Scegger ppbus_attach(device_t parent, device_t self, void *aux)
109e23cd1a7Sjdolecek {
110ec03de0cSthorpej struct ppbus_softc *ppbus = device_private(self);
111e23cd1a7Sjdolecek struct parport_adapter *sc_link = aux;
112e23cd1a7Sjdolecek struct ppbus_attach_args args;
113e23cd1a7Sjdolecek
114e23cd1a7Sjdolecek printf("\n");
115e23cd1a7Sjdolecek
116e23cd1a7Sjdolecek /* Initialize config data from adapter (bus + device methods) */
117b849cd90Scegger ppbus->sc_dev = self;
118e23cd1a7Sjdolecek args.capabilities = ppbus->sc_capabilities = sc_link->capabilities;
119e23cd1a7Sjdolecek ppbus->ppbus_io = sc_link->parport_io;
120e23cd1a7Sjdolecek ppbus->ppbus_exec_microseq = sc_link->parport_exec_microseq;
121e23cd1a7Sjdolecek ppbus->ppbus_reset_epp_timeout = sc_link->
122e23cd1a7Sjdolecek parport_reset_epp_timeout;
123e23cd1a7Sjdolecek ppbus->ppbus_setmode = sc_link->parport_setmode;
124e23cd1a7Sjdolecek ppbus->ppbus_getmode = sc_link->parport_getmode;
125e23cd1a7Sjdolecek ppbus->ppbus_ecp_sync = sc_link->parport_ecp_sync;
126e23cd1a7Sjdolecek ppbus->ppbus_read = sc_link->parport_read;
127e23cd1a7Sjdolecek ppbus->ppbus_write = sc_link->parport_write;
128e23cd1a7Sjdolecek ppbus->ppbus_read_ivar = sc_link->parport_read_ivar;
129e23cd1a7Sjdolecek ppbus->ppbus_write_ivar = sc_link->parport_write_ivar;
130e23cd1a7Sjdolecek ppbus->ppbus_dma_malloc = sc_link->parport_dma_malloc;
131e23cd1a7Sjdolecek ppbus->ppbus_dma_free = sc_link->parport_dma_free;
132e23cd1a7Sjdolecek ppbus->ppbus_add_handler = sc_link->parport_add_handler;
133e23cd1a7Sjdolecek ppbus->ppbus_remove_handler = sc_link->parport_remove_handler;
134e23cd1a7Sjdolecek
135e23cd1a7Sjdolecek /* Initially there is no device owning the bus */
136e23cd1a7Sjdolecek ppbus->ppbus_owner = NULL;
137e23cd1a7Sjdolecek
138e23cd1a7Sjdolecek /* Initialize locking structures */
1396874e511Sad mutex_init(&(ppbus->sc_lock), MUTEX_DEFAULT, IPL_NONE);
140e23cd1a7Sjdolecek
141e23cd1a7Sjdolecek /* Set up bus mode and ieee state */
1425887891aSthorpej ppbus->sc_mode = ppbus->ppbus_getmode(device_parent(self));
143e23cd1a7Sjdolecek ppbus->sc_use_ieee = 1;
144e23cd1a7Sjdolecek ppbus->sc_1284_state = PPBUS_FORWARD_IDLE;
145e23cd1a7Sjdolecek ppbus->sc_1284_error = PPBUS_NO_ERROR;
146e23cd1a7Sjdolecek
147ba5c90c4Smsaitoh /* Record device's successful attachment */
148e23cd1a7Sjdolecek ppbus->sc_dev_ok = PPBUS_OK;
149e23cd1a7Sjdolecek
150e23cd1a7Sjdolecek #ifndef DONTPROBE_1284
151e23cd1a7Sjdolecek /* detect IEEE1284 compliant devices */
152e23cd1a7Sjdolecek if (ppbus_scan_bus(self)) {
1531b044f41Scegger printf("%s: No IEEE1284 device found.\n", device_xname(self));
154bbd13d13Sdrochner } else {
1551b044f41Scegger printf("%s: IEEE1284 device found.\n", device_xname(self));
156e23cd1a7Sjdolecek /*
157e23cd1a7Sjdolecek * Detect device ID (interrupts must be disabled because we
1583bc401dcSrmind * cannot do a block to wait for it - no context)
159e23cd1a7Sjdolecek */
160e23cd1a7Sjdolecek if (args.capabilities & PPBUS_HAS_INTR) {
161e23cd1a7Sjdolecek int val = 0;
162e23cd1a7Sjdolecek if(ppbus_write_ivar(self, PPBUS_IVAR_INTR, &val) != 0) {
163e23cd1a7Sjdolecek printf(" <problem initializing interrupt "
164e23cd1a7Sjdolecek "usage>");
165e23cd1a7Sjdolecek }
166e23cd1a7Sjdolecek }
167e23cd1a7Sjdolecek ppbus_pnp_detect(self);
168e23cd1a7Sjdolecek }
169e23cd1a7Sjdolecek #endif /* !DONTPROBE_1284 */
170e23cd1a7Sjdolecek
171e23cd1a7Sjdolecek /* Configure child devices */
172e23cd1a7Sjdolecek SLIST_INIT(&(ppbus->sc_childlist_head));
1732685996bSthorpej config_search(self, &args,
174*c7fb772bSthorpej CFARGS(.search = ppbus_search_children,
175*c7fb772bSthorpej .iattr = "ppbus"));
176e23cd1a7Sjdolecek
177b574865eScegger #if NGPIO > 0
178b574865eScegger gpio_ppbus_attach(ppbus);
179b574865eScegger #endif
180e23cd1a7Sjdolecek return;
181e23cd1a7Sjdolecek }
182e23cd1a7Sjdolecek
1838b43bcb6Sdyoung static void
ppbus_childdet(device_t self,device_t target)1848b43bcb6Sdyoung ppbus_childdet(device_t self, device_t target)
1858b43bcb6Sdyoung {
186c90853c4She struct ppbus_softc * ppbus = device_private(self);
187c90853c4She struct ppbus_device_softc * child;
188c90853c4She
1898b43bcb6Sdyoung SLIST_FOREACH(child, &ppbus->sc_childlist_head, entries) {
1908b43bcb6Sdyoung if (child->sc_dev == target)
1918b43bcb6Sdyoung break;
1928b43bcb6Sdyoung }
1938b43bcb6Sdyoung if (child != NULL)
1948b43bcb6Sdyoung SLIST_REMOVE(&ppbus->sc_childlist_head, child,
195c90853c4She ppbus_device_softc, entries);
1968b43bcb6Sdyoung }
1978b43bcb6Sdyoung
198e23cd1a7Sjdolecek /* Detach function for ppbus. */
199e23cd1a7Sjdolecek static int
ppbus_detach(device_t self,int flag)200b849cd90Scegger ppbus_detach(device_t self, int flag)
201e23cd1a7Sjdolecek {
202ec03de0cSthorpej struct ppbus_softc * ppbus = device_private(self);
203e23cd1a7Sjdolecek struct ppbus_device_softc * child;
204e23cd1a7Sjdolecek
205e23cd1a7Sjdolecek if (ppbus->sc_dev_ok != PPBUS_OK) {
206e23cd1a7Sjdolecek if (!(flag & DETACH_QUIET))
207e23cd1a7Sjdolecek printf("%s: detach called on unattached device.\n",
208b849cd90Scegger device_xname(ppbus->sc_dev));
209e23cd1a7Sjdolecek if (!(flag & DETACH_FORCE))
210e23cd1a7Sjdolecek return 0;
211e23cd1a7Sjdolecek if (!(flag & DETACH_QUIET))
212e23cd1a7Sjdolecek printf("%s: continuing detach (DETACH_FORCE).\n",
213b849cd90Scegger device_xname(ppbus->sc_dev));
214e23cd1a7Sjdolecek }
215e23cd1a7Sjdolecek
2166874e511Sad mutex_destroy(&(ppbus->sc_lock));
217e23cd1a7Sjdolecek
218e23cd1a7Sjdolecek /* Detach children devices */
2198b43bcb6Sdyoung while ((child = SLIST_FIRST(&ppbus->sc_childlist_head)) != NULL) {
2205db50545Scegger if (config_detach(child->sc_dev, flag)) {
221e23cd1a7Sjdolecek if(!(flag & DETACH_QUIET))
222b849cd90Scegger aprint_error_dev(ppbus->sc_dev, "error detaching %s.",
223b849cd90Scegger device_xname(child->sc_dev));
224e23cd1a7Sjdolecek if(!(flag & DETACH_FORCE))
225e23cd1a7Sjdolecek return 0;
226e23cd1a7Sjdolecek if(!(flag & DETACH_QUIET))
227e23cd1a7Sjdolecek printf("%s: continuing (DETACH_FORCE).\n",
228b849cd90Scegger device_xname(ppbus->sc_dev));
229e23cd1a7Sjdolecek }
230e23cd1a7Sjdolecek }
231e23cd1a7Sjdolecek
232e23cd1a7Sjdolecek if (!(flag & DETACH_QUIET))
233b849cd90Scegger printf("%s: detached.\n", device_xname(ppbus->sc_dev));
234e23cd1a7Sjdolecek
235e23cd1a7Sjdolecek return 1;
236e23cd1a7Sjdolecek }
237e23cd1a7Sjdolecek
238e23cd1a7Sjdolecek /* Search for children device and add to list */
239e23cd1a7Sjdolecek static int
ppbus_search_children(device_t parent,cfdata_t cf,const int * ldesc,void * aux)240cbab9cadSchs ppbus_search_children(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
241e23cd1a7Sjdolecek {
242b849cd90Scegger struct ppbus_softc *ppbus = device_private(parent);
243e23cd1a7Sjdolecek struct ppbus_device_softc *child;
244cbab9cadSchs device_t dev;
245e23cd1a7Sjdolecek int rval = 0;
246e23cd1a7Sjdolecek
2472685996bSthorpej if (config_probe(parent, cf, aux)) {
248*c7fb772bSthorpej dev = config_attach(parent, cf, aux, NULL, CFARGS_NONE);
249cbab9cadSchs if (dev) {
250cbab9cadSchs child = device_private(dev);
251cbab9cadSchs SLIST_INSERT_HEAD(&(ppbus->sc_childlist_head),
252cbab9cadSchs child, entries);
253e23cd1a7Sjdolecek rval = 1;
254e23cd1a7Sjdolecek }
255e23cd1a7Sjdolecek }
256e23cd1a7Sjdolecek
257e23cd1a7Sjdolecek return rval;
258e23cd1a7Sjdolecek }
259e23cd1a7Sjdolecek
260