The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/pcmcia/pcmcom.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: pcmcom.c,v 1.14 2003/01/01 00:10:24 thorpej Exp $      */
    2 
    3 /*-
    4  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by RedBack Networks, Inc.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  * 3. All advertising materials mentioning features or use of this software
   19  *    must display the following acknowledgement:
   20  *      This product includes software developed by the NetBSD
   21  *      Foundation, Inc. and its contributors.
   22  * 4. Neither the name of The NetBSD Foundation nor the names of its
   23  *    contributors may be used to endorse or promote products derived
   24  *    from this software without specific prior written permission.
   25  *
   26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   36  * POSSIBILITY OF SUCH DAMAGE.
   37  */
   38 
   39 /*
   40  * Device driver for multi-port PCMCIA serial cards, written by
   41  * Jason R. Thorpe for RedBack Networks, Inc.
   42  *
   43  * Most of these cards are simply multiple UARTs sharing a single interrupt
   44  * line, and rely on the fact that PCMCIA level-triggered interrupts can
   45  * be shared.  There are no special interrupt registers on them, as there
   46  * are on most ISA multi-port serial cards.
   47  *
   48  * If there are other cards that have interrupt registers, they should not
   49  * be glued into this driver.  Rather, separate drivers should be written
   50  * for those devices, as we have in the ISA multi-port serial card case.
   51  */
   52 
   53 #include <sys/cdefs.h>
   54 __KERNEL_RCSID(0, "$NetBSD: pcmcom.c,v 1.14 2003/01/01 00:10:24 thorpej Exp $");
   55 
   56 #include <sys/param.h>
   57 #include <sys/systm.h>
   58 #include <sys/device.h> 
   59 #include <sys/termios.h>
   60 #include <sys/malloc.h>
   61 
   62 #include <machine/bus.h>
   63 #include <machine/intr.h>
   64 
   65 #include <dev/ic/comreg.h>
   66 #include <dev/ic/comvar.h> 
   67 
   68 #include <dev/pcmcia/pcmciavar.h>
   69 #include <dev/pcmcia/pcmciareg.h>
   70 #include <dev/pcmcia/pcmciadevs.h>
   71 
   72 #include "com.h"
   73 #include "pcmcom.h"
   74 
   75 #include "locators.h"
   76 
   77 struct pcmcom_slave_info {
   78         struct pcmcia_io_handle psi_pcioh;      /* PCMCIA i/o space info */
   79         int psi_io_window;                      /* our i/o window */
   80         struct device *psi_child;               /* child's device */
   81 };
   82 
   83 struct pcmcom_softc {
   84         struct device sc_dev;                   /* generic device glue */
   85 
   86         struct pcmcia_function *sc_pf;          /* our PCMCIA function */
   87         void *sc_ih;                            /* interrupt handle */
   88         int sc_enabled_count;                   /* enabled count */
   89 
   90         struct pcmcom_slave_info *sc_slaves;    /* slave info */
   91         int sc_nslaves;                         /* slave count */
   92 };
   93 
   94 struct pcmcom_attach_args {
   95         bus_space_tag_t pca_iot;                /* I/O tag */
   96         bus_space_handle_t pca_ioh;             /* I/O handle */
   97         int pca_slave;                          /* slave # */
   98 };
   99 
  100 int     pcmcom_match __P((struct device *, struct cfdata *, void *));
  101 void    pcmcom_attach __P((struct device *, struct device *, void *));
  102 int     pcmcom_detach __P((struct device *, int));
  103 int     pcmcom_activate __P((struct device *, enum devact));
  104 
  105 CFATTACH_DECL(pcmcom, sizeof(struct pcmcom_softc),
  106     pcmcom_match, pcmcom_attach, pcmcom_detach, pcmcom_activate);
  107 
  108 const struct pcmcom_product {
  109         struct pcmcia_product pp_product;
  110         int             pp_nslaves;             /* number of slaves */
  111 } pcmcom_products[] = {
  112         { { PCMCIA_STR_SOCKET_DUAL_RS232,       PCMCIA_VENDOR_SOCKET,
  113             PCMCIA_PRODUCT_SOCKET_DUAL_RS232,   0 },
  114           2 },
  115 
  116         { { NULL } }
  117 };
  118 
  119 int     pcmcom_print __P((void *, const char *));
  120 int     pcmcom_submatch __P((struct device *, struct cfdata *, void *));
  121 
  122 int     pcmcom_check_cfe __P((struct pcmcom_softc *,
  123             struct pcmcia_config_entry *));
  124 void    pcmcom_attach_slave __P((struct pcmcom_softc *, int));
  125 
  126 int     pcmcom_enable __P((struct pcmcom_softc *));
  127 void    pcmcom_disable __P((struct pcmcom_softc *));
  128 
  129 int     pcmcom_intr __P((void *));
  130 
  131 int
  132 pcmcom_match(parent, cf, aux)
  133         struct device *parent;
  134         struct cfdata *cf;
  135         void *aux;
  136 {
  137         struct pcmcia_attach_args *pa = aux;
  138 
  139         if (pcmcia_product_lookup(pa,
  140             (const struct pcmcia_product *)pcmcom_products,
  141             sizeof pcmcom_products[0], NULL) != NULL)
  142                 return (10);    /* beat com_pcmcia */
  143         return (0);
  144 }
  145 
  146 void
  147 pcmcom_attach(parent, self, aux)
  148         struct device *parent, *self;
  149         void *aux;
  150 {
  151         struct pcmcom_softc *sc = (struct pcmcom_softc *)self;
  152         struct pcmcia_attach_args *pa = aux;
  153         struct pcmcia_config_entry *cfe;
  154         const struct pcmcom_product *pp;
  155         size_t size;
  156         int i;
  157 
  158         sc->sc_pf = pa->pf;
  159 
  160         pp = (const struct pcmcom_product *)pcmcia_product_lookup(pa,
  161             (const struct pcmcia_product *)pcmcom_products,
  162             sizeof pcmcom_products[0], NULL);
  163         if (pp == NULL) {
  164                 printf("\n");
  165                 panic("pcmcom_attach: impossible");
  166         }
  167 
  168         printf(": %s\n", pp->pp_product.pp_name);
  169 
  170         /* Allocate the slave info. */
  171         sc->sc_nslaves = pp->pp_nslaves;
  172         size = sizeof(struct pcmcom_slave_info) * sc->sc_nslaves;
  173         sc->sc_slaves = malloc(size, M_DEVBUF, M_NOWAIT|M_ZERO);
  174         if (sc->sc_slaves == NULL) {
  175                 printf("%s: unable to allocate slave info\n",
  176                     sc->sc_dev.dv_xname);
  177                 return;
  178         }
  179 
  180         /*
  181          * The address decoders on these cards are stupid.  They decode
  182          * (usually) 10 bits of address, so you need to allocate the
  183          * regions they request in order for the card to differentiate
  184          * between serial ports.
  185          */
  186         SIMPLEQ_FOREACH(cfe, &pa->pf->cfe_head, cfe_list) {
  187                 if (pcmcom_check_cfe(sc, cfe)) {
  188                         /* Found one! */
  189                         break;
  190                 }
  191         }
  192         if (cfe == NULL) {
  193                 printf("%s: unable to find a suitable config table entry\n",
  194                     sc->sc_dev.dv_xname);
  195                 return;
  196         }
  197 
  198         /* Enable the card. */
  199         pcmcia_function_init(pa->pf, cfe);
  200         if (pcmcia_function_enable(sc->sc_pf)) {
  201                 printf("%s: function enable failed\n", sc->sc_dev.dv_xname);
  202                 return;
  203         }
  204 
  205         sc->sc_enabled_count = 1;
  206 
  207         /* Attach the children. */
  208         for (i = 0; i < sc->sc_nslaves; i++)
  209                 pcmcom_attach_slave(sc, i);
  210 
  211         sc->sc_enabled_count = 0;
  212         pcmcia_function_disable(sc->sc_pf);
  213 }
  214 
  215 int
  216 pcmcom_check_cfe(sc, cfe)
  217         struct pcmcom_softc *sc;
  218         struct pcmcia_config_entry *cfe;
  219 {
  220         struct pcmcom_slave_info *psi;
  221         int slave;
  222 
  223         /* We need to have the same number of I/O spaces as we do slaves. */
  224         if (cfe->num_iospace != sc->sc_nslaves)
  225                 return (0);
  226 
  227         for (slave = 0; slave < sc->sc_nslaves; slave++) {
  228                 psi = &sc->sc_slaves[slave];
  229                 if (pcmcia_io_alloc(sc->sc_pf,
  230                     cfe->iospace[slave].start,
  231                     cfe->iospace[slave].length,
  232                     cfe->iospace[slave].length,
  233                     &psi->psi_pcioh))
  234                         goto release;
  235         }
  236 
  237         /* If we got here, we were able to allocate space for all slaves! */
  238         return (1);
  239 
  240  release:
  241         /* Release the i/o spaces we've allocated. */
  242         for (slave--; slave >= 0; slave--) {
  243                 psi = &sc->sc_slaves[slave];
  244                 pcmcia_io_free(sc->sc_pf, &psi->psi_pcioh);
  245         }
  246         return (0);
  247 }
  248 
  249 void
  250 pcmcom_attach_slave(sc, slave)
  251         struct pcmcom_softc *sc;
  252         int slave;
  253 {
  254         struct pcmcom_slave_info *psi = &sc->sc_slaves[slave];
  255         struct pcmcom_attach_args pca;
  256 
  257         printf("%s: slave %d", sc->sc_dev.dv_xname, slave);
  258 
  259         if (pcmcia_io_map(sc->sc_pf, PCMCIA_WIDTH_IO8, 0, psi->psi_pcioh.size,
  260             &psi->psi_pcioh, &psi->psi_io_window)) {
  261                 printf(": can't map i/o space\n");
  262                 return;
  263         }
  264 
  265         printf("\n");
  266 
  267         pca.pca_iot = psi->psi_pcioh.iot;
  268         pca.pca_ioh = psi->psi_pcioh.ioh;
  269         pca.pca_slave = slave;
  270 
  271         psi->psi_child = config_found_sm(&sc->sc_dev, &pca, pcmcom_print,
  272             pcmcom_submatch);
  273 }
  274 
  275 int
  276 pcmcom_detach(self, flags)
  277         struct device *self;
  278         int flags;
  279 {
  280         struct pcmcom_softc *sc = (struct pcmcom_softc *)self;
  281         struct pcmcom_slave_info *psi;
  282         int slave, error;
  283 
  284         for (slave = sc->sc_nslaves - 1; slave >= 0; slave--) {
  285                 psi = &sc->sc_slaves[slave];
  286                 if (psi->psi_child == NULL)
  287                         continue;
  288 
  289                 /* Detach the child. */
  290                 if ((error = config_detach(psi->psi_child, flags)) != 0)
  291                         return (error);
  292                 psi->psi_child = NULL;
  293 
  294                 /* Unmap the i/o window. */
  295                 pcmcia_io_unmap(sc->sc_pf, psi->psi_io_window);
  296 
  297                 /* Free the i/o space. */
  298                 pcmcia_io_free(sc->sc_pf, &psi->psi_pcioh);
  299         }
  300         return (0);
  301 }
  302 
  303 int
  304 pcmcom_activate(self, act)
  305         struct device *self;
  306         enum devact act;
  307 {
  308         struct pcmcom_softc *sc = (struct pcmcom_softc *)self;
  309         struct pcmcom_slave_info *psi;
  310         int slave, error = 0, s;
  311 
  312         s = splserial();
  313         switch (act) {
  314         case DVACT_ACTIVATE:
  315                 error = EOPNOTSUPP;
  316                 break;
  317 
  318         case DVACT_DEACTIVATE:
  319                 for (slave = sc->sc_nslaves - 1; slave >= 0; slave--) {
  320                         psi = &sc->sc_slaves[slave];
  321                         if (psi->psi_child == NULL)
  322                                 continue;
  323 
  324                         /*
  325                          * Deactivate the child.  Doing so will cause our
  326                          * own enabled count to drop to 0, once all children
  327                          * are deactivated.
  328                          */
  329                         if ((error = config_deactivate(psi->psi_child)) != 0)
  330                                 break;
  331                 }
  332                 break;
  333         }
  334         splx(s);
  335         return (error);
  336 }
  337 
  338 int
  339 pcmcom_print(aux, pnp)
  340         void *aux;
  341         const char *pnp;
  342 {
  343         struct pcmcom_attach_args *pca = aux;
  344 
  345         /* only com's can attach to pcmcom's; easy... */
  346         if (pnp)
  347                 aprint_normal("com at %s", pnp);
  348 
  349         aprint_normal(" slave %d", pca->pca_slave);
  350 
  351         return (UNCONF);
  352 }
  353 
  354 int
  355 pcmcom_submatch(parent, cf, aux)
  356         struct device *parent;
  357         struct cfdata *cf;
  358         void *aux;
  359 {
  360         struct pcmcom_attach_args *pca = aux;
  361 
  362         if (cf->cf_loc[PCMCOMCF_SLAVE] != pca->pca_slave &&
  363             cf->cf_loc[PCMCOMCF_SLAVE] != PCMCOMCF_SLAVE_DEFAULT)
  364                 return (0);
  365 
  366         return (config_match(parent, cf, aux));
  367 }
  368 
  369 int
  370 pcmcom_intr(arg)
  371         void *arg;
  372 {
  373 #if NCOM > 0
  374         struct pcmcom_softc *sc = arg;
  375         int i, rval = 0;
  376 
  377         if (sc->sc_enabled_count == 0)
  378                 return (0);
  379 
  380         for (i = 0; i < sc->sc_nslaves; i++) {
  381                 if (sc->sc_slaves[i].psi_child != NULL)
  382                         rval |= comintr(sc->sc_slaves[i].psi_child);
  383         }
  384 
  385         return (rval);
  386 #else
  387         return (0);
  388 #endif /* NCOM > 0 */
  389 }
  390 
  391 int
  392 pcmcom_enable(sc)
  393         struct pcmcom_softc *sc;
  394 {
  395 
  396         sc->sc_enabled_count++;
  397         if (sc->sc_enabled_count > 1)
  398                 return (0);
  399 
  400         /* Establish the interrupt. */
  401         sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_SERIAL,
  402             pcmcom_intr, sc);
  403         if (sc->sc_ih == NULL) {
  404                 printf("%s: couldn't establish interrupt\n",
  405                     sc->sc_dev.dv_xname);
  406                 return (1);
  407         }
  408 
  409         return (pcmcia_function_enable(sc->sc_pf));
  410 }
  411 
  412 void
  413 pcmcom_disable(sc)
  414         struct pcmcom_softc *sc;
  415 {
  416 
  417         sc->sc_enabled_count--;
  418         if (sc->sc_enabled_count != 0)
  419                 return;
  420 
  421         pcmcia_function_disable(sc->sc_pf);
  422         pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
  423 }
  424 
  425 /****** Here begins the com attachment code. ******/
  426 
  427 #if NCOM_PCMCOM > 0
  428 int     com_pcmcom_match __P((struct device *, struct cfdata *, void *));
  429 void    com_pcmcom_attach __P((struct device *, struct device *, void *));
  430 
  431 /* No pcmcom-specific goo in the softc; it's all in the parent. */
  432 CFATTACH_DECL(com_pcmcom, sizeof(struct com_softc),
  433     com_pcmcom_match, com_pcmcom_attach, com_detach, com_activate);
  434 
  435 int     com_pcmcom_enable __P((struct com_softc *));
  436 void    com_pcmcom_disable __P((struct com_softc *));
  437 
  438 int
  439 com_pcmcom_match(parent, cf, aux)
  440         struct device *parent;
  441         struct cfdata *cf;
  442         void *aux;
  443 {
  444 
  445         /* Device is always present. */
  446         return (1);
  447 }
  448 
  449 void
  450 com_pcmcom_attach(parent, self, aux)
  451         struct device *parent, *self;
  452         void *aux;
  453 {
  454         struct com_softc *sc = (struct com_softc *)self;
  455         struct pcmcom_attach_args *pca = aux;
  456 
  457         sc->sc_iot = pca->pca_iot;
  458         sc->sc_ioh = pca->pca_ioh;
  459 
  460         sc->enabled = 1;
  461 
  462         sc->sc_iobase = -1;
  463         sc->sc_frequency = COM_FREQ;
  464 
  465         sc->enable = com_pcmcom_enable;
  466         sc->disable = com_pcmcom_disable;
  467 
  468         com_attach_subr(sc);
  469 
  470         sc->enabled = 0;
  471 }
  472 
  473 int
  474 com_pcmcom_enable(sc)
  475         struct com_softc *sc;
  476 {
  477 
  478         return (pcmcom_enable((struct pcmcom_softc *)sc->sc_dev.dv_parent));
  479 }
  480 
  481 void
  482 com_pcmcom_disable(sc)
  483         struct com_softc *sc;
  484 {
  485 
  486         pcmcom_disable((struct pcmcom_softc *)sc->sc_dev.dv_parent);
  487 }
  488 #endif /* NCOM_PCMCOM > 0 */

Cache object: feda0e296f0c5ce1fa62dc174f4257f2


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.