freebsd-dev/sys/dev/rp/rp_pci.c
gallatin 9da53bef26 Prevent the rp driver from panic'ing on first access and make at
least the pci device unloadable

- Use ttymalloc() rather than a plain  malloc to allocate the
  rp->rp_tty ttys.  This is now required due to the recent locking
  changes to ttys and prevents a panic due to locking an unitialized
  t_mtx.

- Allow the pci driver to be unloaded.  This involved moving
  the call rp_releaseresource() to the end of rp_pcireleaseresource(),
  since rp_pcireleaseresource() uses ctlp->dev, which is freed
  by rp_releaseresource().

- Allow the generic part of the driver to be unattached by providing
  a hook to cancel timeouts.

Glanced at by: obrien
2004-06-21 13:02:25 +00:00

371 lines
9.4 KiB
C

/*-
* Copyright (c) Comtrol Corporation <support@comtrol.com>
* All rights reserved.
*
* PCI-specific part separated from:
* sys/i386/isa/rp.c,v 1.33 1999/09/28 11:45:27 phk Exp
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted prodived that the follwoing conditions
* are met.
* 1. Redistributions of source code must retain the above copyright
* notive, this list of conditions and the following disclainer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials prodided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Comtrol Corporation.
* 4. The name of Comtrol Corporation may not be used to endorse or
* promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY COMTROL CORPORATION ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL COMTROL CORPORATION BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/fcntl.h>
#include <sys/malloc.h>
#include <sys/tty.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <machine/resource.h>
#include <machine/bus.h>
#include <sys/bus.h>
#include <sys/rman.h>
#define ROCKET_C
#include <dev/rp/rpreg.h>
#include <dev/rp/rpvar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
/* PCI IDs */
#define RP_VENDOR_ID 0x11FE
#define RP_DEVICE_ID_32I 0x0001
#define RP_DEVICE_ID_8I 0x0002
#define RP_DEVICE_ID_16I 0x0003
#define RP_DEVICE_ID_4Q 0x0004
#define RP_DEVICE_ID_8O 0x0005
#define RP_DEVICE_ID_8J 0x0006
#define RP_DEVICE_ID_4J 0x0007
#define RP_DEVICE_ID_6M 0x000C
#define RP_DEVICE_ID_4M 0x000D
/**************************************************************************
MUDBAC remapped for PCI
**************************************************************************/
#define _CFG_INT_PCI 0x40
#define _PCI_INT_FUNC 0x3A
#define PCI_STROB 0x2000
#define INTR_EN_PCI 0x0010
/***************************************************************************
Function: sPCIControllerEOI
Purpose: Strobe the MUDBAC's End Of Interrupt bit.
Call: sPCIControllerEOI(CtlP)
CONTROLLER_T *CtlP; Ptr to controller structure
*/
#define sPCIControllerEOI(CtlP) rp_writeio2(CtlP, 0, _PCI_INT_FUNC, PCI_STROB)
/***************************************************************************
Function: sPCIGetControllerIntStatus
Purpose: Get the controller interrupt status
Call: sPCIGetControllerIntStatus(CtlP)
CONTROLLER_T *CtlP; Ptr to controller structure
Return: Byte_t: The controller interrupt status in the lower 4
bits. Bits 0 through 3 represent AIOP's 0
through 3 respectively. If a bit is set that
AIOP is interrupting. Bits 4 through 7 will
always be cleared.
*/
#define sPCIGetControllerIntStatus(CTLP) ((rp_readio2(CTLP, 0, _PCI_INT_FUNC) >> 8) & 0x1f)
static devclass_t rp_devclass;
static int rp_pciprobe(device_t dev);
static int rp_pciattach(device_t dev);
#if notdef
static int rp_pcidetach(device_t dev);
static int rp_pcishutdown(device_t dev);
#endif /* notdef */
static void rp_pcireleaseresource(CONTROLLER_t *ctlp);
static int sPCIInitController( CONTROLLER_t *CtlP,
int AiopNum,
int IRQNum,
Byte_t Frequency,
int PeriodicOnly,
int VendorDevice);
static rp_aiop2rid_t rp_pci_aiop2rid;
static rp_aiop2off_t rp_pci_aiop2off;
static rp_ctlmask_t rp_pci_ctlmask;
/*
* The following functions are the pci-specific part
* of rp driver.
*/
static int
rp_pciprobe(device_t dev)
{
char *s;
s = NULL;
if ((pci_get_devid(dev) & 0xffff) == RP_VENDOR_ID)
s = "RocketPort PCI";
if (s != NULL) {
device_set_desc(dev, s);
return (0);
}
return (ENXIO);
}
static int
rp_pciattach(device_t dev)
{
int num_ports, num_aiops;
int aiop;
CONTROLLER_t *ctlp;
int unit;
int retval;
u_int32_t stcmd;
ctlp = device_get_softc(dev);
bzero(ctlp, sizeof(*ctlp));
ctlp->dev = dev;
unit = device_get_unit(dev);
ctlp->aiop2rid = rp_pci_aiop2rid;
ctlp->aiop2off = rp_pci_aiop2off;
ctlp->ctlmask = rp_pci_ctlmask;
/* Wake up the device. */
stcmd = pci_read_config(dev, PCIR_COMMAND, 4);
if ((stcmd & PCIM_CMD_PORTEN) == 0) {
stcmd |= (PCIM_CMD_PORTEN);
pci_write_config(dev, PCIR_COMMAND, 4, stcmd);
}
/* The IO ports of AIOPs for a PCI controller are continuous. */
ctlp->io_num = 1;
ctlp->io_rid = malloc(sizeof(*(ctlp->io_rid)) * ctlp->io_num, M_DEVBUF, M_NOWAIT | M_ZERO);
ctlp->io = malloc(sizeof(*(ctlp->io)) * ctlp->io_num, M_DEVBUF, M_NOWAIT | M_ZERO);
if (ctlp->io_rid == NULL || ctlp->io == NULL) {
device_printf(dev, "rp_pciattach: Out of memory.\n");
retval = ENOMEM;
goto nogo;
}
ctlp->bus_ctlp = NULL;
ctlp->io_rid[0] = 0x10;
ctlp->io[0] = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
&ctlp->io_rid[0], RF_ACTIVE);
if(ctlp->io[0] == NULL) {
device_printf(dev, "ioaddr mapping failed for RocketPort(PCI).\n");
retval = ENXIO;
goto nogo;
}
num_aiops = sPCIInitController(ctlp,
MAX_AIOPS_PER_BOARD, 0,
FREQ_DIS, 0, (pci_get_devid(dev) >> 16) & 0xffff);
num_ports = 0;
for(aiop=0; aiop < num_aiops; aiop++) {
sResetAiopByNum(ctlp, aiop);
num_ports += sGetAiopNumChan(ctlp, aiop);
}
retval = rp_attachcommon(ctlp, num_aiops, num_ports);
if (retval != 0)
goto nogo;
return (0);
nogo:
rp_pcireleaseresource(ctlp);
return (retval);
}
static int
rp_pcidetach(device_t dev)
{
CONTROLLER_t *ctlp;
if (device_get_state(dev) == DS_BUSY)
return (EBUSY);
ctlp = device_get_softc(dev);
rp_pcireleaseresource(ctlp);
return (0);
}
static int
rp_pcishutdown(device_t dev)
{
CONTROLLER_t *ctlp;
if (device_get_state(dev) == DS_BUSY)
return (EBUSY);
ctlp = device_get_softc(dev);
rp_pcireleaseresource(ctlp);
return (0);
}
static void
rp_pcireleaseresource(CONTROLLER_t *ctlp)
{
rp_untimeout();
if (ctlp->io != NULL) {
if (ctlp->io[0] != NULL)
bus_release_resource(ctlp->dev, SYS_RES_IOPORT, ctlp->io_rid[0], ctlp->io[0]);
free(ctlp->io, M_DEVBUF);
ctlp->io = NULL;
}
if (ctlp->io_rid != NULL) {
free(ctlp->io_rid, M_DEVBUF);
ctlp->io = NULL;
}
rp_releaseresource(ctlp);
}
static int
sPCIInitController( CONTROLLER_t *CtlP,
int AiopNum,
int IRQNum,
Byte_t Frequency,
int PeriodicOnly,
int VendorDevice)
{
int i;
CtlP->CtlID = CTLID_0001; /* controller release 1 */
sPCIControllerEOI(CtlP);
/* Init AIOPs */
CtlP->NumAiop = 0;
for(i=0; i < AiopNum; i++)
{
/*device_printf(CtlP->dev, "aiop %d.\n", i);*/
CtlP->AiopID[i] = sReadAiopID(CtlP, i); /* read AIOP ID */
/*device_printf(CtlP->dev, "ID = %d.\n", CtlP->AiopID[i]);*/
if(CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */
{
break; /* done looking for AIOPs */
}
switch( VendorDevice ) {
case RP_DEVICE_ID_4Q:
case RP_DEVICE_ID_4J:
case RP_DEVICE_ID_4M:
CtlP->AiopNumChan[i] = 4;
break;
case RP_DEVICE_ID_6M:
CtlP->AiopNumChan[i] = 6;
break;
case RP_DEVICE_ID_8O:
case RP_DEVICE_ID_8J:
case RP_DEVICE_ID_8I:
case RP_DEVICE_ID_16I:
case RP_DEVICE_ID_32I:
CtlP->AiopNumChan[i] = 8;
break;
default:
#if notdef
CtlP->AiopNumChan[i] = 8;
#else
CtlP->AiopNumChan[i] = sReadAiopNumChan(CtlP, i);
#endif /* notdef */
break;
}
/*device_printf(CtlP->dev, "%d channels.\n", CtlP->AiopNumChan[i]);*/
rp_writeaiop2(CtlP, i, _INDX_ADDR,_CLK_PRE); /* clock prescaler */
/*device_printf(CtlP->dev, "configuring clock prescaler.\n");*/
rp_writeaiop1(CtlP, i, _INDX_DATA,CLOCK_PRESC);
/*device_printf(CtlP->dev, "configured clock prescaler.\n");*/
CtlP->NumAiop++; /* bump count of AIOPs */
}
if(CtlP->NumAiop == 0)
return(-1);
else
return(CtlP->NumAiop);
}
/*
* ARGSUSED
* Maps (aiop, offset) to rid.
*/
static int
rp_pci_aiop2rid(int aiop, int offset)
{
/* Always return zero for a PCI controller. */
return 0;
}
/*
* ARGSUSED
* Maps (aiop, offset) to the offset of resource.
*/
static int
rp_pci_aiop2off(int aiop, int offset)
{
/* Each AIOP reserves 0x40 bytes. */
return aiop * 0x40 + offset;
}
/* Read the int status for a PCI controller. */
static unsigned char
rp_pci_ctlmask(CONTROLLER_t *ctlp)
{
return sPCIGetControllerIntStatus(ctlp);
}
static device_method_t rp_pcimethods[] = {
/* Device interface */
DEVMETHOD(device_probe, rp_pciprobe),
DEVMETHOD(device_attach, rp_pciattach),
DEVMETHOD(device_detach, rp_pcidetach),
DEVMETHOD(device_shutdown, rp_pcishutdown),
{ 0, 0 }
};
static driver_t rp_pcidriver = {
"rp",
rp_pcimethods,
sizeof(CONTROLLER_t),
};
/*
* rp can be attached to a pci bus.
*/
DRIVER_MODULE(rp, pci, rp_pcidriver, rp_devclass, 0, 0);